update:删除个人训练首页的无用组件
This commit is contained in:
@@ -1,200 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="radar-panel">
|
|
||||||
<canvas
|
|
||||||
:canvas-id="canvasId"
|
|
||||||
:id="canvasId"
|
|
||||||
class="radar-canvas"
|
|
||||||
:style="{ width: `${displayWidth}rpx`, height: `${displayHeight}rpx` }"
|
|
||||||
:width="canvasWidth"
|
|
||||||
:height="canvasHeight"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<text class="radar-label top">{{ labels[0] }}</text>
|
|
||||||
<text class="radar-label right">{{ labels[1] }}</text>
|
|
||||||
<text class="radar-label bottom-right">{{ labels[2] }}</text>
|
|
||||||
<text class="radar-label bottom-left">{{ labels[3] }}</text>
|
|
||||||
<text class="radar-label left">{{ labels[4] }}</text>
|
|
||||||
|
|
||||||
<TrainingRecordBubble
|
|
||||||
:surpass-text="surpassText"
|
|
||||||
:record-text="recordText"
|
|
||||||
@record="$emit('record')"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { getCurrentInstance, nextTick, onMounted, watch } from 'vue'
|
|
||||||
import TrainingRecordBubble from './TrainingRecordBubble.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
labels: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
values: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
maxValue: {
|
|
||||||
type: Number,
|
|
||||||
default: 10
|
|
||||||
},
|
|
||||||
surpassText: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
recordText: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
defineEmits(['record'])
|
|
||||||
|
|
||||||
const canvasId = 'training-radar'
|
|
||||||
const canvasWidth = 280
|
|
||||||
const canvasHeight = 230
|
|
||||||
const displayWidth = 560
|
|
||||||
const displayHeight = 460
|
|
||||||
const instance = getCurrentInstance()
|
|
||||||
|
|
||||||
function getPoint(centerX, centerY, radius, angle) {
|
|
||||||
return {
|
|
||||||
x: centerX + radius * Math.cos(angle),
|
|
||||||
y: centerY + radius * Math.sin(angle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawRadar() {
|
|
||||||
const target = instance?.proxy
|
|
||||||
const ctx = uni.createCanvasContext(canvasId, target)
|
|
||||||
const centerX = canvasWidth / 2
|
|
||||||
const centerY = 118
|
|
||||||
const layerCount = 4
|
|
||||||
const outerRadius = 88
|
|
||||||
const angles = props.labels.map((_, index) => (-90 + index * 72) * (Math.PI / 180))
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, canvasWidth, canvasHeight)
|
|
||||||
|
|
||||||
for (let layer = layerCount; layer >= 1; layer -= 1) {
|
|
||||||
const radius = (outerRadius / layerCount) * layer
|
|
||||||
ctx.beginPath()
|
|
||||||
angles.forEach((angle, index) => {
|
|
||||||
const point = getPoint(centerX, centerY, radius, angle)
|
|
||||||
if (index === 0) {
|
|
||||||
ctx.moveTo(point.x, point.y)
|
|
||||||
} else {
|
|
||||||
ctx.lineTo(point.x, point.y)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
ctx.closePath()
|
|
||||||
ctx.setStrokeStyle(layer === layerCount ? 'rgba(125,107,83,0.75)' : 'rgba(125,107,83,0.4)')
|
|
||||||
ctx.setLineWidth(1)
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
|
|
||||||
angles.forEach((angle) => {
|
|
||||||
const point = getPoint(centerX, centerY, outerRadius, angle)
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.moveTo(centerX, centerY)
|
|
||||||
ctx.lineTo(point.x, point.y)
|
|
||||||
ctx.setStrokeStyle('rgba(125,107,83,0.36)')
|
|
||||||
ctx.setLineWidth(1)
|
|
||||||
ctx.stroke()
|
|
||||||
})
|
|
||||||
|
|
||||||
const points = props.values.map((value, index) => {
|
|
||||||
const radius = (Math.max(0, Math.min(value, props.maxValue)) / props.maxValue) * outerRadius
|
|
||||||
return getPoint(centerX, centerY, radius, angles[index])
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
points.forEach((point, index) => {
|
|
||||||
if (index === 0) {
|
|
||||||
ctx.moveTo(point.x, point.y)
|
|
||||||
} else {
|
|
||||||
ctx.lineTo(point.x, point.y)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
ctx.closePath()
|
|
||||||
ctx.setFillStyle('rgba(212, 156, 89, 0.12)')
|
|
||||||
ctx.fill()
|
|
||||||
ctx.setStrokeStyle('rgba(212, 156, 89, 0.95)')
|
|
||||||
ctx.setLineWidth(2)
|
|
||||||
ctx.stroke()
|
|
||||||
|
|
||||||
points.forEach((point) => {
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.arc(point.x, point.y, 4, 0, 2 * Math.PI)
|
|
||||||
ctx.setFillStyle('rgba(212, 156, 89, 1)')
|
|
||||||
ctx.fill()
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.arc(centerX, centerY, 4, 0, 2 * Math.PI)
|
|
||||||
ctx.setFillStyle('rgba(125,107,83,0.7)')
|
|
||||||
ctx.fill()
|
|
||||||
|
|
||||||
ctx.draw()
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
nextTick(drawRadar)
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.values,
|
|
||||||
() => {
|
|
||||||
nextTick(drawRadar)
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.radar-panel {
|
|
||||||
position: relative;
|
|
||||||
margin-top: 24rpx;
|
|
||||||
padding: 26rpx 0 18rpx;
|
|
||||||
min-height: 560rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radar-canvas {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radar-label {
|
|
||||||
position: absolute;
|
|
||||||
color: rgba(243, 224, 185, 0.8);
|
|
||||||
font-size: 34rpx;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top {
|
|
||||||
top: 26rpx;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
top: 208rpx;
|
|
||||||
right: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-right {
|
|
||||||
right: 140rpx;
|
|
||||||
bottom: 86rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-left {
|
|
||||||
left: 142rpx;
|
|
||||||
bottom: 86rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left {
|
|
||||||
top: 208rpx;
|
|
||||||
left: 28rpx;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="featured-banner" @tap="$emit('tap-banner')">
|
|
||||||
<view class="banner-copy">
|
|
||||||
<text class="banner-title">{{ title }}</text>
|
|
||||||
<text class="banner-progress">{{ progressText }}</text>
|
|
||||||
<text class="banner-desc">{{ description }}</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="banner-ornament">
|
|
||||||
<view class="ornament-ring ring-large"></view>
|
|
||||||
<view class="ornament-ring ring-small"></view>
|
|
||||||
<view class="ornament-core">训</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
defineProps({
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
progressText: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
defineEmits(['tap-banner'])
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.featured-banner {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 22rpx;
|
|
||||||
padding: 28rpx 28rpx 24rpx;
|
|
||||||
border-radius: 26rpx;
|
|
||||||
background:
|
|
||||||
radial-gradient(circle at 78% 25%, rgba(255, 238, 186, 0.82), rgba(255, 238, 186, 0) 26%),
|
|
||||||
linear-gradient(135deg, #f3ca98 0%, #e8be8d 48%, #e6b97f 100%);
|
|
||||||
box-shadow: 0 10rpx 24rpx rgba(0, 0, 0, 0.16);
|
|
||||||
}
|
|
||||||
|
|
||||||
.featured-banner::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
right: -36rpx;
|
|
||||||
bottom: -52rpx;
|
|
||||||
width: 220rpx;
|
|
||||||
height: 220rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: radial-gradient(circle, rgba(255, 217, 71, 0.55) 0%, rgba(255, 217, 71, 0) 70%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner-copy {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
max-width: 390rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner-title {
|
|
||||||
display: inline-block;
|
|
||||||
color: #b86c11;
|
|
||||||
font-size: 44rpx;
|
|
||||||
font-weight: 800;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner-progress {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 14rpx;
|
|
||||||
color: #895409;
|
|
||||||
font-size: 32rpx;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner-desc {
|
|
||||||
display: block;
|
|
||||||
margin-top: 14rpx;
|
|
||||||
color: rgba(137, 84, 9, 0.94);
|
|
||||||
font-size: 22rpx;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner-ornament {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
width: 152rpx;
|
|
||||||
height: 112rpx;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ornament-ring {
|
|
||||||
position: absolute;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 4rpx solid rgba(255, 255, 255, 0.48);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ring-large {
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 124rpx;
|
|
||||||
height: 124rpx;
|
|
||||||
transform: rotate(-18deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ring-small {
|
|
||||||
right: 32rpx;
|
|
||||||
top: 18rpx;
|
|
||||||
width: 64rpx;
|
|
||||||
height: 64rpx;
|
|
||||||
border-color: rgba(184, 108, 17, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ornament-core {
|
|
||||||
position: absolute;
|
|
||||||
right: 28rpx;
|
|
||||||
top: 24rpx;
|
|
||||||
width: 72rpx;
|
|
||||||
height: 72rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(180deg, rgba(255, 217, 71, 1) 0%, rgba(231, 186, 128, 1) 100%);
|
|
||||||
color: #6f4300;
|
|
||||||
font-size: 34rpx;
|
|
||||||
font-weight: 800;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-shadow: 0 8rpx 20rpx rgba(184, 108, 17, 0.28);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="nav-shell" :style="{ paddingTop: `${statusBarHeight}px` }">
|
|
||||||
<view class="top-row">
|
|
||||||
<text class="time-text">{{ currentTime }}</text>
|
|
||||||
<view class="capsule">
|
|
||||||
<view class="capsule-dots">
|
|
||||||
<text class="dot"></text>
|
|
||||||
<text class="dot active"></text>
|
|
||||||
<text class="dot"></text>
|
|
||||||
</view>
|
|
||||||
<view class="capsule-divider"></view>
|
|
||||||
<view class="capsule-circle"></view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="action-row">
|
|
||||||
<button class="back-button" hover-class="none" @tap="$emit('back')">
|
|
||||||
<image class="back-icon" src="/static/training-home/icons/back.svg" mode="aspectFit" />
|
|
||||||
</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
|
||||||
|
|
||||||
const emit = defineEmits(['back'])
|
|
||||||
|
|
||||||
const currentTime = ref('9:41')
|
|
||||||
const statusBarHeight = ref(24)
|
|
||||||
let timer = null
|
|
||||||
|
|
||||||
function formatTime(date) {
|
|
||||||
const hours = String(date.getHours()).padStart(2, '0')
|
|
||||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
|
||||||
return `${hours}:${minutes}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function syncTime() {
|
|
||||||
currentTime.value = formatTime(new Date())
|
|
||||||
}
|
|
||||||
|
|
||||||
function syncSafeArea() {
|
|
||||||
const info = typeof uni.getSystemInfoSync === 'function' ? uni.getSystemInfoSync() : null
|
|
||||||
statusBarHeight.value = info?.statusBarHeight || 24
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
syncSafeArea()
|
|
||||||
syncTime()
|
|
||||||
timer = setInterval(syncTime, 60 * 1000)
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
if (timer) {
|
|
||||||
clearInterval(timer)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.nav-shell {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
padding-left: 20rpx;
|
|
||||||
padding-right: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-text {
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 30rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capsule {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 176rpx;
|
|
||||||
height: 58rpx;
|
|
||||||
padding: 0 18rpx;
|
|
||||||
border-radius: 44rpx;
|
|
||||||
border: 1rpx solid rgba(255, 255, 255, 0.18);
|
|
||||||
background: rgba(0, 0, 0, 0.18);
|
|
||||||
backdrop-filter: blur(10rpx);
|
|
||||||
}
|
|
||||||
|
|
||||||
.capsule-dots {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dot {
|
|
||||||
width: 10rpx;
|
|
||||||
height: 10rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgba(255, 255, 255, 0.55);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dot.active {
|
|
||||||
width: 14rpx;
|
|
||||||
height: 14rpx;
|
|
||||||
background: rgba(255, 255, 255, 0.95);
|
|
||||||
}
|
|
||||||
|
|
||||||
.capsule-divider {
|
|
||||||
width: 1rpx;
|
|
||||||
height: 26rpx;
|
|
||||||
background: rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.capsule-circle {
|
|
||||||
width: 28rpx;
|
|
||||||
height: 28rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 6rpx solid rgba(255, 255, 255, 0.9);
|
|
||||||
box-shadow: inset 0 0 0 4rpx rgba(0, 0, 0, 0.28);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 18rpx;
|
|
||||||
min-height: 48rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
width: 48rpx;
|
|
||||||
height: 48rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-icon {
|
|
||||||
width: 30rpx;
|
|
||||||
height: 30rpx;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="badge">
|
|
||||||
<text class="badge-text">{{ text }}</text>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
defineProps({
|
|
||||||
text: {
|
|
||||||
type: String,
|
|
||||||
default: '推荐'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.badge {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
padding: 8rpx 16rpx 6rpx;
|
|
||||||
border-radius: 16rpx 4rpx 20rpx 4rpx;
|
|
||||||
background: linear-gradient(135deg, rgba(255, 209, 154, 1) 0%, rgba(161, 118, 54, 1) 100%);
|
|
||||||
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-text {
|
|
||||||
color: #000000;
|
|
||||||
font-size: 20rpx;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="mode-card" :class="{ disabled }" @tap="$emit('select')">
|
|
||||||
<RecommendBadge v-if="recommended" />
|
|
||||||
|
|
||||||
<view class="copy-wrap">
|
|
||||||
<text class="mode-title">{{ title }}</text>
|
|
||||||
<text class="mode-subtitle">{{ subtitle }}</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<image class="mode-icon" :src="icon" mode="aspectFit" />
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import RecommendBadge from './RecommendBadge.vue'
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
subtitle: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
recommended: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
defineEmits(['select'])
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.mode-card {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
min-height: 148rpx;
|
|
||||||
padding: 28rpx 24rpx;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
border: 1rpx solid rgba(255, 217, 71, 0.24);
|
|
||||||
background:
|
|
||||||
radial-gradient(circle at 78% 26%, rgba(255, 244, 205, 0.14), rgba(255, 244, 205, 0) 25%),
|
|
||||||
linear-gradient(180deg, rgba(34, 34, 46, 0.92) 0%, rgba(17, 19, 27, 0.96) 100%);
|
|
||||||
box-shadow: inset 0 2rpx 8rpx rgba(255, 255, 255, 0.04);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mode-card.disabled {
|
|
||||||
opacity: 0.78;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-wrap {
|
|
||||||
max-width: 250rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mode-title {
|
|
||||||
display: block;
|
|
||||||
color: rgba(231, 186, 128, 1);
|
|
||||||
font-size: 44rpx;
|
|
||||||
font-weight: 800;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mode-subtitle {
|
|
||||||
display: block;
|
|
||||||
margin-top: 18rpx;
|
|
||||||
color: rgba(252, 206, 150, 0.78);
|
|
||||||
font-size: 22rpx;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mode-icon {
|
|
||||||
width: 86rpx;
|
|
||||||
height: 86rpx;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="grid-wrap">
|
|
||||||
<TrainingModeCard
|
|
||||||
v-for="item in items"
|
|
||||||
:key="item.key"
|
|
||||||
:title="item.title"
|
|
||||||
:subtitle="item.subtitle"
|
|
||||||
:icon="item.icon"
|
|
||||||
:recommended="item.recommended"
|
|
||||||
:disabled="item.disabled"
|
|
||||||
@select="$emit('select', item)"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import TrainingModeCard from './TrainingModeCard.vue'
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
items: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
defineEmits(['select'])
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.grid-wrap {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
gap: 18rpx;
|
|
||||||
margin-top: 18rpx;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="bubble-wrap" @tap="$emit('record')">
|
|
||||||
<view class="bubble-body">
|
|
||||||
<text class="bubble-main">{{ surpassText }}</text>
|
|
||||||
<view class="bubble-row">
|
|
||||||
<text class="bubble-sub">{{ recordText }}</text>
|
|
||||||
<text class="bubble-arrow">></text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="bubble-badge">L</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
defineProps({
|
|
||||||
surpassText: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
recordText: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
defineEmits(['record'])
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.bubble-wrap {
|
|
||||||
position: absolute;
|
|
||||||
top: 10rpx;
|
|
||||||
right: 8rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-body {
|
|
||||||
padding: 14rpx 18rpx;
|
|
||||||
border: 2rpx dashed rgba(255, 255, 255, 0.24);
|
|
||||||
border-radius: 32rpx;
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-main {
|
|
||||||
display: block;
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 6rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-sub {
|
|
||||||
color: rgba(255, 217, 71, 1);
|
|
||||||
font-size: 24rpx;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-arrow {
|
|
||||||
margin-left: 8rpx;
|
|
||||||
color: rgba(255, 217, 71, 1);
|
|
||||||
font-size: 24rpx;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-badge {
|
|
||||||
width: 64rpx;
|
|
||||||
height: 64rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(180deg, rgba(255, 217, 71, 1) 0%, rgba(231, 186, 128, 1) 100%);
|
|
||||||
color: #6f4300;
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-weight: 800;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-shadow: 0 8rpx 16rpx rgba(255, 217, 71, 0.22);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="summary-card">
|
|
||||||
<text class="quote quote-left">“</text>
|
|
||||||
<text class="quote quote-right">”</text>
|
|
||||||
|
|
||||||
<view v-for="item in items" :key="item.key" class="summary-item">
|
|
||||||
<view class="value-row">
|
|
||||||
<text class="value-text">{{ item.value }}</text>
|
|
||||||
<text class="unit-text">{{ item.unit }}</text>
|
|
||||||
</view>
|
|
||||||
<text class="label-text">{{ item.label }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
defineProps({
|
|
||||||
items: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.summary-card {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
margin-top: 28rpx;
|
|
||||||
padding: 30rpx 18rpx 20rpx;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
background: rgba(34, 34, 46, 0.76);
|
|
||||||
box-shadow: inset 0 2rpx 6rpx rgba(255, 255, 255, 0.06);
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary-item {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 2rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value-text {
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 50rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unit-text {
|
|
||||||
color: rgba(243, 224, 185, 1);
|
|
||||||
font-size: 22rpx;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-text {
|
|
||||||
color: rgba(252, 206, 150, 1);
|
|
||||||
font-size: 20rpx;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote {
|
|
||||||
position: absolute;
|
|
||||||
color: rgba(255, 255, 255, 0.08);
|
|
||||||
font-size: 66rpx;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-left {
|
|
||||||
left: 12rpx;
|
|
||||||
top: 4rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-right {
|
|
||||||
right: 16rpx;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="week-bar">
|
|
||||||
<view
|
|
||||||
v-for="item in items"
|
|
||||||
:key="item.key"
|
|
||||||
class="day-card"
|
|
||||||
:class="[`status-${item.status}`]"
|
|
||||||
>
|
|
||||||
<view class="icon-wrap">
|
|
||||||
<image
|
|
||||||
v-if="item.status === 'done'"
|
|
||||||
class="state-icon"
|
|
||||||
src="/static/training-home/icons/check.svg"
|
|
||||||
mode="aspectFit"
|
|
||||||
/>
|
|
||||||
<image
|
|
||||||
v-else-if="item.status === 'failed'"
|
|
||||||
class="state-icon"
|
|
||||||
src="/static/training-home/icons/close.svg"
|
|
||||||
mode="aspectFit"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
<text class="day-text">{{ item.label }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
defineProps({
|
|
||||||
items: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.week-bar {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.day-card {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
height: 96rpx;
|
|
||||||
padding: 14rpx 0 10rpx;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
background: linear-gradient(180deg, rgba(47, 45, 43, 0.9) 0%, rgba(37, 40, 49, 0.92) 100%);
|
|
||||||
box-shadow: inset 0 2rpx 6rpx rgba(255, 255, 255, 0.08);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-wrap {
|
|
||||||
width: 34rpx;
|
|
||||||
height: 34rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-done .icon-wrap {
|
|
||||||
background: linear-gradient(180deg, rgba(255, 217, 71, 1) 0%, rgba(231, 186, 128, 1) 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-failed .icon-wrap {
|
|
||||||
background: rgba(202, 202, 202, 0.92);
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-current .icon-wrap {
|
|
||||||
border: 2rpx solid rgba(255, 217, 71, 0.45);
|
|
||||||
background: rgba(255, 255, 255, 0.02);
|
|
||||||
}
|
|
||||||
|
|
||||||
.state-icon {
|
|
||||||
width: 20rpx;
|
|
||||||
height: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.day-text {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: rgba(255, 255, 255, 0.88);
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-done .day-text {
|
|
||||||
color: rgba(231, 186, 128, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-current .day-text {
|
|
||||||
color: rgba(255, 217, 71, 1);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
Reference in New Issue
Block a user