完成我的成长脚印UI

This commit is contained in:
kron
2025-05-27 12:38:39 +08:00
parent e9070438f2
commit 6b4eff428c
10 changed files with 336 additions and 51 deletions

View File

@@ -11,7 +11,7 @@ const bgs = ref(["../static/app-bg.png", "../static/app-bg2.png"]);
<template> <template>
<view class="background"> <view class="background">
<image class="bg-image" :src="bgs[type]" mode="aspectFill" /> <image class="bg-image" :src="bgs[type]" mode="widthFix" />
<view class="bg-overlay" v-if="type === 0"></view> <view class="bg-overlay" v-if="type === 0"></view>
</view> </view>
</template> </template>
@@ -27,11 +27,8 @@ const bgs = ref(["../static/app-bg.png", "../static/app-bg2.png"]);
} }
.bg-image { .bg-image {
position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
left: 0;
top: 0;
} }
.bg-overlay { .bg-overlay {
@@ -42,8 +39,8 @@ const bgs = ref(["../static/app-bg.png", "../static/app-bg2.png"]);
top: 0; top: 0;
background: linear-gradient( background: linear-gradient(
to bottom, to bottom,
rgba(26, 26, 26, 0.8), rgba(26, 26, 26, 0.2),
rgba(0, 0, 0, 0.8) rgba(0, 0, 0, 0.2)
); );
} }
</style> </style>

View File

@@ -1,16 +1,16 @@
<script setup> <script setup>
import { ref } from "vue";
const activeTab = ref("member");
const tabs = [ const tabs = [
{ id: "member", image: "../static/tab-vip.png" }, { image: "../static/tab-vip.png" },
{ id: "growth", image: "../static/tab-grow.png" }, { image: "../static/tab-grow.png" },
{ id: "shop", image: "../static/tab-mall.png" }, { image: "../static/tab-mall.png" },
]; ];
function handleTabClick(tabId) { function handleTabClick(index) {
activeTab.value = tabId; if (index === 1) {
uni.navigateTo({
url: "/pages/my-growth",
});
}
} }
</script> </script>
@@ -19,10 +19,9 @@ function handleTabClick(tabId) {
<image class="footer-bg" src="../static/tab-bg.png" mode="widthFix" /> <image class="footer-bg" src="../static/tab-bg.png" mode="widthFix" />
<view <view
v-for="(tab, index) in tabs" v-for="(tab, index) in tabs"
:key="tab.id" :key="index"
class="tab-item" class="tab-item"
:class="{ active: activeTab === tab.id }" @click="handleTabClick(index)"
@click="handleTabClick(tab.id)"
> >
<image <image
:src="tab.image" :src="tab.image"

87
src/components/Avatar.vue Normal file
View File

@@ -0,0 +1,87 @@
<script setup>
defineProps({
src: {
type: String,
default: "",
},
onClick: {
type: Function,
default: () => {},
},
frame: {
type: Boolean,
default: false,
},
rank: {
type: Number,
default: 0,
},
});
</script>
<template>
<view class="avatar" @click="onClick">
<image
v-if="frame"
src="../static/avatar-frame.png"
mode="widthFix"
class="avatar-frame"
/>
<image
v-if="rank === 1"
src="../static/champ1.png"
mode="widthFix"
class="avatar-rank"
/>
<image
v-if="rank === 2"
src="../static/champ2.png"
mode="widthFix"
class="avatar-rank"
/>
<image
v-if="rank === 3"
src="../static/champ3.png"
mode="widthFix"
class="avatar-rank"
/>
<view v-if="rank > 3" class="rank-view">{{ rank }}</view>
<image :src="src" mode="widthFix" />
</view>
</template>
<style scoped>
.avatar {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.avatar-frame {
position: absolute;
width: 55px;
height: 55px;
}
.avatar-rank,
.rank-view {
position: absolute;
width: 20px;
height: 15px;
top: -6px;
right: -4px;
}
.rank-view {
background-color: #6d6d6d;
text-align: center;
line-height: 15px;
font-size: 12px;
border-top-left-radius: 50%;
border-bottom-right-radius: 50%;
}
.avatar > image:last-child {
width: 45px;
height: 45px;
border-radius: 50%;
border: 1px solid #fff;
}
</style>

View File

@@ -7,7 +7,7 @@ defineProps({
default: "", default: "",
}, },
bgType: { bgType: {
type: String, type: Number,
default: 0, default: 0,
}, },
}); });

View File

@@ -1,5 +1,6 @@
<script setup> <script setup>
import { computed } from "vue"; import { computed } from "vue";
import Avatar from "@/components/Avatar.vue";
const props = defineProps({ const props = defineProps({
showRank: { showRank: {
type: Boolean, type: Boolean,
@@ -8,11 +9,11 @@ const props = defineProps({
user: { user: {
type: Object, type: Object,
default: () => ({ default: () => ({
nickName:'', nickName: "",
lvl:0, lvl: 0,
points:0, points: 0,
rankLvl:0, rankLvl: 0,
lvlPoints:0, lvlPoints: 0,
}), }),
}, },
}); });
@@ -33,10 +34,7 @@ const toUserPage = () => {
<template> <template>
<view class="container" :style="{ width: containerWidth }"> <view class="container" :style="{ width: containerWidth }">
<view class="avatar" @click="toUserPage"> <Avatar frame="true" src="../static/avatar.png" :onClick="toUserPage" />
<image src="../static/avatar-frame.png" mode="widthFix" />
<image src="../static/avatar.png" mode="widthFix" />
</view>
<view class="user-details"> <view class="user-details">
<view class="user-name"> <view class="user-name">
<text>{{ user.nickName }}</text> <text>{{ user.nickName }}</text>
@@ -72,25 +70,6 @@ const toUserPage = () => {
color: #fff; color: #fff;
} }
.avatar {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.avatar > image:first-child {
position: absolute;
width: 55px;
height: 55px;
}
.avatar > image {
width: 45px;
height: 45px;
border-radius: 50%;
}
.user-details { .user-details {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -165,7 +165,6 @@ onMounted(() => {
</view> </view>
</view> </view>
</view> </view>
<AppFooter /> <AppFooter />
</view> </view>
</template> </template>

View File

@@ -5,8 +5,8 @@ import SButton from "@/components/SButton.vue";
import useStore from "@/store"; import useStore from "@/store";
import { loginAPI } from "@/apis"; import { loginAPI } from "@/apis";
const store = useStore(); const store = useStore();
// 使用actions方法
const { updateUser, updateToken } = store; const { updateUser } = store;
const agree = ref(false); const agree = ref(false);
const handleAgree = () => { const handleAgree = () => {
@@ -31,7 +31,6 @@ const handleLogin = () => {
const { code } = loginRes; const { code } = loginRes;
const result = await loginAPI(nickName, avatarUrl, code); const result = await loginAPI(nickName, avatarUrl, code);
updateUser(result.user); updateUser(result.user);
updateToken(result.token, result.expires);
uni.navigateBack(); uni.navigateBack();
}, },
fail: (err) => { fail: (err) => {

225
src/pages/my-growth.vue Normal file
View File

@@ -0,0 +1,225 @@
<script setup>
import Container from "@/components/Container.vue";
import Avatar from "@/components/Avatar.vue";
import { ref } from "vue";
const selectedIndex = ref(0);
const handleSelect = (index) => {
selectedIndex.value = index;
};
</script>
<template>
<Container title="我的成长脚印">
<view class="tabs">
<view
v-for="(rankType, index) in ['排位赛', '好友约战', '射馆练习']"
:key="index"
:style="{
color: index === selectedIndex ? '#000' : '#fff',
backgroundColor: index === selectedIndex ? '#FFD947' : 'transparent',
}"
@tap="handleSelect(index)"
>
{{ rankType }}
</view>
</view>
<view class="contents">
<view
:style="{
display: selectedIndex !== 2 ? 'flex' : 'none',
}"
>
<view>
<view class="contest-header">
<text>1V1</text>
<text>2025.01.21 14:09:23</text>
<image src="../static/back.png" mode="widthFix" />
</view>
<view class="contest-1v1">
<view class="player">
<Avatar frame src="../static/avatar.png" />
<text>选手1</text>
<image src="../static/winner-badge.png" mode="widthFix" />
</view>
<view class="player">
<Avatar frame src="../static/avatar.png" />
<text>选手2</text>
</view>
</view>
</view>
<view>
<view class="contest-header">
<text>1V1</text>
<text>2025.01.21 14:09:23</text>
<image src="../static/back.png" mode="widthFix" />
</view>
<view class="contest-1v1">
<view class="player">
<Avatar frame src="../static/avatar.png" />
<text>选手1</text>
<image src="../static/winner-badge.png" mode="widthFix" />
</view>
<view class="player">
<Avatar frame src="../static/avatar.png" />
<text>选手2</text>
</view>
</view>
</view>
<view>
<view class="contest-header">
<text>5v5</text>
<text>2025.01.21 14:09:23</text>
<image src="../static/back.png" mode="widthFix" />
</view>
<view class="contest-multi">
<view class="player">
<Avatar :rank="1" src="../static/avatar.png" />
<text>选手1</text>
</view>
<view class="player">
<Avatar :rank="2" src="../static/avatar.png" />
<text>选手2</text>
</view>
<view class="player">
<Avatar :rank="3" src="../static/avatar.png" />
<text>选手3</text>
</view>
<view class="player">
<Avatar :rank="4" src="../static/avatar.png" />
<text>选手4</text>
</view>
<view class="player">
<Avatar :rank="5" src="../static/avatar.png" />
<text>选手5</text>
</view>
</view>
</view>
</view>
<view
:style="{
display: selectedIndex === 2 ? 'flex' : 'none',
}"
>
<view
v-for="(_, index) in new Array(6).fill(0)"
:key="index"
class="practice-record"
>
<text>单组练习 2025.01.21 14:09:23</text>
<image src="../static/back.png" mode="widthFix" />
</view>
</view>
</view>
</Container>
</template>
<style scoped>
.tabs {
width: calc(100% - 30px);
display: flex;
justify-content: space-around;
font-size: 15px;
padding: 15px;
padding-top: 0;
}
.tabs > view {
width: 33.3%;
padding: 7px 10px;
text-align: center;
border-radius: 20px;
}
.contents > view {
width: 100%;
display: flex;
flex-direction: column;
}
.contest-header {
display: flex;
justify-content: space-between;
padding: 7px 12px;
font-size: 12px;
background: linear-gradient(180deg, #323845 0%, #2e2e39 100%);
position: relative;
}
.contest-header > text:first-child {
color: #ffd947;
font-size: 14px;
}
.contest-header > text:nth-child(2) {
color: #fff9;
margin-right: 20px;
}
.contest-header > image {
position: absolute;
top: 8px;
right: 10px;
width: 15px;
transform: rotate(180deg);
}
.player {
display: flex;
flex-direction: column;
align-items: center;
color: #fff9;
padding-top: 10px;
padding-bottom: 5px;
position: relative;
}
.player > text {
margin-top: 5px;
}
.player > image:last-child {
position: absolute;
width: 60px;
right: 0;
bottom: 0;
}
.contest-1v1,
.contest-multi {
display: flex;
justify-content: space-around;
margin-bottom: 10px;
}
.contest-1v1 > view {
width: 50%;
}
.contest-1v1 > view:first-child {
background-color: #364469;
}
.contest-1v1 > view:last-child {
background-color: #692735;
}
.contest-multi > view {
width: 20%;
}
.contest-multi > view:nth-child(1) {
background-color: #364469;
}
.contest-multi > view:nth-child(2) {
background-color: #692735;
}
.contest-multi > view:nth-child(3) {
background-color: #934b4b;
}
.contest-multi > view:nth-child(4) {
background-color: #a98b69;
}
.contest-multi > view:nth-child(5) {
background-color: #8268a2;
}
.practice-record {
color: #fff9;
border-bottom: 1px solid #fff9;
padding: 15px;
display: flex;
align-items: center;
justify-content: space-between;
}
.practice-record > image {
width: 15px;
transform: rotate(180deg);
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

After

Width:  |  Height:  |  Size: 266 KiB

BIN
src/static/winner-badge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB