完成ios首页改造

This commit is contained in:
kron
2025-10-27 13:56:27 +08:00
parent a9168201b3
commit 14f43e929f
15 changed files with 454 additions and 251 deletions

View File

@@ -27,7 +27,7 @@ const props = defineProps({
default: "",
},
});
const itemTexts = ["选择弓种", "选择练习距离", "选择靶纸", "选择组/箭数"];
const itemTexts = ["Bow Type", "Distance", "Target Type", "Sets/Arrows"];
const distances = [5, 8, 10, 18, 25, 30, 50, 60, 70];
const groupArrows = [3, 6, 12, 18];
@@ -136,21 +136,21 @@ onMounted(async () => {
>
<view @click="() => onExpand(itemIndex, !expand)">
<text :style="{ opacity: expand ? 1 : 0 }">{{
itemIndex !== 3 ? itemTexts[itemIndex] : "选择组"
itemIndex !== 3 ? itemTexts[itemIndex] : "Select Sets"
}}</text>
<block>
<text :style="{ opacity: expand ? 0 : 1 }" v-if="itemIndex === 0">{{
value || itemTexts[itemIndex]
}}</text>
<text :style="{ opacity: expand ? 0 : 1 }" v-if="itemIndex === 1">{{
value && value > 0 ? value + "" : itemTexts[itemIndex]
value && value > 0 ? value + "m" : itemTexts[itemIndex]
}}</text>
<text :style="{ opacity: expand ? 0 : 1 }" v-if="itemIndex === 2">{{
value || itemTexts[itemIndex]
}}</text>
<text :style="{ opacity: expand ? 0 : 1 }" v-if="itemIndex === 3">{{
selectedIndex !== -1 && secondSelectIndex !== -1
? `${selectedIndex}/${groupArrows[secondSelectIndex]}`
? `${selectedIndex}sets/${groupArrows[secondSelectIndex]}arrows`
: itemTexts[itemIndex]
}}</text>
</block>
@@ -186,7 +186,7 @@ onMounted(async () => {
@click="onSelectItem(index)"
>
<text>{{ item }}</text>
<text></text>
<text>m</text>
</view>
<view
:style="{
@@ -195,12 +195,12 @@ onMounted(async () => {
>
<input
v-model="meter"
placeholder="自定义"
placeholder="custom"
placeholder-style="color: #DDDDDD"
@focus="() => (selectedIndex = 9)"
@change="onMeterChange"
/>
<text></text>
<text>m</text>
</view>
</view>
<view v-if="itemIndex === 2" class="bowtarget-items">
@@ -227,12 +227,12 @@ onMounted(async () => {
@click="onSelectItem(i)"
>
<text>{{ i }}</text>
<text></text>
<text>sets</text>
</view>
</view>
<view
:style="{ marginTop: '5px', marginBottom: '10px', color: '#999999' }"
>选择每组的箭数</view
>Select arrows per set</view
>
<view class="amount-items">
<view
@@ -244,7 +244,7 @@ onMounted(async () => {
@click="onSelectSecondItem(index)"
>
<text>{{ item }}</text>
<text></text>
<text>arrows</text>
</view>
</view>
</view>

View File

@@ -68,7 +68,6 @@ onMounted(() => {
display: flex;
align-items: center;
border-radius: 25rpx;
margin-bottom: 25rpx;
height: 200rpx;
border: 2rpx solid #fed848;
}

View File

@@ -4,7 +4,7 @@ import { ref, computed } from "vue";
const props = defineProps({
data: {
type: Object,
default: () => ({}),
default: Array,
},
total: {
type: Number,
@@ -69,6 +69,7 @@ const ringText = (ring) => {
display: flex;
flex-direction: column;
justify-content: flex-end;
margin-top: -30rpx;
}
.container > view {
padding: 0 10rpx;

View File

@@ -59,6 +59,7 @@ onShow(async () => {
<scroll-view
class="scroll-list"
scroll-y
enable-flex="true"
:show-scrollbar="false"
enhanced="true"
:bounces="false"
@@ -73,8 +74,8 @@ onShow(async () => {
>
<slot></slot>
<view class="tips">
<text v-if="loading">加载中...</text>
<text v-if="noMore">{{ count === 0 ? "暂无数据" : "没有更多了" }}</text>
<text v-if="loading">Loading...</text>
<text v-if="noMore">{{ count === 0 ? "No data" : "Thats all" }}</text>
</view>
</scroll-view>
</template>
@@ -83,9 +84,7 @@ onShow(async () => {
.scroll-list {
width: 100%;
height: 100%;
}
.tips {
height: 50rpx;
flex-direction: column;
}
.tips > text {
color: #d0d0d0;

View File

@@ -1,7 +1,6 @@
<script setup>
import { ref, computed, onMounted, watch } from "vue";
import { onShow } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import PointRecord from "@/components/PointRecord.vue";
import RingBarChart from "@/components/RingBarChart.vue";
@@ -22,6 +21,7 @@ const isIOS = computed(() => {
const loadImage = ref(false);
const data = ref({
weeksCheckIn: [],
ringRate: [],
});
const bowTargetSrc = ref("");
@@ -48,6 +48,7 @@ const loadData = async () => {
else if (result.checkInCount >= 5) hot = 3;
else if (result.checkInCount === 7) hot = 4;
uni.$emit("update-hot", hot);
return;
loadImage.value = true;
const generateHeatmapAsync = async () => {
const weekArrows = result.weekArrows
@@ -115,7 +116,6 @@ onShow(async () => {
</script>
<template>
<Container :bgType="4" bgColor="#F5F5F5" :whiteBackArrow="false" title="">
<view class="container">
<view class="daily-signin">
<view>
@@ -128,7 +128,7 @@ onShow(async () => {
mode="widthFix"
/>
<view v-else></view>
<text>周一</text>
<text>Mon</text>
</view>
<view :class="data.weeksCheckIn[1] ? 'checked' : ''">
<image
@@ -137,7 +137,7 @@ onShow(async () => {
mode="widthFix"
/>
<view v-else></view>
<text>周二</text>
<text>Tue</text>
</view>
<view :class="data.weeksCheckIn[2] ? 'checked' : ''">
<image
@@ -146,7 +146,7 @@ onShow(async () => {
mode="widthFix"
/>
<view v-else></view>
<text>周三</text>
<text>Wed</text>
</view>
<view :class="data.weeksCheckIn[3] ? 'checked' : ''">
<image
@@ -155,7 +155,7 @@ onShow(async () => {
mode="widthFix"
/>
<view v-else></view>
<text>周四</text>
<text>Thu</text>
</view>
<view :class="data.weeksCheckIn[4] ? 'checked' : ''">
<image
@@ -164,7 +164,7 @@ onShow(async () => {
mode="widthFix"
/>
<view v-else></view>
<text>周五</text>
<text>Fri</text>
</view>
<view :class="data.weeksCheckIn[5] ? 'checked' : ''">
<image
@@ -173,7 +173,7 @@ onShow(async () => {
mode="widthFix"
/>
<view v-else></view>
<text>周六</text>
<text>Sat</text>
</view>
<view :class="data.weeksCheckIn[6] ? 'checked' : ''">
<image
@@ -182,25 +182,25 @@ onShow(async () => {
mode="widthFix"
/>
<view v-else></view>
<text>周日</text>
<text>Sun</text>
</view>
</view>
<view class="statistics">
<view>
<text>{{ data.todayTotalArrow || "-" }}</text>
<text>今日射箭()</text>
<text>Today's Arrows</text>
</view>
<view>
<text>{{ data.totalArrow || "-" }}</text>
<text>累计射箭()</text>
<text>Total Arrows</text>
</view>
<view>
<text>{{ data.totalDay || "-" }}</text>
<text>已训练天数()</text>
<text>Training Days</text>
</view>
<view>
<text>{{ data.averageRing || "-" }}</text>
<text>平均环数()</text>
<text>Avg Score</text>
</view>
<view>
<text>{{
@@ -208,7 +208,7 @@ onShow(async () => {
? Number((data.yellowRate * 100).toFixed(2)) + "%"
: "-"
}}</text>
<text>黄心率</text>
<text>X-Ring Rate</text>
</view>
<view>
<button hover-class="none" @click="startScoring">
@@ -224,13 +224,9 @@ onShow(async () => {
:src="bowTargetSrc || '../static/bow-target.png'"
mode="widthFix"
/>
<image
v-if="heatMapImageSrc"
:src="heatMapImageSrc"
mode="aspectFill"
/>
<image v-if="heatMapImageSrc" :src="heatMapImageSrc" mode="aspectFill" />
<view v-if="loadImage" class="load-image">
<text>生成中...</text>
<text>Generating...</text>
</view>
<canvas
id="heatMapCanvas"
@@ -246,15 +242,16 @@ onShow(async () => {
"
/>
</view>
<RingBarChart :data="data.ringRate" v-if="user.id" />
<RingBarChart :data="data.ringRate" />
<view :style="{ height: '25rpx' }" />
</view>
</Container>
</template>
<style scoped>
.container {
width: calc(100% - 50rpx);
padding: 25rpx;
width: 100%;
height: 100%;
overflow: auto;
}
.statistics {
border-radius: 25rpx;

View File

@@ -1,6 +1,5 @@
<script setup>
import { ref, onMounted } from "vue";
import Container from "@/components/Container.vue";
import SModal from "@/components/SModal.vue";
import EditOption from "@/components/EditOption.vue";
import PointRecord from "@/components/PointRecord.vue";
@@ -48,29 +47,23 @@ const onSelectOption = (itemIndex, value) => {
</script>
<template>
<Container
:bgType="2"
bgColor="#F5F5F5"
:whiteBackArrow="false"
title="计分记录"
>
<view class="container">
<view class="selectors">
<view @click="() => openSelector(0)">
<text :style="{ color: bowType.name ? '#000' : '#999' }">{{
bowType.name || "请选择"
bowType.name || "Bow Type"
}}</text>
<image src="../static/arrow-grey.png" mode="widthFix" />
</view>
<view @click="() => openSelector(1)">
<text :style="{ color: distance ? '#000' : '#999' }">{{
distance ? distance + " " : "请选择"
distance ? distance + " m" : "Distance"
}}</text>
<image src="../static/arrow-grey.png" mode="widthFix" />
</view>
<view @click="() => openSelector(2)">
<text :style="{ color: bowtargetType.name ? '#000' : '#999' }">{{
bowtargetType.name || "请选择"
bowtargetType.name || "Target Type"
}}</text>
<image src="../static/arrow-grey.png" mode="widthFix" />
</view>
@@ -78,9 +71,9 @@ const onSelectOption = (itemIndex, value) => {
<view class="point-records">
<ScrollList :onLoading="onListLoading">
<view v-for="(item, index) in list" :key="index">
<view v-if="index > 0" :style="{ height: '25rpx' }" />
<PointRecord :data="item" />
</view>
<view class="no-data" v-if="list.length === 0">暂无数据</view>
</ScrollList>
</view>
<SModal
@@ -120,7 +113,6 @@ const onSelectOption = (itemIndex, value) => {
</view>
</SModal>
</view>
</Container>
</template>
<style scoped>
@@ -132,15 +124,13 @@ const onSelectOption = (itemIndex, value) => {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
margin-top: 10px;
}
.selectors > view {
height: 110rpx;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
height: 55px;
border-radius: 12px;
color: #333;
font-size: 13px;
@@ -150,7 +140,7 @@ const onSelectOption = (itemIndex, value) => {
width: 34vw;
}
.selectors > view > text {
width: calc(100% - 11vw);
width: 100%;
text-align: center;
margin-left: 3vw;
}
@@ -173,17 +163,8 @@ const onSelectOption = (itemIndex, value) => {
width: 40px;
}
.point-records {
margin: 0 15px;
margin-top: 10px;
height: calc(100% - 80px);
}
.no-data {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
color: #999999;
font-size: 14px;
height: calc(100% - 130rpx);
overflow: auto;
}
</style>

View File

@@ -0,0 +1,114 @@
<script setup>
import { ref } from "vue";
import Avatar from "@/components/Avatar.vue";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const toEditPage = () => {
uni.navigateTo({
url: "/pages/point-book-edit",
});
};
</script>
<template>
<view class="container">
<view class="header">
<image :src="user.avatar" mode="widthFix" />
<button hover-class="none" @click="toEditPage">
<image src="../static/pen-yellow.png" mode="widthFix" />
</button>
</view>
<view class="body">
<view>
<button hover-class="none">
<image src="../static/user-yellow.png" mode="widthFix" />
<text>Name</text>
<image src="../static/back-grey.png" mode="widthFix" />
</button>
<button hover-class="none">
<image src="../static/email-yellow.png" mode="widthFix" />
<text>Email</text>
<image src="../static/back-grey.png" mode="widthFix" />
</button>
<button hover-class="none">
<image src="../static/password-yellow.png" mode="widthFix" />
<text>Password</text>
<image src="../static/back-grey.png" mode="widthFix" />
</button>
</view>
<button hover-class="none">Log out</button>
</view>
</view>
</template>
<style scoped lang="scss">
.container {
display: flex;
flex-direction: column;
align-items: center;
}
.header {
position: relative;
margin-top: -120rpx;
}
.header > image {
width: 180rpx;
height: 180rpx;
border-radius: 50%;
border: 4rpx solid #fff;
}
.header > button {
position: absolute;
right: 0;
bottom: 0;
}
.header > button > image {
width: 60rpx;
height: 60rpx;
}
.body {
width: 100%;
margin-top: 20rpx;
}
.body > view {
background-color: $uni-bg-color;
border-radius: 25px;
padding: 0 20px;
}
.body > view > button {
display: flex;
align-items: center;
padding: 20px 0;
}
.body > view > button:not(:last-child) {
border-bottom: 1rpx solid #e3e3e3;
}
.body > view > button > image:first-child {
width: 40rpx;
height: 40rpx;
}
.body > view > button > text {
flex: 1;
font-size: 26rpx;
color: #333333;
text-align: left;
padding-left: 20rpx;
}
.body > view > button > image:last-child {
width: 28rpx;
height: 28rpx;
}
.body > button:last-child {
margin-top: 24rpx;
background: $uni-bg-color;
border-radius: 24rpx;
font-size: 26rpx;
color: $uni-link-color;
text-align: center;
padding: 20px 0;
}
</style>

View File

@@ -1,6 +1,9 @@
<script setup>
import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
import Container from "@/components/Container.vue";
import Home from "@/pages/point-book-home.vue";
import History from "@/pages/point-book-list.vue";
import Profile from "@/pages/point-book-profile.vue";
import Avatar from "@/components/Avatar.vue";
import { getHomeData } from "@/apis";
@@ -11,18 +14,28 @@ const { updateUser } = store;
const { user } = storeToRefs(store);
const activeTab = ref(0);
const heat = ref(0);
const isIOS = computed(() => {
const systemInfo = uni.getDeviceInfo();
return systemInfo.osName === "ios";
});
const onClickTab = (index) => {
if (index > 0 && !user.value.id) {
return uni.navigateTo({
const capsuleHeight = computed(() => {
const menuBtnInfo = uni.getMenuButtonBoundingClientRect();
return menuBtnInfo.top - 9;
});
const goSignIn = () => {
uni.navigateTo({
url: "/pages/sign-in",
});
}
};
const onClickTab = (index) => {
// if (index > 0 && !user.value.id) {
// return goSignIn();
// }
activeTab.value = index;
};
@@ -36,6 +49,10 @@ watch(
}
);
const updateHot = (value) => {
heat.value = value;
};
onMounted(async () => {
const token = uni.getStorageSync(
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
@@ -44,12 +61,55 @@ onMounted(async () => {
const data = await getHomeData();
if (data.user) updateUser(data.user);
}
uni.$on("update-hot", updateHot);
});
onBeforeUnmount(() => {
uni.$off("update-hot", updateHot);
});
</script>
<template>
<Container :bgType="4" bgColor="#F5F5F5" :whiteBackArrow="false" title="">
<view class="container"> </view>
<view class="container">
<view
class="header"
:style="{
paddingTop: capsuleHeight + 'px',
overflow: activeTab === 1 ? 'hidden' : 'unset',
}"
>
<image
class="bg-image"
:src="`../static/app-bg${activeTab === 2 ? 6 : 5}.png`"
mode="widthFix"
/>
<view v-if="activeTab === 0" class="user-header" @click="goSignIn">
<block v-if="user.id">
<Avatar :src="user.avatar" :size="40" borderColor="#333" />
<text class="truncate">{{ user.nickName }}</text>
<image
v-if="heat"
:src="`../static/hot${heat}.png`"
mode="widthFix"
/>
</block>
<block v-else>
<image src="../static/user-icon.png" mode="widthFix" />
<text>新来的弓箭手你好呀~</text>
</block>
</view>
<view v-if="activeTab === 1" class="title">History</view>
</view>
<view
class="body"
:style="{
height: 'calc(100% - 50px - ' + capsuleHeight + 'px - 160rpx - 25rpx)',
overflowY: activeTab === 2 ? 'unset' : 'hidden',
}"
>
<Home v-show="activeTab === 0" />
<History v-show="activeTab === 1" />
<Profile v-show="activeTab === 2" />
</view>
<view class="tabbar">
<button hover-class="none" @click="onClickTab(0)">
<image
@@ -77,14 +137,64 @@ onMounted(async () => {
>
</button>
</view>
</Container>
</view>
</template>
<style scoped>
.container {
width: calc(100% - 50rpx);
height: 100%;
padding: 25rpx;
width: 100vw;
height: 100vh;
background: #f5f5f5;
}
.header {
height: 50px;
position: relative;
font-weight: 500;
font-size: 30rpx;
color: #333333;
display: flex;
align-items: center;
z-index: 1;
}
.title {
position: relative;
width: 100%;
text-align: center;
}
.header > image {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.user-header {
display: flex;
align-items: center;
justify-content: flex-start;
position: relative;
margin-left: 30rpx;
}
.user-header > image:first-child {
width: 40px;
height: 40px;
border-radius: 50%;
border: 2rpx solid #333;
}
.user-header > image:last-child {
width: 36rpx;
height: 36rpx;
}
.user-header > text:nth-child(2) {
font-weight: 500;
font-size: 30rpx;
color: #333333;
margin: 0 20rpx;
max-width: 300rpx;
}
.body {
position: relative;
padding: 25rpx 25rpx 0rpx 25rpx;
z-index: 2;
}
.tabbar {
width: 100%;

BIN
src/static/app-bg6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
src/static/back-grey.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

BIN
src/static/email-yellow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

BIN
src/static/pen-yellow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/static/user-yellow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

View File

@@ -74,3 +74,5 @@ $uni-color-subtitle: #555; // 二级标题颜色
$uni-font-size-subtitle: 18px;
$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 15px;
$uni-link-color: #287fff;