积分表UI修改

This commit is contained in:
kron
2025-09-24 21:05:06 +08:00
parent 59016fe54f
commit 94edc3d6c9
20 changed files with 483 additions and 92 deletions

View File

@@ -484,3 +484,7 @@ export const chooseTeamAPI = async (number, group) => {
export const getVIPDescAPI = async () => {
return request("GET", "/index/memberVipDescribe");
};
export const getPointBookStatisticsAPI = async () => {
return request("GET", `/v2/user/score/sheet/statistics`);
};

View File

@@ -1,15 +1,4 @@
<script setup>
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const props = defineProps({
signin: {
type: Function,
default: () => {},
},
});
const tabs = [
{ image: "../static/tab-vip.png" },
@@ -18,7 +7,6 @@ const tabs = [
];
function handleTabClick(index) {
if (index === 1 && !user.value.id) return props.signin();
if (index === 0) {
uni.navigateTo({
url: "/pages/be-vip",
@@ -26,7 +14,7 @@ function handleTabClick(index) {
}
if (index === 1) {
uni.navigateTo({
url: "/pages/point-book-create",
url: "/pages/point-book",
});
}
if (index === 2) {

View File

@@ -1,5 +1,5 @@
<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue";
import { ref, onMounted } from "vue";
import { onShow } from "@dcloudio/uni-app";
import AppBackground from "@/components/AppBackground.vue";
import Header from "@/components/Header.vue";
@@ -60,12 +60,6 @@ onMounted(() => {
capsuleHeight.value = menuBtnInfo.top - 9;
});
onBeforeUnmount(() => {
// const pages = getCurrentPages();
// const currentPage = pages[pages.length - 1];
// uni.setStorageSync("last-route", currentPage.route);
});
onShow(() => {
uni.$showHint = showGlobalHint;
uni.$hideHint = hideGlobalHint;

View File

@@ -1,6 +1,17 @@
<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue";
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import HeaderProgress from "@/components/HeaderProgress.vue";
import Avatar from "@/components/Avatar.vue";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const currentPage = computed(() => {
const pages = getCurrentPages();
return pages[pages.length - 1].route;
});
const props = defineProps({
title: {
@@ -32,6 +43,18 @@ const onClick = () => {
}
};
const toUserPage = () => {
uni.navigateTo({
url: "/pages/user",
});
};
const signin = () => {
if (!user.value.id) {
uni.$emit("point-book-signin");
}
};
const loading = ref(false);
const showLoader = ref(false);
const pointBook = ref(null);
@@ -73,6 +96,26 @@ onBeforeUnmount(() => {
/>
</view>
<view :style="{ color: whiteBackArrow ? '#fff' : '#000' }">
<view
v-if="currentPage === 'pages/point-book'"
class="user-header"
@click="signin"
>
<block v-if="user.id">
<Avatar
:rankLvl="user.rankLvl"
:src="user.avatar"
:onClick="toUserPage"
:size="40"
borderColor="#333"
/>
<text>{{ user.nickName }}</text>
</block>
<block v-else>
<image src="../static/user-icon.png" mode="widthFix" />
<text>新来的弓箭手你好呀~</text>
</block>
</view>
<block
v-if="
'-凹造型-感知距离-小试牛刀'.indexOf(title) === -1 ||
@@ -200,4 +243,21 @@ onBeforeUnmount(() => {
display: flex;
justify-content: center;
}
.user-header {
display: flex;
align-items: center;
justify-content: flex-start;
}
.user-header > image {
width: 40px;
height: 40px;
border-radius: 50%;
border: 2rpx solid #333;
}
.user-header > text:nth-child(2) {
font-weight: 500;
font-size: 30rpx;
color: #333333;
margin: 0 20rpx;
}
</style>

View File

@@ -100,8 +100,6 @@ async function onReceiveMessage(messages = []) {
audioManager.play("决金箭轮");
tips.value = "即将开始...";
currentRoundEnded.value = false;
} else if (msg.constructor === MESSAGETYPES.ShootSyncMePracticeID) {
ended.value = true;
} else if (msg.constructor === MESSAGETYPES.MatchOver) {
ended.value = true;
} else if (msg.constructor === MESSAGETYPES.BackToGame) {

View File

@@ -10,6 +10,12 @@ const props = defineProps({
const bowOptions = ref({});
const targetOptions = ref({});
const toDetailPage = () => {
uni.navigateTo({
url: `/pages/point-book-detail?id=${props.data.id}`,
});
};
onMounted(() => {
const result = uni.getStorageSync("point-book-config");
(result.bowOption || []).forEach((item) => {
@@ -22,9 +28,10 @@ onMounted(() => {
</script>
<template>
<view class="container">
<view class="container" @click="toDetailPage">
<view>
<view class="labels">
<view></view>
<text>{{
bowOptions[data.bowType] ? bowOptions[data.bowType].name : ""
}}</text>
@@ -38,10 +45,15 @@ onMounted(() => {
<view>
<text>{{ data.createAt }}</text>
</view>
<view>
<text>黄心率{{ data.yellowRate * 100 }}%</text>
<text>10环数{{ data.tenRings }}</text>
<text>平均{{ data.averageRing }}</text>
</view>
</view>
<view>
<image src="../static/bow-target.png" mode="widthFix" />
<view class="aroow-amount">
<view class="arrow-amount">
<text></text>
<text>{{ data.arrows * data.groups }}</text>
<text></text>
@@ -53,38 +65,54 @@ onMounted(() => {
<style scoped>
.container {
background-color: #fff;
border-radius: 15px;
display: flex;
margin-bottom: 15px;
height: 24vw;
align-items: center;
border-radius: 25rpx;
margin-bottom: 25rpx;
height: 200rpx;
border: 1rpx solid #fed847;
}
.container > view {
position: relative;
margin-left: 15px;
}
.container > view:first-child {
width: calc(100% - 5vw);
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
height: calc(100% - 50rpx);
color: #333333;
}
.container > view:first-child > view {
width: 100%;
height: 50%;
display: flex;
position: relative;
}
.container > view:first-child > view:nth-child(3) {
display: flex;
align-items: center;
font-size: 20rpx;
color: #666;
}
.container > view:first-child > view:last-child {
font-weight: 500;
color: #000;
.container > view:first-child > view:nth-child(3) > text {
margin-right: 10rpx;
}
.labels {
align-items: flex-end !important;
}
.labels > view:first-child {
position: absolute;
bottom: 0;
height: 10rpx;
background: #fee947;
border-radius: 5rpx;
width: 300rpx;
}
.labels > text {
font-size: 12px;
color: #333333;
border: 1px solid #eee;
font-size: 26rpx;
margin-right: 10px;
border-radius: 10px;
padding: 5px 10px;
position: relative;
}
.container > view:last-child {
margin-right: 1vw;
@@ -92,7 +120,7 @@ onMounted(() => {
.container > view:last-child > image {
width: 24vw;
}
.aroow-amount {
.arrow-amount {
position: absolute;
background-color: #0009;
border-radius: 10px;
@@ -105,7 +133,7 @@ onMounted(() => {
top: calc(50% - 11px);
left: calc(50% - 30px);
}
.aroow-amount > text:nth-child(2) {
.arrow-amount > text:nth-child(2) {
color: #fff;
font-size: 14px;
margin: 0 3px;

View File

@@ -5,6 +5,10 @@ const props = defineProps({
type: Boolean,
default: false,
},
height: {
type: String,
default: "260px",
},
onClose: {
type: Function,
default: () => {},
@@ -46,7 +50,7 @@ watch(
class="modal-content"
:style="{
transform: `translateY(${showContent ? '0%' : '100%'})`,
height: !noBg ? '260px' : 'auto',
height,
}"
@click.stop=""
>
@@ -55,7 +59,7 @@ watch(
src="https://static.shelingxingqiu.com/attachment/2025-08-05/dbuaf19pf7qd8ps0uh.png"
mode="widthFix"
/>
<view class="close-btn" @click="onClose">
<view class="close-btn" @click="onClose" v-if="!noBg">
<image src="../static/close-yellow.png" mode="widthFix" />
</view>
<slot></slot>

View File

@@ -157,8 +157,6 @@ async function onReceiveMessage(messages = []) {
audioManager.play("比赛结束");
} else if (msg.constructor === MESSAGETYPES.FinalShoot) {
audioManager.play("决金箭轮");
} else if (msg.constructor === MESSAGETYPES.ShootSyncMePracticeID) {
ended.value = true;
} else if (msg.constructor === MESSAGETYPES.MatchOver) {
ended.value = true;
}

View File

@@ -8,6 +8,10 @@ import useStore from "@/store";
const store = useStore();
const { updateUser, updateDevice } = store;
const props = defineProps({
noBg: {
type: Boolean,
default: false,
},
onClose: {
type: Function,
default: () => {},
@@ -106,9 +110,9 @@ onShow(() => {
</script>
<template>
<view class="container">
<view class="avatar">
<text>头像:</text>
<view class="container" :style="{ background: noBg ? '#fff' : 'none' }">
<view class="avatar" :style="{ borderColor: noBg ? '#E3E3E3' : '#fff' }">
<text :style="{ color: noBg ? '#666' : '#fff' }">头像:</text>
<button
open-type="chooseAvatar"
@chooseavatar="onChooseAvatar"
@@ -116,17 +120,19 @@ onShow(() => {
hover-class="none"
>
<Avatar v-if="avatarUrl" :src="avatarUrl" :size="30" />
<text v-else>点击获取</text>
<text v-else :style="{ color: noBg ? '#666' : '#fff9' }">点击获取</text>
<image src="../static/enter.png" mode="widthFix" />
</button>
</view>
<view class="nickname">
<text>昵称:</text>
<view class="nickname" :style="{ borderColor: noBg ? '#E3E3E3' : '#fff' }">
<text :style="{ color: noBg ? '#666' : '#fff' }">昵称:</text>
<input
type="nickname"
placeholder="请输入昵称"
placeholder-style="color: #fff9"
:placeholder-style="{ color: noBg ? '#666' : '#fff9' }"
@change="onNicknameChange"
@blur="onNicknameBlur"
:style="{ color: noBg ? '#333' : '#fff' }"
/>
</view>
<SButton :rounded="20" width="80vw" :onClick="handleLogin">
@@ -147,13 +153,21 @@ onShow(() => {
</block>
</SButton>
<view class="protocol" @click="handleAgree">
<view v-if="!agree" />
<view v-if="!agree" :style="{ borderColor: noBg ? '#E3E3E3' : '#fff' }" />
<image v-if="agree" src="../static/checked.png" mode="widthFix" />
<view>
<text>已同意并阅读</text>
<view @click.stop="openServiceLink">用户协议</view>
<view
@click.stop="openServiceLink"
:style="{ color: noBg ? '#333' : '#fff' }"
>用户协议</view
>
<text></text>
<view @click.stop="openPrivacyLink">隐私协议</view>
<view
@click.stop="openPrivacyLink"
:style="{ color: noBg ? '#333' : '#fff' }"
>隐私协议</view
>
<text>内容</text>
</view>
</view>
@@ -168,6 +182,8 @@ onShow(() => {
flex-direction: column;
justify-content: center;
align-items: center;
border-top-left-radius: 24rpx;
border-top-right-radius: 24rpx;
}
.avatar,
.nickname {
@@ -175,7 +191,7 @@ onShow(() => {
display: flex;
align-items: center;
margin-bottom: 20px;
border-bottom: 1px solid #fff3;
border-bottom: 1rpx solid #fff3;
}
.avatar {
margin: 0;
@@ -183,7 +199,6 @@ onShow(() => {
.avatar > text,
.nickname > text {
width: 20%;
color: #fff9;
font-size: 14px;
line-height: 55px;
}
@@ -194,7 +209,6 @@ onShow(() => {
.nickname > input {
flex: 1;
font-size: 14px;
color: #fff;
line-height: 55px;
}
.wechat-icon {
@@ -226,13 +240,16 @@ onShow(() => {
display: flex;
align-items: center;
}
.protocol > view:last-child > view {
color: #fff;
}
.login-btn {
line-height: 55px;
width: 80%;
display: flex;
align-items: center;
justify-content: space-between;
}
.login-btn > image {
width: 28rpx;
height: 28rpx;
}
.loading {
width: 25px;

View File

@@ -3,6 +3,9 @@
{
"path": "pages/index"
},
{
"path": "pages/point-book"
},
{
"path": "pages/about-us"
},

View File

@@ -94,7 +94,6 @@ onShow(async () => {
});
onMounted(async () => {
uni.removeStorageSync("point-book-config");
const config = await getAppConfig();
updateConfig(config);
console.log("全局配置:", config);
@@ -289,7 +288,7 @@ onShareTimeline(() => {
<Signin :onClose="() => (showModal = false)" />
</SModal>
</view>
<AppFooter :signin="() => (showModal = true)" />
<AppFooter />
</Container>
</template>

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted } from "vue";
import { onShow, onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
import { onShow } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import EditOption from "@/components/EditOption.vue";
import SButton from "@/components/SButton.vue";
@@ -75,23 +75,6 @@ onMounted(async () => {
expandIndex.value = 3;
}
});
onShareAppMessage(() => {
return {
title: "高效记录每一次射箭,深度分析助你提升!",
path: "pages/point-book-create",
imageUrl:
"https://static.shelingxingqiu.com/attachment/2025-09-22/dcz4m4nbgycqqwknrv.png",
};
});
onShareTimeline(() => {
return {
title: "高效记录每一次射箭,深度分析助你提升!",
query: "from=timeline",
imageUrl:
"https://static.shelingxingqiu.com/attachment/2025-09-22/dcz4m4nbgycqqwknrv.png",
};
});
</script>
<template>

View File

@@ -34,7 +34,9 @@ const onSelect = (index) => {
};
const goBack = () => {
uni.navigateBack();
uni.navigateBack({
delta: 2,
});
};
onLoad(async (options) => {
@@ -57,7 +59,13 @@ onLoad(async (options) => {
</script>
<template>
<Container :bgType="2" bgColor="#F5F5F5" :whiteBackArrow="false" title="分析">
<Container
:bgType="2"
bgColor="#F5F5F5"
:whiteBackArrow="false"
title="分析"
:onBack="goBack"
>
<view class="container">
<view class="tab-bar">
<view

View File

@@ -45,11 +45,6 @@ const onSelectOption = (itemIndex, value) => {
showModal.value = false;
onListLoading(1);
};
const toDetailPage = (id) => {
uni.navigateTo({
url: `/pages/point-book-detail?id=${id}`,
});
};
</script>
<template>
@@ -82,11 +77,7 @@ const toDetailPage = (id) => {
</view>
<view class="point-records">
<ScrollList :onLoading="onListLoading">
<view
v-for="(item, index) in list"
:key="index"
@click="() => toDetailPage(item.id)"
>
<view v-for="(item, index) in list" :key="index">
<PointRecord :data="item" />
</view>
<view class="no-data" v-if="list.length === 0">暂无数据</view>
@@ -95,6 +86,7 @@ const toDetailPage = (id) => {
<SModal
:show="showModal"
:noBg="true"
height="auto"
:onClose="() => (showModal = false)"
>
<view class="selector">

315
src/pages/point-book.vue Normal file
View File

@@ -0,0 +1,315 @@
<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { onShow, onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import PointRecord from "@/components/PointRecord.vue";
import SModal from "@/components/SModal.vue";
import Signin from "@/components/Signin.vue";
import {
getHomeData,
getPointBookConfigAPI,
getPointBookListAPI,
getPointBookStatisticsAPI,
} from "@/apis";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { updateUser } = store;
const { user } = storeToRefs(store);
const isIOS = computed(() => {
const systemInfo = uni.getDeviceInfo();
return systemInfo.osName === "ios";
});
const showModal = ref(false);
const data = ref({
weeksCheckIn: [],
});
const list = ref([]);
const toListPage = () => {
uni.navigateTo({
url: "/pages/point-book-list",
});
};
onShow(async () => {
const result = await getPointBookListAPI(1);
list.value = result;
const result2 = await getPointBookStatisticsAPI();
data.value = result2;
});
const onSignin = () => {
showModal.value = true;
};
const startScoring = () => {
if (user.value.id) {
uni.navigateTo({
url: "/pages/point-book-create",
});
} else {
showModal.value = true;
}
};
onMounted(async () => {
uni.$on("point-book-signin", onSignin);
const token = uni.getStorageSync(
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
);
if (!user.value.id && token) {
const data = await getHomeData();
if (data.user) updateUser(data.user);
}
const config = await getPointBookConfigAPI();
uni.setStorageSync("point-book-config", config);
});
onBeforeUnmount(() => {
uni.$off("point-book-signin", onSignin);
});
onShareAppMessage(() => {
return {
title: "高效记录每一次射箭,深度分析助你提升!",
path: "pages/point-book-create",
imageUrl:
"https://static.shelingxingqiu.com/attachment/2025-09-22/dcz4m4nbgycqqwknrv.png",
};
});
onShareTimeline(() => {
return {
title: "高效记录每一次射箭,深度分析助你提升!",
query: "from=timeline",
imageUrl:
"https://static.shelingxingqiu.com/attachment/2025-09-22/dcz4m4nbgycqqwknrv.png",
};
});
</script>
<template>
<Container :bgType="2" bgColor="#F5F5F5" :whiteBackArrow="false" title="">
<view class="container">
<view class="daily-signin">
<view>
<image src="../static/week-check.png" mode="widthFix" />
</view>
<view>
<image
v-if="data.weeksCheckIn[0]"
src="../static/checked-green2.png"
mode="widthFix"
/>
<view v-else></view>
<text>周一</text>
</view>
<view>
<image
v-if="data.weeksCheckIn[1]"
src="../static/checked-green2.png"
mode="widthFix"
/>
<view v-else></view>
<text>周二</text>
</view>
<view>
<image
v-if="data.weeksCheckIn[2]"
src="../static/checked-green2.png"
mode="widthFix"
/>
<view v-else></view>
<text>周三</text>
</view>
<view>
<image
v-if="data.weeksCheckIn[3]"
src="../static/checked-green2.png"
mode="widthFix"
/>
<view v-else></view>
<text>周四</text>
</view>
<view>
<image
v-if="data.weeksCheckIn[4]"
src="../static/checked-green2.png"
mode="widthFix"
/>
<view v-else></view>
<text>周五</text>
</view>
<view>
<image
v-if="data.weeksCheckIn[5]"
src="../static/checked-green2.png"
mode="widthFix"
/>
<view v-else></view>
<text>周六</text>
</view>
<view>
<image
v-if="data.weeksCheckIn[6]"
src="../static/checked-green2.png"
mode="widthFix"
/>
<view v-else></view>
<text>周日</text>
</view>
</view>
<view class="statistics">
<view>
<text>{{ data.todayTotalArrow }}</text>
<text>今日射箭</text>
</view>
<view>
<text>{{ data.totalArrow }}</text>
<text>累计射箭</text>
</view>
<view>
<text>{{ data.totalDay }}</text>
<text>已训练天数</text>
</view>
<view>
<text>{{ data.averageRing }}</text>
<text>平均环数</text>
</view>
<view>
<text>{{ data.yellowRate * 100 }}</text>
<text>黄心率%</text>
</view>
<view>
<button hover-class="none" @click="startScoring">
<image src="../static/start-scoring.png" mode="widthFix" />
</button>
</view>
</view>
<view class="title">
<image src="../static/point-book-title1.png" mode="widthFix" />
</view>
<view class="title">
<image src="../static/point-book-title2.png" mode="widthFix" />
</view>
<block v-for="(item, index) in list" :key="index">
<PointRecord :data="item" />
</block>
<view
class="see-more"
@click="toListPage"
v-if="list.length"
:style="{ marginBottom: isIOS ? '10rpx' : 0 }"
>
<text>查看所有记录</text>
<image src="../static/enter-arrow-blue.png" mode="widthFix" />
</view>
</view>
<SModal :show="showModal" :onClose="() => (showModal = false)" :noBg="true">
<Signin :onClose="() => (showModal = false)" :noBg="true" />
</SModal>
</Container>
</template>
<style scoped>
.container {
width: calc(100% - 50rpx);
padding: 25rpx;
}
.statistics {
border-radius: 25rpx;
border-bottom-left-radius: 50rpx;
border-bottom-right-radius: 50rpx;
border: 1rpx solid #fed847;
background: #fff;
font-size: 22rpx;
display: flex;
flex-wrap: wrap;
padding: 25rpx;
margin-bottom: 25rpx;
}
.statistics > view {
width: 33.33%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.statistics > view:nth-child(-n + 3) {
margin-bottom: 25rpx;
}
.statistics > view:nth-child(2),
.statistics > view:nth-child(5) {
border-left: 1rpx solid #eeeeee;
border-right: 1rpx solid #eeeeee;
box-sizing: border-box;
}
.statistics > view > text {
text-align: center;
font-size: 22rpx;
color: #333333;
}
.statistics > view > text:first-child {
font-weight: 500;
font-size: 40rpx;
margin-bottom: 10rpx;
}
.statistics > view:last-child > button > image {
width: 164rpx;
}
.daily-signin {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 12rpx;
border-radius: 20rpx;
margin-bottom: 25rpx;
}
.daily-signin > view {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 12rpx;
box-sizing: border-box;
}
.daily-signin > view:not(:first-child) {
background: #f8f8f8;
padding: 15rpx 8rpx;
}
.daily-signin > view:not(:first-child) > image {
width: 32rpx;
height: 32rpx;
}
.daily-signin > view:not(:first-child) > view {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
box-sizing: border-box;
border: 1rpx solid #333;
}
.daily-signin > view > text {
font-size: 24rpx;
/* color: #333; */
color: #999999;
font-weight: 500;
text-align: center;
margin-top: 12rpx;
}
.daily-signin > view:first-child > image {
width: 100%;
}
.title {
width: 100%;
display: flex;
justify-content: center;
margin-bottom: 20rpx;
}
.title > image {
width: 566rpx;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/static/week-check.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB