update
init
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="add-sport">
|
||||
<div class="sport-header">
|
||||
<van-icon name="search" />
|
||||
<input class="sport-input" placeholder="请输入运动名称" />
|
||||
</div>
|
||||
<div class="sport-container">
|
||||
<div class="nav-list">
|
||||
<van-sidebar v-model="activeTab">
|
||||
<van-sidebar-item title="标签名称" />
|
||||
<van-sidebar-item title="标签名称" />
|
||||
<van-sidebar-item title="标签名称" />
|
||||
</van-sidebar>
|
||||
</div>
|
||||
<div v-if="detailVOList.length > 0" class="nav-right">
|
||||
<div v-for="(item, index) in detailVOList" :key="index">
|
||||
<div class="nav-right-item {{rightTab===index?'active':'' }}">
|
||||
{{ item?.detailName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="nocate">
|
||||
<div @click="openSportForm">该分类暂无数据</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useTabHooks } from '/@/hooks/useTab';
|
||||
import { newPageParams, openPage } from '/@/hooks/openPage';
|
||||
|
||||
const { activeTab, setActiveTab } = useTabHooks();
|
||||
const { activeTab: rightTab } = useTabHooks();
|
||||
const detailVOList = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
initData();
|
||||
});
|
||||
|
||||
function initData() {}
|
||||
|
||||
// function getList() {}
|
||||
|
||||
function openSportForm() {
|
||||
openPage('/sport-form', newPageParams());
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/less/index';
|
||||
|
||||
.add-sport {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
.flex-column();
|
||||
|
||||
.sport-header {
|
||||
width: 86%;
|
||||
margin: 10px 7% 10px 7%;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 10px;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 20px;
|
||||
|
||||
.sport-input {
|
||||
font-size: 14px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-sidebar) {
|
||||
width: 100%;
|
||||
|
||||
.van-sidebar-item {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-sidebar-item--select)::before {
|
||||
left: 6px;
|
||||
background-color: #21bebe !important;
|
||||
}
|
||||
|
||||
.sport-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
|
||||
.nav-list {
|
||||
width: 125px;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.nav-right {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
|
||||
.nav-right-item {
|
||||
height: 45px;
|
||||
padding-left: 30px;
|
||||
border-bottom: 1rpx solid #e0e0e0;
|
||||
font-size: 14px;
|
||||
color: #666666;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
import { get, post } from '/@/views/mobile/api/api';
|
||||
import { apiPrefix } from '/@/api/apiPrefix';
|
||||
|
||||
const prefix: string = import.meta.env.VITE_GLOB_APP_PREFIX;
|
||||
const { healthFoodExercise } = apiPrefix;
|
||||
export const Api = {
|
||||
exerciseTypeSum: prefix + `/rest/${healthFoodExercise}/record/exerciseTypeSum`,
|
||||
topData: prefix + `/rest/${healthFoodExercise}/record/day`,
|
||||
rankList: prefix + `/rest/${healthFoodExercise}/record/score/rank`,
|
||||
exerciseSum: prefix + `/rest/${healthFoodExercise}/record/exerciseSum`,
|
||||
sportAdd: prefix + `/rest/${healthFoodExercise}/record/build`,
|
||||
};
|
||||
// 运动类别统计
|
||||
export const getSportType = (params: any) => {
|
||||
return get(Api.exerciseTypeSum, params);
|
||||
};
|
||||
//今日运动首页
|
||||
export const topData = (params: any) => get(Api.topData, params);
|
||||
// 排行榜
|
||||
export const rankListApi = (params: any) => get(Api.rankList, params);
|
||||
//运动周报-运动统计
|
||||
export const exerciseSumApi = (params: any) => get(Api.exerciseSum, params);
|
||||
//运动录入
|
||||
export const sportAdd = (params: any) => post(Api.sportAdd, params);
|
||||
@@ -0,0 +1,220 @@
|
||||
import { ref } from 'vue';
|
||||
import { getChooseWeek, weekTime } from '/@/hooks/utils.ts';
|
||||
|
||||
export const firstIcon = new URL('/@/assets/images/health-interventions/first-icon.png', import.meta.url).href;
|
||||
|
||||
export const dateText = [
|
||||
{
|
||||
type: 'week',
|
||||
text: '周',
|
||||
img: new URL('/@/assets/images/health-interventions/week-icon.png', import.meta.url).href,
|
||||
},
|
||||
{
|
||||
type: 'month',
|
||||
text: '月',
|
||||
img: new URL('/@/assets/images/health-interventions/month-icon.png', import.meta.url).href,
|
||||
},
|
||||
{
|
||||
type: 'year',
|
||||
text: '年',
|
||||
img: new URL('/@/assets/images/health-interventions/year-icon.png', import.meta.url).href,
|
||||
},
|
||||
];
|
||||
export const dateTypeEnum = {
|
||||
year: '1',
|
||||
month: '2',
|
||||
week: '3',
|
||||
};
|
||||
export const highestText = ['日', '日', '月'];
|
||||
export const getHighestText = (index: number) => `单${highestText[index]}最高纪录`;
|
||||
|
||||
// 运动统计柱状图
|
||||
export const stepChart = ({ xData, yData }) => {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xData,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#E0E0E0',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#333333',
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: '8%',
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
bottom: '5%',
|
||||
containLabel: true,
|
||||
},
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
color: '#333333',
|
||||
},
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E0E0E0',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: yData,
|
||||
type: 'bar',
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: {
|
||||
color: '#13C2C2',
|
||||
borderRadius: 5,
|
||||
},
|
||||
barWidth: 10,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
//运动周报折线图
|
||||
export const sportsStatisticLine = ({ xData, yData1, yData2 }) => {
|
||||
return {
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
width: 120,
|
||||
right: '10px',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xData || ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#E0E0E0',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#333333',
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: '50px',
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
bottom: '5%',
|
||||
containLabel: true,
|
||||
},
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
color: '#333333',
|
||||
},
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E0E0E0',
|
||||
},
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '分数',
|
||||
data: yData1,
|
||||
type: 'line',
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: {
|
||||
color: '#13C2C2',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '运动消耗',
|
||||
data: yData2,
|
||||
type: 'line',
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: {
|
||||
color: '#52C41A',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
export const usePanelData = () => {
|
||||
const panelData = ref({
|
||||
totalTime: 0,
|
||||
highDurationTime: 0,
|
||||
totalTimeDail: 0,
|
||||
highDurationTimeDail: 0,
|
||||
score: 0,
|
||||
distance: 0,
|
||||
exerciseConsumeSum: 0,
|
||||
conclusion: 0,
|
||||
averageDaily: {
|
||||
exerciseConsumeDail: 0,
|
||||
totalTimeDail: 0,
|
||||
highDurationConsume: 0,
|
||||
highDurationTimeDail: 0,
|
||||
highDurationConsumeDail: 0,
|
||||
glycogenConsumeDail: 0,
|
||||
fatConsumeDail: 0,
|
||||
},
|
||||
});
|
||||
|
||||
function setPanelData(data) {
|
||||
const { averageDaily } = data;
|
||||
const defaultVal = 0;
|
||||
panelData.value = {
|
||||
totalTime: parseInt(data?.exerciseSum?.totalTime || defaultVal),
|
||||
highDurationTime: parseInt(data?.exerciseSum?.totalTime || defaultVal), // 中高强度(分钟)
|
||||
totalTimeDail: parseInt(data?.averageDaily?.totalTimeDail || defaultVal), // 日均活动时长
|
||||
highDurationTimeDail: parseInt(data?.averageDaily?.highDurationTimeDail || defaultVal), // 日均中高强度时间
|
||||
score: parseInt(data?.exerciseSum?.score || defaultVal),
|
||||
distance: parseInt(data?.exerciseSum?.distance || defaultVal),
|
||||
exerciseConsumeSum: parseInt(data?.exerciseSum?.exerciseConsumeSum || defaultVal),
|
||||
conclusion: data?.conclusion,
|
||||
averageDaily: {
|
||||
exerciseConsumeDail: averageDaily?.exerciseConsumeDail || defaultVal, //日均活动消耗
|
||||
totalTimeDail: parseInt(data?.averageDaily?.totalTimeDail || defaultVal), //日均活动时长
|
||||
highDurationConsume: averageDaily?.highDurationConsume || defaultVal, //中高强度活动消耗
|
||||
highDurationTimeDail: parseInt(data?.averageDaily?.highDurationTimeDail || defaultVal), // 日均中高强度时间
|
||||
highDurationConsumeDail: averageDaily?.highDurationConsumeDail || defaultVal, //日均中高强度能耗
|
||||
glycogenConsumeDail: averageDaily?.glycogenConsumeDail || defaultVal, //糖原消耗
|
||||
fatConsumeDail: averageDaily?.fatConsumeDail || defaultVal, //脂肪消耗
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
panelData,
|
||||
setPanelData,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="sport-tit">
|
||||
<div class="left-txt">{{ leftText }}</div>
|
||||
<div class="right-con" @click="handleDetail">
|
||||
<span class="right-txt">{{ rightText }}</span>
|
||||
<span class="circle" v-show="showCircle"></span>
|
||||
<van-icon name="arrow" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
leftText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
rightText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
showCircle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['handleDetail']);
|
||||
function handleDetail() {
|
||||
emit('handleDetail');
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
@import '/@/assets/less/index';
|
||||
.sport-tit {
|
||||
.flex-jc-between();
|
||||
padding-bottom: 10px;
|
||||
.left-txt {
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
font-size: 16px;
|
||||
}
|
||||
.right-con {
|
||||
.right-txt {
|
||||
font-size: 16px;
|
||||
}
|
||||
.circle {
|
||||
display: inline-flex;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin: 0 5px;
|
||||
background: #52c41a;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div class="date-panel">
|
||||
<van-icon name="arrow-left" @click="lastDate" />
|
||||
<span class="date-time" @click="openPopup">{{ timeText }}</span>
|
||||
<van-icon name="arrow" @click="nextDate" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch, unref } from 'vue';
|
||||
import { getMondayDate, getChooseWeek, timeFormat, weekTime, getMonth, usTime, weekRange } from '/@/hooks/utils';
|
||||
import { showToast } from 'vant';
|
||||
|
||||
const props = defineProps({
|
||||
dateType: {
|
||||
type: String,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['emitTime', 'emitPopup']);
|
||||
const dateType = computed(() => props.dateType);
|
||||
// 时间区间文本
|
||||
const timeText = ref('');
|
||||
// week 开始时间-结束时间
|
||||
const timeInterval = ref({
|
||||
start: getMondayDate()[0],
|
||||
end: getMondayDate()[1],
|
||||
});
|
||||
onMounted(() => {
|
||||
timeText.value = `${timeInterval.value.start}至${timeInterval.value.end}`;
|
||||
});
|
||||
// 周月年变化
|
||||
watch(dateType, (nVal) => {
|
||||
if (nVal == 'week') {
|
||||
let [start, end] = getMondayDate();
|
||||
timeInterval.value = { start, end };
|
||||
timeText.value = `${timeInterval.value.start}至${timeInterval.value.end}`;
|
||||
return;
|
||||
}
|
||||
const { YYYY, MM } = usTime();
|
||||
if (nVal == 'month') {
|
||||
timeText.value = `${YYYY}-${MM}`;
|
||||
return;
|
||||
}
|
||||
if (nVal == 'year') {
|
||||
timeText.value = `${YYYY}`;
|
||||
return;
|
||||
}
|
||||
});
|
||||
watch(
|
||||
timeText,
|
||||
() => {
|
||||
emit('emitTime', unref(timeText));
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
// 获取上一个日期
|
||||
function lastDate() {
|
||||
changeDate(-1);
|
||||
}
|
||||
|
||||
// 获取下一个日期
|
||||
function nextDate() {
|
||||
changeDate(1);
|
||||
}
|
||||
function isDateCross(checkTime: Date): boolean {
|
||||
return new Date(checkTime).getTime() <= Date.now();
|
||||
}
|
||||
// type 周=3 月=2 年=1
|
||||
function changeDate(direction: number) {
|
||||
let date;
|
||||
// if (direction == 1) {
|
||||
// debugger;
|
||||
// }
|
||||
|
||||
if (dateType.value == 'week') {
|
||||
let [start, end] = getChooseWeek(new Date(timeInterval.value.start).getTime() + direction * weekTime);
|
||||
const checkTime = direction > 0 ? start : end;
|
||||
|
||||
// 判断是否越界
|
||||
if (!isDateCross(checkTime)) {
|
||||
return showToast('没有更多数据了~');
|
||||
}
|
||||
timeInterval.value = { start, end };
|
||||
timeText.value = `${start}至${end}`;
|
||||
} else if (dateType.value == 'month') {
|
||||
date = new Date(timeText.value);
|
||||
date.setMonth(date.getMonth() + direction);
|
||||
const month = getMonth(date);
|
||||
// 判断是否越界
|
||||
if (!isDateCross(month)) {
|
||||
return showToast('没有更多数据了~');
|
||||
}
|
||||
timeText.value = month;
|
||||
} else if (dateType.value == 'year') {
|
||||
date = new Date(timeText.value);
|
||||
date.setFullYear(date.getFullYear() + direction);
|
||||
const year = timeFormat(date, 'yyyy');
|
||||
// 判断是否越界
|
||||
if (!isDateCross(year)) {
|
||||
return showToast('没有更多数据了~');
|
||||
}
|
||||
timeText.value = year;
|
||||
}
|
||||
}
|
||||
|
||||
function openPopup() {
|
||||
if (dateType.value !== 'week') return;
|
||||
let arr = weekRange();
|
||||
|
||||
emit('emitPopup', { column: arr, nowTime: timeInterval.value });
|
||||
}
|
||||
|
||||
function setTimeText(text: string) {
|
||||
timeText.value = text;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
setTimeText,
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.date-panel {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
|
||||
.date-time {
|
||||
margin: 0 16px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<van-popup v-model:show="show" position="bottom" :style="{ height: '50%' }">
|
||||
<van-date-picker
|
||||
v-model="currentDate"
|
||||
title="选择日期"
|
||||
:max-date="maxDate"
|
||||
:columnsType="columnsType"
|
||||
:close-on-click-overlay="false"
|
||||
@cancel="cancel"
|
||||
@confirm="confirm"
|
||||
@change="change"
|
||||
/>
|
||||
<!-- <van-date-picker-->
|
||||
<!-- v-model="currentDate"-->
|
||||
<!-- title="选择日期"-->
|
||||
<!-- :columnsType="['day']"-->
|
||||
<!-- :close-on-click-overlay="false"-->
|
||||
<!-- :filter="filter"-->
|
||||
<!-- @cancel="cancel"-->
|
||||
<!-- @confirm="confirm"-->
|
||||
<!-- @change="change"-->
|
||||
<!-- />-->
|
||||
</van-popup>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
const show = ref(false);
|
||||
const showPopup = () => {
|
||||
show.value = true;
|
||||
};
|
||||
const currentDate = ref();
|
||||
const maxDate = new Date();
|
||||
const columnsType = ['year', 'month'];
|
||||
|
||||
const option = [
|
||||
{ text: '04至10', value: '04至10' },
|
||||
{ text: '28至03', value: '28至03' },
|
||||
{ text: '21至27', value: '21至27' },
|
||||
];
|
||||
const filter = (type, options) => {
|
||||
console.log('options', options);
|
||||
if (type === 'day') {
|
||||
return options.filter((option) => Number(option.value) % 6 === 0);
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
function cancel() {
|
||||
show.value = false;
|
||||
}
|
||||
|
||||
function change() {}
|
||||
|
||||
function confirm() {
|
||||
show.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
showPopup,
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="less"></style>
|
||||
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="sport-card-module">
|
||||
<div class="sport-box" @click="openDetail">
|
||||
<div class="sport-left">
|
||||
<img class="img" :src="sportImg" alt="" />
|
||||
<span class="sport-name">{{ cardData?.className }}</span>
|
||||
</div>
|
||||
<van-icon name="arrow" color="#999999" />
|
||||
</div>
|
||||
<div class="sport-box info-bottom">
|
||||
<div>
|
||||
<span class="sports-num">{{ toFixed1(cardData?.consume) }}</span>
|
||||
<span class="sport-text"> 千卡</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="sport-text">{{ toFixed1(cardData?.totalMinute) }}</span>
|
||||
<span class="sport-text">分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { sportImg } from '/@/views/motion-data/sports-platform/data';
|
||||
|
||||
const props = defineProps({
|
||||
cardData: {
|
||||
type: Object,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['sportDetail']);
|
||||
|
||||
function toFixed1(num) {
|
||||
try {
|
||||
return parseFloat(Number(num).toFixed(1));
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// 打开详情页
|
||||
function openDetail() {
|
||||
emit('sportDetail', props.cardData);
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
@import '/@/assets/less/index';
|
||||
|
||||
.sport-card-module {
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding: 10px 10px;
|
||||
.flex-column();
|
||||
justify-content: space-between;
|
||||
|
||||
.sport-box {
|
||||
.flex-center();
|
||||
justify-content: space-between;
|
||||
|
||||
.sport-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.sport-name {
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.sports-num {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sport-text {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.info-bottom {
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="tab-time-components">
|
||||
<div
|
||||
class="tab-time-item"
|
||||
v-for="(item, i) in dynamicText"
|
||||
:key="i"
|
||||
:class="[activeTab == i ? 'active' : '']"
|
||||
:style="propsStyle(activeTab == i)"
|
||||
@click="setActiveTab(i)"
|
||||
v-text="item.text"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, watch, unref } from 'vue';
|
||||
import { dateText } from '/@/views/motion-data/common.data';
|
||||
import { useTabHooks } from '/@/hooks/useTab';
|
||||
const emit = defineEmits(['emitActiveTab']);
|
||||
const props = defineProps({
|
||||
activeColor: {
|
||||
type: String, //激活背景色
|
||||
},
|
||||
});
|
||||
|
||||
function propsStyle(flag: boolean) {
|
||||
if (props.activeColor) {
|
||||
return flag && `background:${props.activeColor}`;
|
||||
}
|
||||
}
|
||||
const dynamicText = computed(() => dateText);
|
||||
const { activeTab, setActiveTab } = useTabHooks(0);
|
||||
watch(
|
||||
activeTab,
|
||||
() => {
|
||||
emit('emitActiveTab', dateText[unref(activeTab)]);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
@import '/@/assets/less/index.less';
|
||||
.tab-time-components {
|
||||
margin: 10px 0;
|
||||
padding: 2px;
|
||||
height: 35px;
|
||||
background: #ffffff;
|
||||
border-radius: 35px;
|
||||
display: flex;
|
||||
|
||||
.tab-time-item {
|
||||
flex: 1;
|
||||
color: #666666;
|
||||
font-size: 16px;
|
||||
.flex-center();
|
||||
|
||||
&.active {
|
||||
color: #fff;
|
||||
border-radius: 20px;
|
||||
background: #13c2c2;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
// 骑行
|
||||
export const rideCardText = [
|
||||
{
|
||||
text1: '时速',
|
||||
text2: '时长',
|
||||
text3: '消耗',
|
||||
},
|
||||
{
|
||||
text1: '日均时速',
|
||||
text2: '日均时长',
|
||||
text3: '日均消耗',
|
||||
},
|
||||
{
|
||||
text1: '月均时速',
|
||||
text2: '月均时长',
|
||||
text3: '月均消耗',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,284 @@
|
||||
<template>
|
||||
<div class="ride-number">
|
||||
<div class="ride-top">
|
||||
<div class="tab-time">
|
||||
<div
|
||||
class="tab-time-item"
|
||||
v-for="(item, i) in dynamicText"
|
||||
:key="i"
|
||||
:class="[activeTab == i ? 'active' : '']"
|
||||
@click="setActiveTab(i)"
|
||||
v-text="item.text"
|
||||
></div>
|
||||
</div>
|
||||
<div class="date-panel">
|
||||
<van-icon name="arrow-left" />
|
||||
<span class="date-time">9月4日至9月10日</span>
|
||||
<van-icon name="arrow" />
|
||||
</div>
|
||||
<div class="statistics-container panel-m b-12">
|
||||
<div class="img-container">
|
||||
<img class="img" :src="dynamicText[activeTab].img" alt="" />
|
||||
</div>
|
||||
<div class="data-container">
|
||||
<div class="data-count">
|
||||
<p class="data-number">487</p>
|
||||
<p class="data-text">累计{{ dynamicText[activeTab].text }}里程</p>
|
||||
</div>
|
||||
<div class="data-count">
|
||||
<p class="data-number">123</p>
|
||||
<p class="data-text">平均时速(km/h)</p>
|
||||
</div>
|
||||
<div class="data-count">
|
||||
<p class="data-number">123</p>
|
||||
<p class="data-text">累计{{ dynamicText[activeTab].text }}时长(分钟)</p>
|
||||
</div>
|
||||
<div class="data-count">
|
||||
<p class="data-number">123</p>
|
||||
<p class="data-text">累计{{ dynamicText[activeTab].text }}消耗/千卡</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistics-chart panel-m card-bg b-12">
|
||||
<div class="statistics-chart-inner"> 图表1图表1 图表1图表1</div>
|
||||
</div>
|
||||
<div class="bar-data panel-m card-bg b-12">
|
||||
<div class="bar-data-item">
|
||||
<p>{{ cardText.text1 }}</p>
|
||||
<p><span class="bar-data-number">123</span> <span>km/h</span></p>
|
||||
</div>
|
||||
<div class="bar-data-item">
|
||||
<p>{{ cardText.text2 }}</p>
|
||||
<p><span class="bar-data-number">34</span> <span>分钟</span></p>
|
||||
</div>
|
||||
<div class="bar-data-item">
|
||||
<p>{{ cardText.text3 }}</p>
|
||||
<p><span class="bar-data-number">23</span> <span>千卡</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="highest-record panel-m">
|
||||
<p>{{ getHighestText(activeTab) }}</p>
|
||||
<div class="highest-content card-bg b-12">
|
||||
<div class="icon-time">
|
||||
<img class="first-icon" :src="firstIcon" alt="" />
|
||||
<span class="highest-time">2020-11-28</span>
|
||||
</div>
|
||||
<div class="highest-info">
|
||||
<span style="opacity: 0"> </span>
|
||||
<span>1362 <span>公里</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ride-btn">录入</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTabHooks } from '/@/hooks/useTab';
|
||||
import { dateText, firstIcon, getHighestText } from '/@/views/motion-data/common.data';
|
||||
import { rideCardText } from './hooks';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const { activeTab, setActiveTab } = useTabHooks(0);
|
||||
let query = 'ride';
|
||||
const dynamicText = computed(() => {
|
||||
if (query == 'ride') {
|
||||
return dateText;
|
||||
}
|
||||
});
|
||||
const cardText = computed(() => {
|
||||
if (query == 'ride') {
|
||||
return rideCardText[activeTab.value];
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/less/index.less';
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.panel-m {
|
||||
margin: 6px 16px;
|
||||
}
|
||||
|
||||
.card-bg {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.b-12 {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.ride-number {
|
||||
height: 100vh;
|
||||
background: #eeeeee;
|
||||
box-sizing: border-box;
|
||||
//padding-top: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.ride-top {
|
||||
height: calc(100vh - 80px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
|
||||
.tab-time {
|
||||
margin: 10px 20px;
|
||||
padding: 2px;
|
||||
height: 35px;
|
||||
background: #ffffff;
|
||||
border-radius: 35px;
|
||||
display: flex;
|
||||
|
||||
.tab-time-item {
|
||||
flex: 1;
|
||||
color: #666666;
|
||||
font-size: 16px;
|
||||
.flex-center();
|
||||
|
||||
&.active {
|
||||
color: #fff;
|
||||
border-radius: 20px;
|
||||
background: #52c41a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.date-panel {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
|
||||
.date-time {
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-container {
|
||||
height: 140px;
|
||||
padding: 6px 0;
|
||||
background: #ffffff;
|
||||
.flex-center();
|
||||
|
||||
.img-container {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin: 0 16px;
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.data-container {
|
||||
flex: 4;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.data-count {
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
|
||||
.data-number {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.data-text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-chart {
|
||||
//min-height: 240px;
|
||||
.statistics-chart-inner {
|
||||
min-height: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.bar-data {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 16px;
|
||||
|
||||
.bar-data-item {
|
||||
width: 50%;
|
||||
|
||||
& p,
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.bar-data-number {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.highest-record {
|
||||
& > p {
|
||||
margin-bottom: 6px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.highest-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
|
||||
.icon-time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.first-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.highest-time {
|
||||
flex: 1;
|
||||
margin: 0 15px 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.highest-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ride-btn {
|
||||
margin: 12px 15% 28px 15%;
|
||||
height: 40px;
|
||||
.flex-center();
|
||||
.qs-btn();
|
||||
border-radius: 40px;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,5 @@
|
||||
import { get } from '/@/views/mobile/api/api';
|
||||
|
||||
const prefix: string = import.meta.env.VITE_GLOB_APP_PREFIX || '';
|
||||
// 评估列表
|
||||
export const sportListApi = (params) => get(`${prefix}/rest/health-food-exercise/record/getExerciseClassAndDetail`, params);
|
||||
@@ -0,0 +1,64 @@
|
||||
import { timeFormat, toInt } from '/@/hooks/utils';
|
||||
// 含有里程的运动种类
|
||||
export const withMileageCode = ['1', '8', '9', '10', '14'];
|
||||
export function validate(params, keys) {
|
||||
// const keys = ['way', 'mode', 'mileageVal', 'durationVal', 'date', 'division'];
|
||||
let flag = true;
|
||||
keys.forEach((item) => {
|
||||
if (!params[item]) {
|
||||
flag = false;
|
||||
}
|
||||
});
|
||||
return flag;
|
||||
}
|
||||
|
||||
export function createFormParams(params) {
|
||||
if (params?.mileageVal?.indexOf('.') == 0) {
|
||||
params.mileageVal = '0' + params.mileageVal;
|
||||
}
|
||||
if (params?.exerciseDuration?.indexOf('.') == 0) {
|
||||
params.exerciseDuration = '0' + params.exerciseDuration;
|
||||
}
|
||||
return {
|
||||
classNo: params?.way, //运动方式编号
|
||||
detailNo: params?.mode, //运动模式编号
|
||||
exerciseTime: params?.date + ' ' + params?.division + ':00', //运动日期
|
||||
exerciseDistance: params?.mileageVal, // 运动里程
|
||||
exerciseDuration: params?.durationVal && parseInt(params?.durationVal), // 运动时长
|
||||
};
|
||||
}
|
||||
|
||||
export interface SelectedOptions {
|
||||
value: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export function useNowTime() {
|
||||
const [YYYY, MM, DD] = timeFormat(new Date(), 'YYYY-MM-DD').split('-');
|
||||
const [hh, mm] = timeFormat(new Date(), 'HH:mm').split(':');
|
||||
return {
|
||||
YYYY,
|
||||
MM,
|
||||
DD,
|
||||
hh,
|
||||
mm,
|
||||
};
|
||||
}
|
||||
|
||||
export function useDateRule(YYYY: string | number, MM: string | number, DD: string | number) {
|
||||
return {
|
||||
minDate: new Date(2020, 0, 1),
|
||||
maxDate: new Date(toInt(YYYY), toInt(MM), toInt(DD)),
|
||||
};
|
||||
}
|
||||
|
||||
export function returnModeList(sportList: any, value: string) {
|
||||
return (
|
||||
sportList
|
||||
.find((item: any) => item?.exerciseClassNo == value)
|
||||
?.detailVOList.map((item: any) => ({
|
||||
text: item?.detailName,
|
||||
value: item?.detailNo,
|
||||
})) || []
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<div class="sport-container">
|
||||
<div class="sport-mode">
|
||||
<div class="mode-item" @click="openPopup('3')">
|
||||
<span class="title">运动方式</span>
|
||||
<div class="mode-select-text">
|
||||
<span>{{ sportParams?.wayText }}</span>
|
||||
<span class="con arrow"></span
|
||||
></div>
|
||||
</div>
|
||||
<div class="mode-item" @click="openPopup('4')">
|
||||
<span class="title">运动模式</span>
|
||||
<div class="mode-select-text">
|
||||
<span>{{ sportParams?.modeText }}</span>
|
||||
<span class="con arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mode-item">
|
||||
<span class="title">运动时间</span>
|
||||
<div class="time">
|
||||
<div @click="openPopup('1')">{{ sportParams.date }}</div>
|
||||
<div @click="openPopup('2')">{{ sportParams.division }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mode-item" v-if="showMileageVal">
|
||||
<span class="title">运动里程</span>
|
||||
<div class="con">
|
||||
<van-field type="number" v-model.trim="sportParams.mileageVal" class="sport-input" placeholder="请输入运动里程" />
|
||||
<span class="unit">公里</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mode-item">
|
||||
<span class="title">运动时长</span>
|
||||
<div class="con">
|
||||
<van-field type="number" v-model.trim="sportParams.durationVal" class="sport-input" placeholder="请输入运动时长" />
|
||||
<span class="unit">分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<van-popup v-model:show="timePicker.show" position="bottom">
|
||||
<van-date-picker
|
||||
v-if="timesType === '1'"
|
||||
v-model="timePicker.currentDate"
|
||||
:max-date="dateRule.maxDate"
|
||||
title="选择年月"
|
||||
@cancel="closePopup"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
<van-time-picker v-if="timesType === '2'" v-model="timePicker.date" title="选择时间" @cancel="closePopup" @confirm="handleConfirm" />
|
||||
<van-picker v-if="timesType === '3'" :columns="wayList" title="运动方式" @cancel="closePopup" @confirm="sportConfirm" />
|
||||
<van-picker v-if="timesType === '4'" :columns="modeList" :title="'运动模式'" @cancel="closePopup" @confirm="sportConfirm" />
|
||||
</van-popup>
|
||||
<div class="sport-button">
|
||||
<div class="button-text" @click="submit">保存</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, Ref, watch } from 'vue';
|
||||
import { sportListApi } from './api';
|
||||
import { showToast } from 'vant';
|
||||
import { sportAdd } from '/@/views/motion-data/api';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import {
|
||||
validate,
|
||||
createFormParams,
|
||||
SelectedOptions,
|
||||
useNowTime,
|
||||
useDateRule,
|
||||
returnModeList,
|
||||
withMileageCode,
|
||||
} from '/@/views/motion-data/sport-form/data';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { destroyPage } from '/@/hooks/openPage';
|
||||
import { useLoading } from '/@/utils/compUtils';
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { YYYY, MM, DD, hh, mm } = useNowTime();
|
||||
const dateRule = useDateRule(YYYY, MM, DD);
|
||||
|
||||
const timePicker = ref({
|
||||
show: false,
|
||||
currentDate: [YYYY, MM, DD],
|
||||
date: [hh, mm],
|
||||
});
|
||||
const sportParams: Ref<{ [key: string]: string }> = ref({
|
||||
way: '',
|
||||
wayText: '',
|
||||
mode: '',
|
||||
modeText: '',
|
||||
mileageVal: '', // 运动里程
|
||||
durationVal: '', // 运动时长
|
||||
date: `${YYYY}-${MM}-${DD}`,
|
||||
division: `${hh}:${mm}`,
|
||||
});
|
||||
const sportList = ref([]); //接口原始数据
|
||||
const wayList = ref([]); //运动方式
|
||||
const modeList = ref([]); //运动模式
|
||||
const timesType = ref('');
|
||||
const showMileageVal = ref(true);
|
||||
watch(
|
||||
() => sportParams.value.way,
|
||||
(n) => {
|
||||
if (!withMileageCode.includes(n)) {
|
||||
showMileageVal.value = false;
|
||||
delete sportParams.value.mileageVal;
|
||||
} else {
|
||||
showMileageVal.value = true;
|
||||
sportParams.value.mileageVal = '';
|
||||
}
|
||||
}
|
||||
);
|
||||
onMounted(() => {
|
||||
getSportList();
|
||||
});
|
||||
|
||||
// 时间类型 1年月 2 时间 3运动方式 4运动模式
|
||||
function openPopup(type: string) {
|
||||
if (type == '4' && !sportParams.value.way) {
|
||||
return showToast('请先选择运动方式');
|
||||
}
|
||||
timesType.value = type;
|
||||
timePicker.value.show = true;
|
||||
}
|
||||
|
||||
function closePopup() {
|
||||
timePicker.value.show = false;
|
||||
}
|
||||
// 获取运动列表
|
||||
function getSportList() {
|
||||
sportListApi({
|
||||
detailName: '',
|
||||
}).then((res: any) => {
|
||||
const NotNullData = res?.data?.filter((item) => item !== null && item !== undefined);
|
||||
sportList.value = NotNullData;
|
||||
wayList.value = NotNullData?.map((item: any) => ({
|
||||
text: item?.exerciseClassName,
|
||||
value: item?.exerciseClassNo,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// 时间选择
|
||||
function handleConfirm(data: any) {
|
||||
switch (timesType.value) {
|
||||
//年月入
|
||||
case '1':
|
||||
sportParams.value.date = data.selectedValues.join('-');
|
||||
break;
|
||||
//时分
|
||||
case '2':
|
||||
sportParams.value.division = data.selectedValues.join(':');
|
||||
break;
|
||||
}
|
||||
closePopup();
|
||||
}
|
||||
|
||||
function sportConfirm({ selectedOptions }: { selectedOptions: SelectedOptions[] }) {
|
||||
const { value, text } = selectedOptions[0] || '';
|
||||
|
||||
if (timesType.value == '3') {
|
||||
sportParams.value.way = value;
|
||||
sportParams.value.wayText = text;
|
||||
sportParams.value.mode = '';
|
||||
sportParams.value.modeText = '';
|
||||
modeList.value = returnModeList(sportList.value, value);
|
||||
}
|
||||
if (timesType.value == '4') {
|
||||
sportParams.value.mode = value;
|
||||
sportParams.value.modeText = text;
|
||||
}
|
||||
closePopup();
|
||||
}
|
||||
|
||||
const submit = debounce(submitData, 500);
|
||||
const { loadingSpinner, loadingClose } = useLoading();
|
||||
|
||||
function submitData() {
|
||||
const flag = validate(sportParams.value, Object.keys(sportParams.value));
|
||||
if (!flag) {
|
||||
return showToast('请填写所有数据');
|
||||
}
|
||||
loadingSpinner();
|
||||
const params = createFormParams(sportParams.value);
|
||||
sportAdd(params)
|
||||
.then(({ code, data }) => {
|
||||
loadingClose();
|
||||
if (code == 200) {
|
||||
showToast('录入成功!');
|
||||
// 判断安卓是否需要销毁当前页
|
||||
if (route.query.destroyPage) {
|
||||
try {
|
||||
destroyPage();
|
||||
} catch {
|
||||
router.go(-1);
|
||||
}
|
||||
} else {
|
||||
router.go(-1);
|
||||
}
|
||||
} else {
|
||||
showToast(data.msg);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loadingClose();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '../../../assets/less/index';
|
||||
|
||||
.sport-container {
|
||||
background-color: @card-gray;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
.flex-column();
|
||||
justify-content: space-between;
|
||||
.sport-mode {
|
||||
flex: 1;
|
||||
.flex-column();
|
||||
overflow: auto;
|
||||
.mode-item {
|
||||
height: 60px;
|
||||
padding: 20px;
|
||||
background-color: @card-fff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
|
||||
.mode-select-text {
|
||||
display: flex;
|
||||
}
|
||||
.title {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.con {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
.sport-input {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
.time {
|
||||
display: flex;
|
||||
color: #333333;
|
||||
|
||||
> div:nth-child(1) {
|
||||
background-color: #f3f3f3;
|
||||
padding: 5px 16px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
background-color: #f3f3f3;
|
||||
padding: 5px 16px;
|
||||
border-radius: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sport-button {
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.button-text {
|
||||
height: 40px;
|
||||
flex: 1;
|
||||
.flex-center();
|
||||
margin: 0 10%;
|
||||
.footerButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="card-data">
|
||||
<div class="card-row">
|
||||
<div class="card-col-item">
|
||||
<div> 日均活动消耗 </div>
|
||||
<div> {{ cardData.exerciseConsumeDail }}<span class="unit">千卡</span> </div>
|
||||
</div>
|
||||
<div class="card-col-item">
|
||||
<div> 日均活动时长 </div>
|
||||
<div> {{ cardData.totalTimeDail }}<span class="unit">分钟</span> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<div class="card-col-item">
|
||||
<div> 中高强度活动消耗 </div>
|
||||
<div> {{ cardData.highDurationConsume }}<span class="unit">千卡</span> </div>
|
||||
</div>
|
||||
<div class="card-col-item">
|
||||
<div> 日均中高强度时间 </div>
|
||||
<div> {{ cardData.highDurationTimeDail }}<span class="unit">分钟</span> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<div class="card-col-item">
|
||||
<div> 日均中高强度能耗 </div>
|
||||
<div> {{ cardData.highDurationConsumeDail }}<span class="unit">千卡</span> </div>
|
||||
</div>
|
||||
<div class="card-col-item">
|
||||
<div> 糖源消耗 </div>
|
||||
<div> {{ cardData.glycogenConsumeDail }}<span class="unit">千卡</span> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<div class="card-col-item">
|
||||
<div> 脂肪消耗 </div>
|
||||
<div> {{ cardData.fatConsumeDail }}<span class="unit">千卡</span> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
});
|
||||
const cardData = computed(() => props.data);
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.two-outer {
|
||||
background-color: #ffffff;
|
||||
border-radius: 25px;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
.card-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 70px;
|
||||
> :nth-child(n) {
|
||||
width: 50%;
|
||||
text-align: left;
|
||||
}
|
||||
.card-col-item {
|
||||
position: relative;
|
||||
> :nth-child(1),
|
||||
.unit {
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
}
|
||||
span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
> :nth-child(2) {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,292 @@
|
||||
<template>
|
||||
<div class="sport-week-page">
|
||||
<div>
|
||||
<tab-time @emit-active-tab="emitActiveTab" />
|
||||
<date-panel ref="datePanel" :dateType="dateType" @emit-time="emitTime" @emit-popup="openPopup" />
|
||||
<div class="statistics-card">
|
||||
<div class="card-row">
|
||||
<div class="card-row-col">
|
||||
<span class="num">{{ panelData.totalTime }}</span>
|
||||
<span class="text">分钟</span>
|
||||
</div>
|
||||
<div class="card-row-col line-left line-right">
|
||||
<span class="num">{{ panelData.highDurationTime }}</span>
|
||||
<span class="text">中高强度(分钟)</span>
|
||||
</div>
|
||||
<div class="card-row-col">
|
||||
<span class="num">{{ panelData.score }}</span>
|
||||
<span class="text">当日运动得分</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<div class="card-row-col">
|
||||
<span class="num">{{ panelData.distance }}</span>
|
||||
<span class="text">公里</span>
|
||||
</div>
|
||||
<div class="card-row-col line-left">
|
||||
<span class="num">{{ panelData.exerciseConsumeSum }}</span>
|
||||
<span class="text">千卡</span>
|
||||
</div>
|
||||
<div class="card-row-col"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summarize">
|
||||
<div class="tit">本周运动总结</div>
|
||||
<div class="text-area" v-text="panelData.conclusion"></div>
|
||||
<div class="link" @click="openArticle">
|
||||
<span>{{ panelData?.highDurationTime > 150 ? '运动的益处' : '运动不足的伤害' }}</span>
|
||||
<van-icon name="arrow" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-data-container">
|
||||
<card-data :data="panelData.averageDaily" />
|
||||
</div>
|
||||
<!--运动列表-->
|
||||
<div class="card-list">
|
||||
<SportCard v-for="(item, i) in groupList" :key="i" :card-data="item" @sport-detail="openSportDetail" />
|
||||
</div>
|
||||
<!--运动统计图-->
|
||||
<div class="chart-container" id="chartContainer">
|
||||
<div class="title">本{{ dateText }}运动总结(分)</div>
|
||||
<div class="sport-statistics-chart" id="sportStatisticsChart"></div>
|
||||
</div>
|
||||
</div>
|
||||
<van-popup v-model:show="popupData.show" position="bottom">
|
||||
<van-picker title="选择周" :columns="popupData.columns" @cancel="onCancel" @confirm="onConfirm" />
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, unref } from 'vue';
|
||||
import TabTime from '/@/views/motion-data/components/tab-time/tabTime.vue';
|
||||
import DatePanel from '/@/views/motion-data/components/date-panel/datePanel.vue';
|
||||
import CardData from '/@/views/motion-data/sport-week-page/components/cardData.vue';
|
||||
import SportCard from '/@/views/motion-data/components/sport-card/sportCard.vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { dateTypeEnum, sportsStatisticLine, usePanelData } from '/@/views/motion-data/common.data';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { exerciseSumApi } from '/@/views/motion-data/api';
|
||||
import { timeFormat } from '/@/hooks/utils';
|
||||
import { newPageParams, openPage } from '/@/hooks/openPage';
|
||||
|
||||
const dateType = ref('week');
|
||||
const dateText = ref('周');
|
||||
|
||||
// 年月周切换
|
||||
function emitActiveTab({ type, text }: { type: string; text: string }) {
|
||||
dateType.value = type;
|
||||
dateText.value = text;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initData();
|
||||
});
|
||||
|
||||
// 初始化页面
|
||||
function initData() {
|
||||
getSportStatistic();
|
||||
}
|
||||
|
||||
const t = timeFormat(new Date(), 'YYYY-MM-DD');
|
||||
const exerciseTime = ref(t);
|
||||
const emitTime = debounce(debounceInitData, 500);
|
||||
|
||||
function debounceInitData(nowTime: string) {
|
||||
if (!nowTime) return;
|
||||
// 周
|
||||
if (nowTime.includes('至')) {
|
||||
exerciseTime.value = nowTime.split('至')[0];
|
||||
} else {
|
||||
//月或者年
|
||||
exerciseTime.value = nowTime;
|
||||
}
|
||||
initData();
|
||||
}
|
||||
// 运动统计
|
||||
const { panelData, setPanelData } = usePanelData();
|
||||
const groupList = ref([]);
|
||||
const myChart = ref();
|
||||
function getSportStatistic() {
|
||||
const params = {
|
||||
exerciseTime: timeFormat(exerciseTime.value, 'YYYY-MM-DD'),
|
||||
type: dateTypeEnum[unref(dateType)],
|
||||
};
|
||||
exerciseSumApi(params).then(({ code, data }) => {
|
||||
if (code !== 200) return;
|
||||
setPanelData(data);
|
||||
groupList.value = data?.groupList;
|
||||
initChart({ xData: data?.nameList, yData1: data?.scoreList, yData2: data?.consumeValueList });
|
||||
});
|
||||
}
|
||||
|
||||
function initChart({ xData, yData1, yData2 }: { xData: string[]; yData1: number[]; yData2: number[] }) {
|
||||
myChart.value = echarts.init(document.getElementById('sportStatisticsChart'));
|
||||
myChart.value.setOption(sportsStatisticLine({ xData, yData1, yData2 }));
|
||||
}
|
||||
|
||||
const popupData = ref({
|
||||
columns: [],
|
||||
show: false,
|
||||
});
|
||||
|
||||
// 打开周选择
|
||||
function openPopup({ column }) {
|
||||
popupData.value.columns = column;
|
||||
popupData.value.show = true;
|
||||
}
|
||||
|
||||
const datePanel = ref();
|
||||
|
||||
function onConfirm(val) {
|
||||
let text = val.selectedValues[0];
|
||||
exerciseTime.value = text.split('至')[0];
|
||||
initData();
|
||||
datePanel.value.setTimeText(text);
|
||||
popupData.value.show = false;
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
popupData.value.show = false;
|
||||
}
|
||||
//进入运动统计页
|
||||
function openSportDetail({ classId }: { classId: string }) {
|
||||
openPage('/step-statistics', newPageParams({ classId }));
|
||||
}
|
||||
//打开文章详情
|
||||
function openArticle() {
|
||||
let detailId = panelData.value.highDurationTime > 150 ? '506' : '505';
|
||||
openPage('/knowledge-page', newPageParams({ detailId: detailId }));
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
@import '/@/assets/less/index.less';
|
||||
|
||||
.sport-week-page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0 16px 20px 16px;
|
||||
overflow: auto;
|
||||
.flex-column();
|
||||
background: #eeeeee;
|
||||
|
||||
.statistics-card {
|
||||
margin: 10px 0;
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
|
||||
.card-row {
|
||||
display: flex;
|
||||
|
||||
.card-row-col {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
.flex-column();
|
||||
align-items: center;
|
||||
color: #666666;
|
||||
|
||||
.num {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.text {
|
||||
//color: #fefefe;
|
||||
//font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.card-row-col.line-left:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 25%;
|
||||
width: 2px;
|
||||
height: 50%;
|
||||
background-color: rgba(51, 51, 51, 0.3);
|
||||
}
|
||||
|
||||
.card-row-col.line-right:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 25%;
|
||||
width: 2px;
|
||||
height: 50%;
|
||||
background-color: rgba(51, 51, 51, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.card-row:first-of-type {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.summarize {
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
background-color: #ffffff;
|
||||
|
||||
.tit {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.text-area {
|
||||
font-size: 16px;
|
||||
color: #999999;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.link {
|
||||
font-size: 14px;
|
||||
color: #3390ff;
|
||||
text-align: right;
|
||||
|
||||
&:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-data-container {
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
background-color: #ffffff;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin: 12px 0;
|
||||
|
||||
& > div {
|
||||
width: calc(50% - 4px);
|
||||
}
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 10px 20px;
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.sport-statistics-chart {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
import { ref, Ref } from 'vue';
|
||||
import { toFixedNum, toInt } from '/@/hooks/utils';
|
||||
|
||||
export const sportImg = new URL('/@/assets/images/sports-statistics/sport-icon.png', import.meta.url).href;
|
||||
|
||||
interface SportData {
|
||||
exerciseSum: {
|
||||
totalTime: string;
|
||||
highDurationTime: string;
|
||||
activityLevel: string;
|
||||
distance: string;
|
||||
exerciseConsumeSum: string;
|
||||
score: string;
|
||||
glycogenConsume: string;
|
||||
fatConsume: string;
|
||||
};
|
||||
groupList: any[];
|
||||
}
|
||||
|
||||
export function useSportData() {
|
||||
const sportData: Ref<SportData> = ref({
|
||||
exerciseSum: {
|
||||
totalTime: '',
|
||||
highDurationTime: '',
|
||||
activityLevel: '',
|
||||
distance: '',
|
||||
exerciseConsumeSum: '',
|
||||
score: '',
|
||||
glycogenConsume: '',
|
||||
fatConsume: '',
|
||||
},
|
||||
groupList: [],
|
||||
});
|
||||
|
||||
function setSportData(data: SportData): void {
|
||||
const level = toFixedNum(data?.exerciseSum?.activityLevel, 1);
|
||||
sportData.value = {
|
||||
...data,
|
||||
exerciseSum: {
|
||||
...data.exerciseSum,
|
||||
activityLevel: level == '0.0' ? '0' : level,
|
||||
},
|
||||
groupList: data?.groupList?.map((item) => ({
|
||||
...item,
|
||||
totalMinute: toInt(item.totalMinute),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
return [sportData, setSportData];
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
<template>
|
||||
<div class="sports-platform">
|
||||
<div class="scroll-container">
|
||||
<CardTitle leftText="总运动" rightText="运动周报" :showCircle="true" @handle-detail="weekPage" />
|
||||
<div class="basic-card">
|
||||
<div class="card-top">
|
||||
<div class="card-row">
|
||||
<div class="card-row-col">
|
||||
<span class="num">{{ toInt(sportData?.exerciseSum?.totalTime) }}</span>
|
||||
<span class="text">分钟</span>
|
||||
</div>
|
||||
<div class="card-row-col">
|
||||
<span class="num">{{ toInt(sportData?.exerciseSum?.highDurationTime) }}</span>
|
||||
<span class="text">中高强度(分钟)</span>
|
||||
</div>
|
||||
<div class="card-row-col">
|
||||
<span class="num">{{ sportData?.exerciseSum?.activityLevel }}</span>
|
||||
<span class="text">活动水平等级</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<div class="card-row-col">
|
||||
<span class="num">{{ toInt(sportData?.exerciseSum?.distance) }}</span>
|
||||
<span class="text">公里</span>
|
||||
</div>
|
||||
<div class="card-row-col">
|
||||
<span class="num">{{ toInt(sportData?.exerciseSum?.exerciseConsumeSum) }}</span>
|
||||
<span class="text">千卡</span>
|
||||
</div>
|
||||
<div class="card-row-col">
|
||||
<span class="num">{{ toInt(sportData?.exerciseSum?.score) }}</span>
|
||||
<span class="text">当日运动得分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="card-footer-col">
|
||||
<span>糖原消耗: </span>
|
||||
<span>{{ sportData?.exerciseSum?.glycogenConsume }}千卡</span>
|
||||
</div>
|
||||
<div class="card-footer-col">
|
||||
<span>脂肪消耗: </span>
|
||||
<span>{{ sportData?.exerciseSum?.fatConsume }}千卡</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--运动卡片-->
|
||||
<div class="card-list">
|
||||
<SportCard v-for="item in sportData?.groupList" :card-data="item" :key="item.id" @sport-detail="openSportDetail" />
|
||||
</div>
|
||||
<CardTitle leftText="运动排行" rightText="查看完整榜单" @handle-detail="moreRankList" />
|
||||
<!--运动排行-->
|
||||
<div class="rank-list">
|
||||
<div class="rank-header">
|
||||
<div class="head-title">
|
||||
<span class="no">排名</span>
|
||||
<span>姓名</span>
|
||||
</div>
|
||||
<div class="head-name">能耗</div>
|
||||
<div class="head-name">分数</div>
|
||||
<div class="head-name">时长</div>
|
||||
</div>
|
||||
<van-skeleton :row="10" :loading="loading">
|
||||
<div class="rank-table" v-if="rankList.length">
|
||||
<div v-for="(item, i) in rankList" :key="i">
|
||||
<div class="body-column">
|
||||
<div class="head-title">
|
||||
<div class="no">
|
||||
<img class="no-img" v-if="i <= 2" :src="computedNo(i)" alt="" />
|
||||
<span v-else>{{ item.rank }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="d1">{{ item?.userName }}</div>
|
||||
<div class="d2">{{ item?.orgName }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="head-name">{{ item?.consume }}</div>
|
||||
<div class="head-name">{{ item?.score }}</div>
|
||||
<div class="head-name">{{ item?.duration }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<van-empty v-else image-size="100" description="暂无数据" />
|
||||
</van-skeleton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-footer">
|
||||
<div class="btn" @click="addSportInfo">运动录入</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, unref } from 'vue';
|
||||
import CardTitle from '/@/views/motion-data/components/card-title/cardTitle.vue';
|
||||
import SportCard from '/@/views/motion-data/components/sport-card/sportCard.vue';
|
||||
import { imgList } from '/@/views/sport-ranking/sportRanking.data';
|
||||
import { openPage, newPageParams } from '/@/hooks/openPage';
|
||||
import { timeFormat, toFixedNum, toInt } from '/@/hooks/utils';
|
||||
import { topData, rankListApi } from '/@/views/motion-data/api';
|
||||
import { useSportData } from '/@/views/motion-data/sports-platform/data';
|
||||
|
||||
// 排名图标
|
||||
const computedNo = (index: string | number) => {
|
||||
return imgList[index];
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initData();
|
||||
});
|
||||
|
||||
// 初始化数据
|
||||
function initData() {
|
||||
getTopData();
|
||||
getRankList();
|
||||
}
|
||||
|
||||
// 今日运动数据
|
||||
const topDataParams = ref({
|
||||
exerciseTime: timeFormat(new Date(), 'YYYY-MM-DD'),
|
||||
});
|
||||
const [sportData, setSportData] = useSportData();
|
||||
|
||||
function getTopData() {
|
||||
topData({ ...unref(topDataParams) }).then(({ code, data }) => {
|
||||
if (code !== 200) return;
|
||||
setSportData(data);
|
||||
});
|
||||
}
|
||||
|
||||
const loading = ref(true); //加载骨架屏
|
||||
const rankList = ref(); //排行榜
|
||||
// 运动排行
|
||||
function getRankList() {
|
||||
loading.value = true;
|
||||
rankListApi({ type: 'day' })
|
||||
.then(({ code, data }) => {
|
||||
loading.value = false;
|
||||
if (code !== 200) return;
|
||||
rankList.value = data?.userScores.slice(0, 4);
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 运动周报
|
||||
function weekPage() {
|
||||
openPage('/sport-week', newPageParams());
|
||||
}
|
||||
|
||||
// 运动详情页
|
||||
function openSportDetail(val: { classId: any }) {
|
||||
const { classId, className } = val;
|
||||
openPage('/step-statistics', newPageParams({ classId: classId, className: className }));
|
||||
}
|
||||
|
||||
// 完整排行榜单
|
||||
function moreRankList() {
|
||||
openPage('/sport-ranking', newPageParams());
|
||||
}
|
||||
|
||||
function addSportInfo() {
|
||||
openPage('/sport-form', newPageParams({ destroyPage: true }));
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
@import '/@/assets/less/index';
|
||||
|
||||
.sports-platform {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 12px 16px 0 16px;
|
||||
overflow: hidden;
|
||||
.flex-column();
|
||||
background-color: #efefef;
|
||||
|
||||
.scroll-container {
|
||||
flex: 1;
|
||||
.flex-column();
|
||||
overflow: auto;
|
||||
padding-bottom: 40px;
|
||||
|
||||
.basic-card {
|
||||
.flex-column();
|
||||
background: linear-gradient(180deg, #21bebe 0%, #26e0e0 100%);
|
||||
border-radius: 20px;
|
||||
|
||||
.card-top {
|
||||
padding: 10px 0;
|
||||
|
||||
.card-row {
|
||||
display: flex;
|
||||
|
||||
.card-row-col {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
.flex-column();
|
||||
align-items: center;
|
||||
color: #ffffff;
|
||||
|
||||
.num {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: #fefefe;
|
||||
//font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.card-row-col:nth-of-type(2):before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 25%;
|
||||
width: 2px;
|
||||
height: 50%;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.card-row-col:nth-of-type(2):after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 25%;
|
||||
width: 2px;
|
||||
height: 50%;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.card-row:first-of-type {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
background: #21bfbf;
|
||||
border-radius: 0 0 20px 20px;
|
||||
|
||||
.card-footer-col {
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
.flex-center();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin: 12px 0;
|
||||
justify-content: space-between;
|
||||
|
||||
& > div {
|
||||
width: calc(50% - 4px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//排行榜
|
||||
.rank-list {
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.rank-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
margin: 12px 0;
|
||||
color: #999;
|
||||
|
||||
.head-title {
|
||||
flex: 3;
|
||||
display: flex;
|
||||
|
||||
.no {
|
||||
width: 60px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.no:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
top: 20%;
|
||||
width: 1px;
|
||||
height: 60%;
|
||||
background-color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.head-name {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.rank-table {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.body-column {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 10px 0;
|
||||
|
||||
.head-title {
|
||||
flex: 3;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.no {
|
||||
width: 60px;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
color: #666666;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.no-img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
.d1 {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.d2 {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.head-name {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-footer {
|
||||
.flex-center();
|
||||
padding: 8px 14px 12px 14px;
|
||||
.btn {
|
||||
width: 70%;
|
||||
height: 40px;
|
||||
.flex-center();
|
||||
.footerButton();
|
||||
.qs-btn();
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
// 步数
|
||||
import { timeFormat } from '/@/hooks/utils';
|
||||
|
||||
export const stepCardText = [
|
||||
{
|
||||
text1: '步数',
|
||||
text2: '距离',
|
||||
text3: '时长',
|
||||
text4: '消耗',
|
||||
},
|
||||
{
|
||||
text1: '日均步数',
|
||||
text2: '日均距离',
|
||||
text3: '日均时长',
|
||||
text4: '日消耗',
|
||||
},
|
||||
{
|
||||
text1: '月均步数',
|
||||
text2: '月均距离',
|
||||
text3: '月均时长',
|
||||
text4: '月消耗',
|
||||
},
|
||||
];
|
||||
|
||||
export function timeParams(nowTime: string) {
|
||||
if (nowTime.includes('至')) {
|
||||
const d = nowTime.split('至')[0];
|
||||
return timeFormat(d);
|
||||
} else {
|
||||
return timeFormat(nowTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
<template>
|
||||
<div class="step-number">
|
||||
<div class="step-top">
|
||||
<TabTime activeColor="#52c41a" class="tab-time" @emit-active-tab="emitActiveTab" />
|
||||
<date-panel ref="datePanel" :dateType="dateType" @emit-time="emitTime" @emit-popup="openPopup" />
|
||||
<div class="statistics-container panel-m b-12">
|
||||
<div class="img-container">
|
||||
<img class="img" :src="dynamicText.img" alt="" />
|
||||
</div>
|
||||
<div class="data-container">
|
||||
<div class="data-count">
|
||||
<p class="data-number">{{ cardInfo?.totalTime || 0 }}</p>
|
||||
<p class="data-text">累计时长(分)</p>
|
||||
</div>
|
||||
<div class="data-count" v-if="route.query?.classId == '1'">
|
||||
<p class="data-number"> {{ cardInfo?.statisticsObj?.exerciseSum?.step || 0 }}</p>
|
||||
<p class="data-text">累计步数(步)</p>
|
||||
</div>
|
||||
<div class="data-count" v-if="route.query?.classId == '1'">
|
||||
<p class="data-number">{{ cardInfo?.totalFinishCount || 0 }}</p>
|
||||
<p class="data-text">累计运动(次)</p>
|
||||
</div>
|
||||
<div class="data-count">
|
||||
<p class="data-number"> {{ cardInfo?.totalConsumeSum || 0 }}</p>
|
||||
<p class="data-text">累计消耗(千卡)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistics-chart panel-m card-bg b-12" v-show="showChart">
|
||||
<div class="statistics-chart-inner" id="columnar" ref="chartRef"></div>
|
||||
</div>
|
||||
<div class="highest-record panel-m" v-if="cardInfo?.topRecord">
|
||||
<p>{{ getHighestText(activeTab) }}</p>
|
||||
<div class="highest-content card-bg b-12">
|
||||
<div class="icon-time">
|
||||
<img class="first-icon" :src="firstIcon" alt="" />
|
||||
<span class="highest-time">{{ cardInfo?.topRecord?.time || '' }}</span>
|
||||
</div>
|
||||
<div class="highest-info">
|
||||
<span>{{ cardInfo?.topRecord?.consume || '' }} <span>千卡</span></span>
|
||||
<span>{{ cardInfo?.topRecord?.duration || '' }} <span>分钟</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="motion panel-m card-bg b-12">
|
||||
<div class="bb-item">
|
||||
<div>活动消耗</div>
|
||||
<div>
|
||||
<span class="highest-num">{{ cardInfo?.statisticsObj?.exerciseConsumeDail || 0 }}</span>
|
||||
<span>千卡</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bb-item">
|
||||
<div>活动时长</div>
|
||||
<div>
|
||||
<span class="highest-num">{{ cardInfo?.totalTimeDail || 0 }}</span>
|
||||
<span>分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-btn" @click="fillData">录入</div>
|
||||
<van-popup v-model:show="popupData.show" position="bottom">
|
||||
<van-picker title="选择周" :columns="popupData.columns" @cancel="onCancel" @confirm="onConfirm" />
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTabHooks } from '/@/hooks/useTab';
|
||||
import { dateText, dateTypeEnum, firstIcon, getHighestText, stepChart } from '/@/views/motion-data/common.data';
|
||||
import { timeParams } from './hooks';
|
||||
import { ref } from 'vue';
|
||||
import { getSportType } from '/@/views/motion-data/api';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { showToast } from 'vant';
|
||||
import * as echarts from 'echarts';
|
||||
import { newPageParams, openPage } from '/@/hooks/openPage';
|
||||
import TabTime from '/@/views/motion-data/components/tab-time/tabTime.vue';
|
||||
import DatePanel from '/@/views/motion-data/components/date-panel/datePanel.vue';
|
||||
const route = useRoute();
|
||||
document.title = route.query?.className || '';
|
||||
const { activeTab } = useTabHooks(0);
|
||||
// 当前日期类型
|
||||
const dateType = ref('week');
|
||||
const timeText = ref('');
|
||||
const dynamicText = ref(dateText[0]);
|
||||
|
||||
// 年月周切换
|
||||
function emitActiveTab({ type }) {
|
||||
dateType.value = type;
|
||||
dynamicText.value = dateText.find((item) => item.type == dateType.value);
|
||||
}
|
||||
|
||||
// 卡片数据
|
||||
const cardInfo = ref({
|
||||
totalTime: '',
|
||||
totalTimeDail: '',
|
||||
chartVis: true,
|
||||
totalFinishCount: 0,
|
||||
statisticsObj: {},
|
||||
xData: [],
|
||||
yData: [],
|
||||
topRecord: {},
|
||||
totalConsumeSum: 0,
|
||||
});
|
||||
|
||||
function emitTime(nowTime: string) {
|
||||
if (!nowTime) return;
|
||||
timeText.value = timeParams(nowTime);
|
||||
const params = createParams(timeText.value, dateType.value);
|
||||
initData(params);
|
||||
}
|
||||
const chartRef = ref<HTMLElement>();
|
||||
const myChart = ref<any>();
|
||||
const showChart = ref(false);
|
||||
function createParams(exerciseTime: string, type: string) {
|
||||
return {
|
||||
classId: route.query.classId,
|
||||
exerciseTime: exerciseTime,
|
||||
type: dateTypeEnum[type],
|
||||
};
|
||||
}
|
||||
|
||||
function initData(params: any) {
|
||||
showChart.value = false;
|
||||
getSportType(params).then(({ code, data }) => {
|
||||
if (code !== 200) {
|
||||
return showToast('暂无数据');
|
||||
}
|
||||
cardInfo.value = {
|
||||
totalTime: data?.exerciseSum?.totalTime,
|
||||
totalFinishCount: data?.exerciseSum?.totalFinishCount,
|
||||
totalTimeDail: data.totalTimeDail,
|
||||
chartVis: true,
|
||||
statisticsObj: data,
|
||||
xData: data?.nameList,
|
||||
yData: data?.consumeValueList,
|
||||
topRecord: data?.singleDayRecord,
|
||||
totalConsumeSum: data?.exerciseSum?.totalConsumeSum,
|
||||
};
|
||||
if (data?.nameList.length) {
|
||||
showChart.value = true;
|
||||
setTimeout(() => {
|
||||
initChart({ xData: data?.nameList, yData: data?.consumeValueList });
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initChart({ xData, yData }) {
|
||||
myChart.value = echarts.init(document.getElementById('columnar'));
|
||||
myChart.value.setOption(stepChart({ xData, yData }));
|
||||
}
|
||||
|
||||
function fillData() {
|
||||
openPage('/sport-form', newPageParams());
|
||||
}
|
||||
|
||||
const popupData = ref({
|
||||
columns: [],
|
||||
show: false,
|
||||
});
|
||||
|
||||
// 打开周选择
|
||||
function openPopup({ column }) {
|
||||
popupData.value.columns = column;
|
||||
popupData.value.show = true;
|
||||
}
|
||||
|
||||
const datePanel = ref();
|
||||
|
||||
function onConfirm(val) {
|
||||
let text = val.selectedValues[0];
|
||||
timeText.value = timeParams(text);
|
||||
const params = createParams(timeText.value, dateType.value);
|
||||
initData(params);
|
||||
datePanel.value.setTimeText(text);
|
||||
popupData.value.show = false;
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
popupData.value.show = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/less/index.less';
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.panel-m {
|
||||
margin: 6px 16px;
|
||||
}
|
||||
|
||||
.card-bg {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.b-12 {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
height: 100vh;
|
||||
background: #eeeeee;
|
||||
box-sizing: border-box;
|
||||
//padding-top: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.step-top {
|
||||
height: calc(100vh - 80px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
|
||||
.tab-time {
|
||||
margin: 10px 20px;
|
||||
padding: 2px;
|
||||
height: 35px;
|
||||
background: #ffffff;
|
||||
border-radius: 35px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.date-panel {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
|
||||
.date-time {
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-container {
|
||||
padding: 13px 0;
|
||||
background: #ffffff;
|
||||
.flex-center();
|
||||
|
||||
.img-container {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin: 0 16px;
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.data-container {
|
||||
flex: 4;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.data-count {
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
|
||||
.data-number {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.data-text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-chart {
|
||||
//min-height: 240px;
|
||||
.statistics-chart-inner {
|
||||
width: 100%;
|
||||
min-height: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.bar-data {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 16px;
|
||||
|
||||
.bar-data-item {
|
||||
width: 50%;
|
||||
|
||||
& p,
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.bar-data-number {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.highest-record {
|
||||
& > p {
|
||||
font-size: 16px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.highest-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
|
||||
.icon-time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.first-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.highest-time {
|
||||
flex: 1;
|
||||
margin: 0 15px 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.highest-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.motion {
|
||||
padding: 14px;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
|
||||
.bb-item {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.highest-num {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.step-btn {
|
||||
margin: 12px 15% 28px 15%;
|
||||
height: 40px;
|
||||
.flex-center();
|
||||
.qs-btn();
|
||||
border-radius: 40px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,14 @@
|
||||
export const yogaCardText = [
|
||||
{
|
||||
text1: '日均时长',
|
||||
text2: '日均消耗',
|
||||
},
|
||||
{
|
||||
text1: '日均时长',
|
||||
text2: '日均消耗',
|
||||
},
|
||||
{
|
||||
text1: '月均时长',
|
||||
text2: '月均消耗',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,283 @@
|
||||
<template>
|
||||
<div class="yoga-number">
|
||||
<div class="yoga-top">
|
||||
<div class="tab-time">
|
||||
<div
|
||||
class="tab-time-item"
|
||||
v-for="(item, i) in dynamicText"
|
||||
:key="i"
|
||||
:class="[activeTab == i ? 'active' : '']"
|
||||
@click="setActiveTab(i)"
|
||||
v-text="item.text"
|
||||
></div>
|
||||
</div>
|
||||
<div class="date-panel">
|
||||
<van-icon name="arrow-left" />
|
||||
<span class="date-time" @click="changeDate">9月4日至9月10日</span>
|
||||
<van-icon name="arrow" />
|
||||
</div>
|
||||
<div class="statistics-container panel-m b-12">
|
||||
<div class="img-container">
|
||||
<img class="img" :src="dynamicText[activeTab].img" alt="" />
|
||||
</div>
|
||||
<div class="data-container">
|
||||
<div class="data-count">
|
||||
<p class="data-number">487</p>
|
||||
<p class="data-text">时长(分钟)</p>
|
||||
</div>
|
||||
<div class="data-count">
|
||||
<p class="data-number">123</p>
|
||||
<p class="data-text">累计{{ dynamicText[activeTab].text }}消耗</p>
|
||||
</div>
|
||||
<div class="data-count">
|
||||
<p class="data-number">123</p>
|
||||
<p class="data-text">累计完成(次)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistics-chart panel-m card-bg b-12">
|
||||
<div class="statistics-chart-inner"> 图表1图表1 图表1图表1</div>
|
||||
</div>
|
||||
<div class="bar-data panel-m card-bg b-12">
|
||||
<div class="bar-data-item">
|
||||
<p>{{ cardText.text1 }}</p>
|
||||
<p><span class="bar-data-number">123</span> <span>分钟</span></p>
|
||||
</div>
|
||||
<div class="bar-data-item">
|
||||
<p>{{ cardText.text2 }}</p>
|
||||
<p><span class="bar-data-number">34</span> <span>米</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="highest-record panel-m">
|
||||
<p>{{ getHighestText(activeTab) }}</p>
|
||||
<div class="highest-content card-bg b-12">
|
||||
<div class="icon-time">
|
||||
<img class="first-icon" :src="firstIcon" alt="" />
|
||||
<span class="highest-time">2020-11-28</span>
|
||||
</div>
|
||||
<div class="highest-info">
|
||||
<span style="opacity: 0"></span>
|
||||
<span>13.62 <span>分钟</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="yoga-btn">录入</div>
|
||||
<DatePopup ref="datePopup" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTabHooks } from '/@/hooks/useTab';
|
||||
import { dateText, firstIcon, getHighestText } from '/@/views/motion-data/common.data';
|
||||
import { yogaCardText } from './hooks';
|
||||
import { computed, ref } from 'vue';
|
||||
import DatePopup from '/@/views/motion-data/components/date-popup/datePopup.vue';
|
||||
|
||||
const { activeTab, setActiveTab } = useTabHooks(0);
|
||||
let query = 'yoga';
|
||||
const dynamicText = computed(() => {
|
||||
if (query == 'yoga') {
|
||||
return dateText;
|
||||
}
|
||||
});
|
||||
const cardText = computed(() => {
|
||||
if (query == 'yoga') {
|
||||
return yogaCardText[activeTab.value];
|
||||
}
|
||||
});
|
||||
const datePopup = ref();
|
||||
|
||||
function changeDate() {
|
||||
datePopup.value.showPopup();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/less/index.less';
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.panel-m {
|
||||
margin: 6px 16px;
|
||||
}
|
||||
|
||||
.card-bg {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.b-12 {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.yoga-number {
|
||||
height: 100vh;
|
||||
background: #eeeeee;
|
||||
box-sizing: border-box;
|
||||
//padding-top: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.yoga-top {
|
||||
height: calc(100vh - 80px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
|
||||
.tab-time {
|
||||
margin: 10px 20px;
|
||||
padding: 2px;
|
||||
height: 35px;
|
||||
background: #ffffff;
|
||||
border-radius: 35px;
|
||||
display: flex;
|
||||
|
||||
.tab-time-item {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
.flex-center();
|
||||
|
||||
&.active {
|
||||
color: #fff;
|
||||
border-radius: 20px;
|
||||
background: #52c41a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.date-panel {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
|
||||
.date-time {
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-container {
|
||||
height: 140px;
|
||||
padding: 6px 0;
|
||||
background: #ffffff;
|
||||
.flex-center();
|
||||
|
||||
.img-container {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin: 0 16px;
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.data-container {
|
||||
flex: 4;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.data-count {
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
|
||||
.data-number {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.data-text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-chart {
|
||||
//min-height: 240px;
|
||||
.statistics-chart-inner {
|
||||
min-height: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.bar-data {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 16px;
|
||||
|
||||
.bar-data-item {
|
||||
width: 50%;
|
||||
|
||||
& p,
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.bar-data-number {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.highest-record {
|
||||
& > p {
|
||||
font-size: 16px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.highest-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
|
||||
.icon-time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.first-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.highest-time {
|
||||
flex: 1;
|
||||
margin: 0 15px 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.highest-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.yoga-btn {
|
||||
margin: 12px 15% 28px 15%;
|
||||
height: 40px;
|
||||
.flex-center();
|
||||
.qs-btn();
|
||||
border-radius: 40px;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user