fix:组件类型调整

This commit is contained in:
ctw committed 2025-11-12 10:05:42 +08:00
1 parent 6f92eedbbf
commit a73e9c741a
12 files changed
+852 -822

No files matched your search

+18 -18
View File
@@ -4,25 +4,25 @@
</view>
</template>
<script setup lang="ts">
const list = [
{
letter: "A",
data: ["1", "2", "3", "4", "5", "6"],
},
{
letter: "B",
data: ["1", "2", "3", "4", "5", "6"],
},
{
letter: "C",
data: ["1", "2", "3", "4", "5", "6"],
},
];
<script setup>
const list = [
{
letter: 'A',
data: ['1', '2', '3', '4', '5', '6'],
},
{
letter: 'B',
data: ['1', '2', '3', '4', '5', '6'],
},
{
letter: 'C',
data: ['1', '2', '3', '4', '5', '6'],
},
];
const select = () => {
uni.switchTab({ url: "/pages/home/home" });
};
const select = () => {
uni.switchTab({ url: '/pages/home/home' });
};
</script>
<style scoped lang="scss"></style>
+178 -163
View File
@@ -1,5 +1,13 @@
<template>
<uni-nav-bar dark :fixed="true" :border="false" :height="50" background-color="transparent" left-width="360rpx" right-width="500rpx">
<uni-nav-bar
dark
:fixed="true"
:border="false"
:height="50"
background-color="#222"
left-width="360rpx"
right-width="500rpx"
>
<template v-slot:left>
<uni-icons type="back" size="30" color="#ffcd32" @click="back"></uni-icons>
<view class="nav-user-info" :style="navUserInfoStyle">
@@ -30,19 +38,24 @@
<view class="image" style="background-image: url(http://dummyimage.com/100x100/ac0ee3)">
<view class="info">
<image class="avatar" src="" />
<view class="name">{{ "啊发发" }}</view>
<view class="name">{{ '啊发发' }}</view>
<view class="verification">真人认证</view>
<view class="tags">
<text class="tag">女·23岁</text>
<text class="tag">170cm</text>
</view>
<view class="introduction">{{ "发哈夫哈佛哈维合法i凤凰网你" }}</view>
<view class="introduction">{{ '发哈夫哈佛哈维合法i凤凰网你' }}</view>
</view>
<view class="filter" :style="filterStyle"></view>
</view>
<view class="album">
<view class="title">相册</view>
<image class="photo" v-for="(item, index) in 5" :key="index" src="http://dummyimage.com/100x100/ac0ee3" />
<image
class="photo"
v-for="(item, index) in 5"
:key="index"
src="http://dummyimage.com/100x100/ac0ee3"
/>
</view>
<view class="evaluate">
<view class="title">评价1+</view>
@@ -66,190 +79,192 @@
</view>
</view>
<moment v-for="(item, index) in listData">{{ index }}</moment>
<uni-load-more :status="loadMoreStatus" :content-text="loadMoreContent" @clickLoadMore="onClickLoadMore" />
<uni-load-more
:status="loadMoreStatus"
:content-text="loadMoreContent"
@clickLoadMore="onClickLoadMore"
/>
<view style="height: 100rpx"></view>
</scroll-view>
</template>
<script setup lang="ts">
import { ref, computed } from "vue";
import moment from "@/sheep/components/moment/moment.vue";
import makeFriendApi from "@/sheep/api/make-friend";
import useScroll from "@/sheep/hooks/use-scroll";
<script setup>
import { ref, computed } from 'vue';
import moment from '@/sheep/components/moment/moment.vue';
import makeFriendApi from '@/sheep/api/make-friend';
import useScroll from '@/sheep/hooks/use-scroll';
const {
listData,
refresherTriggered,
loadMoreContent,
loadMoreStatus,
onRefresherRefresh,
onRefresherAbort,
onScrollToLower,
onClickLoadMore,
} = useScroll(makeFriendApi.getMakeFriendList);
const {
listData,
refresherTriggered,
loadMoreContent,
loadMoreStatus,
onRefresherRefresh,
onRefresherAbort,
onScrollToLower,
onClickLoadMore,
} = useScroll(makeFriendApi.getMakeFriendList);
const scrollTop = ref(0);
const scrollTop = ref(0);
const filterStyle = computed(() => {
let blur = 0;
const scrollY = scrollTop.value;
if (scrollY >= 0) {
blur = Math.min(1, scrollY / 300) * 10;
const filterStyle = computed(() => {
let blur = 0;
const scrollY = scrollTop.value;
if (scrollY >= 0) {
blur = Math.min(1, scrollY / 300) * 10;
}
return {
backdropFilter: `blur(${blur}px)`,
};
});
const navUserInfoStyle = computed(() => {
let opacity = 1;
const scrollY = scrollTop.value;
if (scrollY >= 0) {
opacity = Math.min(1, scrollY / 300 < 0.4 ? 0 : scrollY / 300);
}
return {
opacity,
};
});
function back() {
uni.navigateBack();
}
return {
backdropFilter: `blur(${blur}px)`,
};
});
const navUserInfoStyle = computed(() => {
let opacity = 1;
const scrollY = scrollTop.value;
if (scrollY >= 0) {
opacity = Math.min(1, scrollY / 300 < 0.4 ? 0 : scrollY / 300);
function onScroll(e) {
scrollTop.value = e.detail.scrollTop;
}
return {
opacity,
};
});
function back() {
uni.navigateBack();
}
function onScroll(e: any) {
scrollTop.value = e.detail.scrollTop;
}
</script>
<style scoped lang="scss">
.nav-user-info {
display: flex;
align-items: center;
.nav-avatar {
width: 70rpx;
height: 70rpx;
background-color: #333;
border-radius: 50%;
margin-right: 10rpx;
.nav-user-info {
display: flex;
align-items: center;
.nav-avatar {
width: 70rpx;
height: 70rpx;
background-color: #333;
border-radius: 50%;
margin-right: 10rpx;
}
.nav-name {
width: 140rpx;
@include no-wrap;
}
}
.nav-name {
width: 140rpx;
@include no-wrap;
.nav-bar-right {
display: flex;
align-items: center;
.status {
font-size: $font-size-small;
padding: 10rpx 16rpx;
background-color: $color-theme-d;
border-radius: 36rpx;
margin-right: 40rpx;
}
}
}
.nav-bar-right {
margin-right: 60rpx;
display: flex;
align-items: center;
.status {
font-size: $font-size-small;
padding: 10rpx 16rpx;
background-color: $color-theme-d;
border-radius: 36rpx;
margin-right: 40rpx;
}
}
.page {
height: calc(100vh - 100rpx);
background-color: $color-background;
overflow: hidden;
.image {
width: 100%;
height: 300px;
background-repeat: no-repeat;
transform-origin: top;
background-size: cover;
position: relative;
.info {
position: absolute;
bottom: 20px;
display: flex;
flex-direction: column;
align-items: center;
.page {
height: calc(100vh - 100rpx);
background-color: $color-background;
.image {
width: 100%;
z-index: 100;
.avatar {
text-align: center;
width: 160rpx;
height: 160rpx;
border-radius: 50%;
background-color: #333;
margin-bottom: 20rpx;
}
.name {
font-size: $font-size-large;
margin-bottom: 20rpx;
}
.verification {
background-color: $color-theme;
font-size: $font-size-small;
color: black;
padding: 8rpx;
border-radius: 6rpx;
margin-bottom: 20rpx;
}
.tags {
height: 300px;
background-repeat: no-repeat;
transform-origin: top;
background-size: cover;
position: relative;
.info {
position: absolute;
bottom: 20px;
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 40rpx;
.tag {
background-color: $color-background;
flex-direction: column;
align-items: center;
width: 100%;
z-index: 100;
.avatar {
text-align: center;
width: 160rpx;
height: 160rpx;
border-radius: 50%;
background-color: #333;
margin-bottom: 20rpx;
}
.name {
font-size: $font-size-large;
margin-bottom: 20rpx;
}
.verification {
background-color: $color-theme;
font-size: $font-size-small;
color: $color-theme;
margin-right: 20rpx;
padding: 6rpx;
color: black;
padding: 8rpx;
border-radius: 6rpx;
margin-bottom: 20rpx;
}
.tags {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 40rpx;
.tag {
background-color: $color-background;
font-size: $font-size-small;
color: $color-theme;
margin-right: 20rpx;
padding: 6rpx;
border-radius: 6rpx;
}
}
.introduction {
font-size: $font-size-medium;
}
}
.introduction {
font-size: $font-size-medium;
.filter {
position: absolute;
z-index: 10;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(7, 17, 27, 0.2);
}
}
.filter {
position: absolute;
z-index: 10;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(7, 17, 27, 0.2);
.album {
margin: 40rpx;
.title {
font-size: $font-size-medium-x;
margin: 20rpx 0;
}
.photo {
width: 140rpx;
height: 140rpx;
margin: 0 20rpx 20rpx 0;
border-radius: 10rpx;
}
}
}
.album {
margin: 40rpx;
.title {
font-size: $font-size-medium-x;
margin: 20rpx 0;
}
.photo {
width: 140rpx;
height: 140rpx;
margin: 0 20rpx 20rpx 0;
border-radius: 10rpx;
}
}
.evaluate {
margin: 40rpx;
.title {
font-size: $font-size-medium-x;
margin: 20rpx 0;
}
.rate {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
font-size: $font-size-medium;
color: $color-theme;
.rate-item {
gap: 20rpx;
background-color: $color-background-d;
.evaluate {
margin: 40rpx;
.title {
font-size: $font-size-medium-x;
margin: 20rpx 0;
}
.rate {
display: flex;
align-items: center;
padding: 12rpx 16rpx;
margin-bottom: 10rpx;
border-radius: 12rpx;
justify-content: space-around;
flex-wrap: wrap;
font-size: $font-size-medium;
color: $color-theme;
.rate-item {
gap: 20rpx;
background-color: $color-background-d;
display: flex;
align-items: center;
padding: 12rpx 16rpx;
margin-bottom: 10rpx;
border-radius: 12rpx;
}
}
}
}
}
</style>
+98 -93
View File
@@ -38,7 +38,7 @@
<view class="user-info">
<view class="user">
<text class="user-name">{{ user.name }}</text>
<text class="user-status">{{ user.status || "离线" }}</text>
<text class="user-status">{{ user.status || '离线' }}</text>
</view>
<view class="verification">
<text class="verify-badge">真人认证</text>
@@ -52,117 +52,122 @@
</view>
</view>
</view>
<uni-load-more :status="loadMoreStatus" :content-text="loadMoreContent" @clickLoadMore="onClickLoadMore" />
<uni-load-more
:status="loadMoreStatus"
:content-text="loadMoreContent"
@clickLoadMore="onClickLoadMore"
/>
<view style="height: 100rpx"></view>
</scroll-view>
</template>
<script setup lang="ts">
import { ref } from "vue";
import segmented from "@/sheep/components/segmented/segmented.vue";
import makeFriendApi from "@/sheep/api/make-friend";
import useScroll from "@/sheep/hooks/use-scroll";
<script setup>
import { ref } from 'vue';
import segmented from '@/sheep/components/segmented/segmented.vue';
import makeFriendApi from '@/sheep/api/make-friend';
import useScroll from '@/sheep/hooks/use-scroll';
const {
listData,
refresherTriggered,
loadMoreContent,
loadMoreStatus,
onRefresherRefresh,
onRefresherAbort,
onScrollToLower,
onClickLoadMore,
} = useScroll(makeFriendApi.getMakeFriendList);
const {
listData,
refresherTriggered,
loadMoreContent,
loadMoreStatus,
onRefresherRefresh,
onRefresherAbort,
onScrollToLower,
onClickLoadMore,
} = useScroll(makeFriendApi.getMakeFriendList);
const current = ref(0);
const segmentedItems = ["广场", "最近", "新人"];
const current = ref(0);
const segmentedItems = ['广场', '最近', '新人'];
const selectAddress = () => {
uni.navigateTo({
url: "/pages/home/address-change",
});
};
const selectAddress = () => {
uni.navigateTo({
url: '/pages/home/address-change',
});
};
const detail = () => {
uni.navigateTo({
url: "/pages/home/detail",
});
};
const detail = () => {
uni.navigateTo({
url: '/pages/home/detail',
});
};
</script>
<style scoped lang="scss">
.segmented {
margin: auto;
}
.location {
display: flex;
align-items: center;
font-size: $font-size-medium;
}
.user-list {
background-color: $color-background;
height: calc(100vh - 100rpx - 100rpx);
.user-card {
height: 300rpx;
.segmented {
margin: auto;
}
.location {
display: flex;
justify-content: center;
align-items: center;
padding: 30rpx;
.user-image {
width: 200rpx;
font-size: $font-size-medium;
}
.user-list {
background-color: $color-background;
height: calc(100vh - 100rpx - 100rpx);
.user-card {
height: 300rpx;
margin-right: 20rpx;
border-radius: 16rpx;
background-color: #777777;
}
.user-info {
flex: 1;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: start;
.user {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 16rpx;
.user-name {
font-size: $font-size-large;
font-weight: bold;
}
.user-status {
background-color: $color-theme-d;
font-size: $font-size-small;
padding: 10rpx 20rpx;
border-radius: 36rpx;
color: white;
}
justify-content: center;
align-items: center;
padding: 10rpx 30rpx;
.user-image {
width: 200rpx;
height: 280rpx;
margin-right: 20rpx;
border-radius: 16rpx;
background-color: #777777;
}
.verification {
margin-top: 16rpx;
.verify-badge {
background-color: $color-theme;
font-size: $font-size-small;
color: black;
padding: 6rpx 12rpx;
border-radius: 6rpx;
}
}
.user-details {
margin-top: 26rpx;
.user-info {
flex: 1;
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
font-size: $font-size-small;
color: $color-text-ll;
.tag {
background-color: $color-highlight-background;
padding: 10rpx 16rpx;
border-radius: 6rpx;
flex-direction: column;
justify-content: start;
.user {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 16rpx;
.user-name {
font-size: $font-size-large;
font-weight: bold;
color: $color-text;
}
.user-status {
background-color: $color-theme-d;
font-size: $font-size-small;
padding: 10rpx 20rpx;
border-radius: 36rpx;
color: $color-text;
}
}
.verification {
margin-top: 16rpx;
.verify-badge {
background-color: $color-theme;
font-size: $font-size-small;
color: black;
padding: 6rpx 12rpx;
border-radius: 6rpx;
}
}
.user-details {
margin-top: 26rpx;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
font-size: $font-size-small;
color: $color-text-ll;
.tag {
background-color: $color-highlight-background;
padding: 10rpx 16rpx;
border-radius: 6rpx;
}
}
}
}
}
}
</style>
+91 -85
View File
@@ -18,7 +18,13 @@
/>
</view>
<view class="input-wapper">
<input class="input" type="number" placeholder="验证码" placeholder-style="color: rgba(255, 255, 255, 0.6)" v-model="code" />
<input
class="input"
type="number"
placeholder="验证码"
placeholder-style="color: rgba(255, 255, 255, 0.6)"
v-model="code"
/>
<view class="get-code-btn" @click="handleGetCode">获取验证码</view>
</view>
</view>
@@ -30,103 +36,103 @@
</view>
</template>
<script setup lang="ts">
import { ref } from "vue";
import userApi from "@/sheep/api/user";
<script setup>
import { ref } from 'vue';
import userApi from '@/sheep/api/user';
const phone = ref("");
const code = ref("");
const phone = ref('');
const code = ref('');
const handleGetCode = () => {
console.log("获取验证码");
};
const handleGetCode = () => {
console.log('获取验证码');
};
const handleLogin = async () => {
const { data } = await userApi.login({ mobile: phone.value, code: code.value });
console.log(data);
};
const handleLogin = async () => {
const { data } = await userApi.login({ mobile: phone.value, code: code.value });
console.log(data);
};
</script>
<style scoped lang="scss">
.page {
width: 100vw;
height: 100vh;
overflow: hidden;
position: relative;
.background-image {
width: 100%;
height: 100%;
position: absolute;
inset: 0;
filter: blur(10rpx);
transform: scale(1.1);
}
.content {
padding: 0 40rpx;
display: flex;
flex-direction: column;
align-items: center;
.title-section {
margin: 100rpx 0;
text-align: center;
.main-title {
font-size: 72rpx;
font-weight: bold;
color: $color-text-ll;
margin-bottom: 30rpx;
}
.sub-title {
font-size: 30rpx;
color: $color-text-ll;
letter-spacing: 4rpx;
}
}
.form-section {
.page {
width: 100vw;
height: 100vh;
overflow: hidden;
position: relative;
.background-image {
width: 100%;
height: 100%;
position: absolute;
inset: 0;
filter: blur(10rpx);
transform: scale(1.1);
}
.content {
padding: 0 40rpx;
display: flex;
flex-direction: column;
align-items: center;
gap: 40rpx;
.input-wapper {
display: flex;
align-items: center;
width: 100%;
position: relative;
.input {
width: 100%;
height: 100rpx;
background: $color-highlight-background;
border-radius: 50rpx;
padding: 0 40rpx;
font-size: 32rpx;
.title-section {
margin: 100rpx 0;
text-align: center;
.main-title {
font-size: 72rpx;
font-weight: bold;
color: $color-text-ll;
margin-bottom: 30rpx;
}
.get-code-btn {
position: absolute;
right: 20rpx;
font-size: $font-size-small;
background: $color-theme-d;
white-space: nowrap;
padding: 16rpx;
border-radius: 16rpx;
.sub-title {
font-size: 30rpx;
color: $color-text-ll;
letter-spacing: 4rpx;
}
}
.form-section {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 40rpx;
.input-wapper {
display: flex;
align-items: center;
width: 100%;
position: relative;
.input {
width: 100%;
height: 100rpx;
background: $color-highlight-background;
border-radius: 50rpx;
padding: 0 40rpx;
font-size: 32rpx;
}
.get-code-btn {
position: absolute;
right: 20rpx;
font-size: $font-size-small;
background: $color-theme-d;
white-space: nowrap;
padding: 16rpx;
border-radius: 16rpx;
}
}
}
}
.login-btn {
width: 100%;
margin-top: 40rpx;
border-radius: 50rpx;
align-items: center;
font-size: $font-size-large;
background: $color-theme-d;
color: $color-text;
}
.agreement-text {
position: absolute;
width: 100%;
text-align: center;
bottom: 40rpx;
font-size: $font-size-medium;
}
}
.login-btn {
width: 100%;
margin-top: 40rpx;
border-radius: 50rpx;
align-items: center;
font-size: $font-size-large;
background: $color-theme-d;
color: $color-text;
}
.agreement-text {
position: absolute;
width: 100%;
text-align: center;
bottom: 40rpx;
font-size: $font-size-medium;
}
}
</style>
+87 -90
View File
@@ -13,7 +13,13 @@
<scroll-view class="chat-list" scroll-y>
<view class="chat-item" v-for="message in 10" :key="message.id">
<uni-badge size="small" text="100" absolute="rightTop" :offset="[10, 10]" :custom-style="{ borderColor: 'red' }">
<uni-badge
size="small"
text="100"
absolute="rightTop"
:offset="[10, 10]"
:custom-style="{ borderColor: 'red' }"
>
<image :src="message.avatar" class="avatar" />
</uni-badge>
<view class="chat-user">
@@ -31,104 +37,95 @@
</view>
</template>
<script setup lang="ts">
import { ref, computed } from "vue";
<script setup>
import { ref, computed } from 'vue';
interface Message {
id: number;
username: string;
avatar: string;
lastMessage: string;
time: string;
unread: number;
}
const messageList = ref();
const messageList = ref<Message[]>();
const unreadCount = computed(() => {
return messageList.value.reduce((total, message) => total + message.unread, 0);
});
const unreadCount = computed(() => {
return messageList.value.reduce((total, message) => total + message.unread, 0);
});
</script>
<style scoped lang="scss">
.messages-page {
background-color: $color-background;
height: calc(100vh - 100rpx);
.header {
position: relative;
height: 100rpx;
.message-type {
position: absolute;
top: 0;
bottom: 0;
left: 20rpx;
display: flex;
align-items: center;
font-size: $font-size-large;
.message {
margin-right: 20rpx;
}
}
.interact {
position: absolute;
right: 20rpx;
top: 50rpx;
transform: translateY(-50%);
display: flex;
align-items: center;
background-color: $color-theme;
border-radius: 40rpx;
padding: 4rpx 20rpx;
.text {
font-size: $font-size-medium;
color: #333;
}
}
}
.chat-list {
height: calc(100vh - 100rpx - 100rpx);
.chat-item {
display: flex;
align-items: center;
padding: 15px 20px;
border-bottom: 1px solid #333;
.avatar {
width: 50px;
height: 50px;
border-radius: 25px;
margin-right: 15rpx;
background-color: #333;
}
.chat-user {
flex: 1;
padding: 0 20rpx;
overflow: hidden;
.top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
.username {
font-size: $font-size-medium-x;
}
.time {
font-size: $font-size-small;
color: $color-text-l;
}
.messages-page {
background-color: $color-background;
height: calc(100vh - 100rpx);
.header {
position: relative;
height: 100rpx;
.message-type {
position: absolute;
top: 0;
bottom: 0;
left: 20rpx;
display: flex;
align-items: center;
font-size: $font-size-large;
.message {
margin-right: 20rpx;
}
.bottom {
display: flex;
justify-content: space-between;
align-items: center;
.text {
font-size: $font-size-medium;
flex: 1;
margin-top: 10rpx;
@include no-wrap();
}
.interact {
position: absolute;
right: 20rpx;
top: 50rpx;
transform: translateY(-50%);
display: flex;
align-items: center;
background-color: $color-theme;
border-radius: 40rpx;
padding: 4rpx 20rpx;
.text {
font-size: $font-size-medium;
color: #333;
}
}
}
.chat-list {
height: calc(100vh - 100rpx - 100rpx);
.chat-item {
display: flex;
align-items: center;
padding: 15px 20px;
border-bottom: 1px solid #333;
.avatar {
width: 50px;
height: 50px;
border-radius: 25px;
margin-right: 15rpx;
background-color: #333;
}
.chat-user {
flex: 1;
padding: 0 20rpx;
overflow: hidden;
.top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
.username {
font-size: $font-size-medium-x;
}
.time {
font-size: $font-size-small;
color: $color-text-l;
}
}
.bottom {
display: flex;
justify-content: space-between;
align-items: center;
.text {
font-size: $font-size-medium;
flex: 1;
margin-top: 10rpx;
@include no-wrap();
}
}
}
}
}
}
}
</style>
+81 -71
View File
@@ -1,83 +1,93 @@
<template>
<view class="header">
<segmented class="segmented" v-model="current" :items="segmentedItems"></segmented>
<view class="camera">
<uni-icons type="camera" size="24" color="#ffcd32"></uni-icons>
<view class="moments-page">
<view class="header">
<segmented class="segmented" v-model="current" :items="segmentedItems"></segmented>
<view class="camera">
<uni-icons type="camera" size="24" color="#ffcd32"></uni-icons>
</view>
</view>
</view>
<scroll-view
class="moments-list"
scroll-y
refresher-enabled
refresher-background="#222"
:refresher-triggered="refresherTriggered"
:refresher-threshold="100"
@refresherrefresh="onRefresherRefresh"
@refresherabort="onRefresherAbort"
@scrolltolower="onScrollToLower"
>
<swiper indicator-dots autoplay circular>
<swiper-item>11</swiper-item>
<swiper-item>22</swiper-item>
<swiper-item>33</swiper-item>
</swiper>
<moment
v-for="item in listData"
:avatar="item.avatar"
:name="item.name"
:verification="item.verification"
:content="item.content"
:images="item.images"
:location="item.location"
:likes="item.likes"
:comments="item.comments"
/>
<uni-load-more :status="loadMoreStatus" :content-text="loadMoreContent" @clickLoadMore="onClickLoadMore" />
<view style="height: 100rpx"></view>
</scroll-view>
<scroll-view
class="moments-list"
scroll-y
refresher-enabled
refresher-background="#222"
:refresher-triggered="refresherTriggered"
:refresher-threshold="100"
@refresherrefresh="onRefresherRefresh"
@refresherabort="onRefresherAbort"
@scrolltolower="onScrollToLower"
>
<swiper indicator-dots autoplay circular>
<swiper-item>11</swiper-item>
<swiper-item>22</swiper-item>
<swiper-item>33</swiper-item>
</swiper>
<moment
v-for="item in listData"
:avatar="item.avatar"
:name="item.name"
:verification="item.verification"
:content="item.content"
:images="item.images"
:location="item.location"
:likes="item.likes"
:comments="item.comments"
/>
<uni-load-more
:status="loadMoreStatus"
:content-text="loadMoreContent"
@clickLoadMore="onClickLoadMore"
/>
<view style="height: 100rpx"></view>
</scroll-view>
</view>
</template>
<script setup lang="ts">
import { ref } from "vue";
import segmented from "@/sheep/components/segmented/segmented.vue";
import moment from "@/sheep/components/moment/moment.vue";
import makeFriendApi from "@/sheep/api/make-friend";
import useScroll from "@/sheep/hooks/use-scroll";
<script setup>
import { ref } from 'vue';
import segmented from '@/sheep/components/segmented/segmented.vue';
import moment from '@/sheep/components/moment/moment.vue';
import makeFriendApi from '@/sheep/api/make-friend';
import useScroll from '@/sheep/hooks/use-scroll';
const {
listData,
refresherTriggered,
loadMoreContent,
loadMoreStatus,
onRefresherRefresh,
onRefresherAbort,
onScrollToLower,
onClickLoadMore,
} = useScroll(makeFriendApi.getMakeFriendList);
const {
listData,
refresherTriggered,
loadMoreContent,
loadMoreStatus,
onRefresherRefresh,
onRefresherAbort,
onScrollToLower,
onClickLoadMore,
} = useScroll(makeFriendApi.getMakeFriendList);
const current = ref(0);
const segmentedItems = ["动态", "附近"];
const current = ref(0);
const segmentedItems = ['动态', '附近'];
</script>
<style scoped lang="scss">
.header {
position: relative;
height: 100rpx;
.segmented {
position: absolute;
left: 30rpx;
top: 50rpx;
transform: translateY(-50%);
.moments-page {
background-color: $color-background;
height: calc(100vh - 100rpx);
.header {
position: relative;
height: 100rpx;
.segmented {
position: absolute;
left: 30rpx;
top: 50rpx;
transform: translateY(-50%);
}
.camera {
position: absolute;
top: 50rpx;
transform: translateY(-50%);
right: 30rpx;
}
}
.moments-list {
height: calc(100vh - 100rpx - 100rpx);
}
}
.camera {
position: absolute;
top: 50rpx;
transform: translateY(-50%);
right: 30rpx;
}
}
.moments-list {
height: calc(100vh - 100rpx - 100rpx);
}
</style>
+106 -116
View File
@@ -13,7 +13,7 @@
<view class="user-info">
<text class="username">{{ userInfo.username }}</text>
<text class="user-id">ID: {{ userInfo.id }}</text>
<text class="tag">{{ "vip" }}</text>
<text class="tag">{{ 'vip' }}</text>
</view>
<view class="edit-btn">
<text class="edit-text">编辑</text>
@@ -31,133 +31,123 @@
</view>
</template>
<script setup lang="ts">
import { ref } from "vue";
<script setup>
import { ref } from 'vue';
interface UserInfo {
username: string;
id: string;
avatar: string;
tags: string[];
}
const userInfo = ref({
username: '我的昵称',
id: '123456789',
avatar: '/static/my-avatar.jpg',
tags: ['真人认证', 'VIP会员'],
});
interface MenuItem {
id: number;
icon: string;
text: string;
}
const userInfo = ref<UserInfo>({
username: "我的昵称",
id: "123456789",
avatar: "/static/my-avatar.jpg",
tags: ["真人认证", "VIP会员"],
});
const menuList = ref<MenuItem[]>([
{ id: 1, icon: "1", text: "个人资料" },
{ id: 2, icon: "2", text: "会员中心" },
{ id: 3, icon: "3", text: "我的礼物" },
{ id: 4, icon: "4", text: "我的喜欢" },
{ id: 5, icon: "5", text: "我的关注" },
{ id: 6, icon: "6", text: "我的相册" },
]);
const menuList = ref([
{ id: 1, icon: '1', text: '个人资料' },
{ id: 2, icon: '2', text: '会员中心' },
{ id: 3, icon: '3', text: '我的礼物' },
{ id: 4, icon: '4', text: '我的喜欢' },
{ id: 5, icon: '5', text: '我的关注' },
{ id: 6, icon: '6', text: '我的相册' },
]);
</script>
<style scoped lang="scss">
.profile-page {
background-color: $color-background;
height: calc(100vh - 100rpx);
.header {
position: relative;
height: 100rpx;
.name {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: $font-size-medium-x;
}
.edit-info {
position: absolute;
right: 20rpx;
top: 50rpx;
transform: translateY(-50%);
display: flex;
align-items: center;
background-color: $color-theme;
border-radius: 40rpx;
padding: 8rpx 20rpx;
.text {
font-size: $font-size-medium;
color: #333;
.profile-page {
background-color: $color-background;
height: calc(100vh - 100rpx);
.header {
position: relative;
height: 100rpx;
.name {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: $font-size-medium-x;
color: $color-text;
}
}
}
.content {
height: calc(100vh - 100rpx - 100rpx);
.user-card {
display: flex;
align-items: center;
border-bottom: 1px solid #333;
padding: 0 40rpx 20rpx;
.user-avatar {
width: 80px;
height: 80px;
border-radius: 40px;
margin-right: 15px;
background-color: #333;
}
.user-info {
flex: 1;
.username {
display: block;
font-size: $font-size-medium-x;
font-weight: bold;
margin-bottom: 8px;
}
.user-id {
display: block;
font-size: 12px;
color: $color-text-l;
margin-bottom: 8px;
}
.tag {
background-color: $color-theme;
color: #000;
padding: 2px 8px;
border-radius: 10px;
font-size: $font-size-small;
}
}
.edit-btn {
padding: 8px 16px;
border: 1px solid $color-theme;
border-radius: 20px;
.edit-text {
color: $color-theme;
font-size: $font-size-medium;
}
}
}
.menu-section {
margin: 20px 40px;
.menu-item {
.edit-info {
position: absolute;
right: 20rpx;
top: 50rpx;
transform: translateY(-50%);
display: flex;
align-items: center;
padding: 15px 0;
border-bottom: 1px solid #333;
font-size: $font-size-medium;
.menu-icon {
width: 30px;
font-size: 18px;
margin-right: 15px;
background-color: $color-theme;
border-radius: 40rpx;
padding: 8rpx 20rpx;
.text {
font-size: $font-size-medium;
color: #333;
}
.menu-text {
}
}
.content {
height: calc(100vh - 100rpx - 100rpx);
.user-card {
display: flex;
align-items: center;
border-bottom: 1px solid #333;
padding: 0 40rpx 20rpx;
.user-avatar {
width: 80px;
height: 80px;
border-radius: 40px;
margin-right: 15px;
background-color: #333;
}
.user-info {
flex: 1;
.username {
display: block;
font-size: $font-size-medium-x;
font-weight: bold;
margin-bottom: 8px;
color: $color-text-ll;
}
.user-id {
display: block;
font-size: 12px;
color: $color-text-l;
margin-bottom: 8px;
}
.tag {
background-color: $color-theme;
color: #000;
padding: 2px 8px;
border-radius: 10px;
font-size: $font-size-small;
}
}
.edit-btn {
padding: 8px 16px;
border: 1px solid $color-theme;
border-radius: 20px;
.edit-text {
color: $color-theme;
font-size: $font-size-medium;
}
}
}
.menu-section {
margin: 20px 40px;
.menu-item {
display: flex;
align-items: center;
padding: 15px 0;
border-bottom: 1px solid #333;
font-size: $font-size-medium;
.menu-icon {
width: 30px;
font-size: 18px;
margin-right: 15px;
}
.menu-text {
flex: 1;
color: $color-text-ll;
}
}
}
}
}
}
</style>
@@ -2,17 +2,17 @@
const mockUsers = Array.from({ length: 26 }).map((_, index) => ({
id: index,
name: "山峰" + (index + 1),
avatar: "/static/avatar2.jpg",
gender: "男",
name: '山峰' + (index + 1),
avatar: '/static/avatar2.jpg',
gender: '男',
age: 30,
height: "185",
weight: "75",
occupation: "健身教练",
distance: "5.2",
height: '185',
weight: '75',
occupation: '健身教练',
distance: '5.2',
photoCount: 3,
isVip: false,
status: "在线",
status: '在线',
}));
const makeFriendApi = {
@@ -24,7 +24,7 @@ const makeFriendApi = {
// });
// },
getMakeFriendList: async (page: number = 1, pageSize: number = 10) => {
getMakeFriendList: async (page = 1, pageSize = 10) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
const startIndex = (page - 1) * pageSize;
+102 -92
View File
@@ -34,111 +34,121 @@
</view>
</template>
<script setup lang="ts">
withDefaults(
defineProps<
Partial<{
avatar: string;
name: string;
verification: boolean;
content: string;
images: string[];
location: string;
likes: number | string;
comments: number | string;
}>
>(),
{
avatar: "",
name: "",
verification: false,
content: "",
images: () => [],
location: "",
likes: 0,
comments: 0,
}
);
<script setup>
defineProps({
avatar: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
verification: {
type: Boolean,
default: false,
},
content: {
type: String,
default: '',
},
images: {
type: Array,
default: [],
},
location: {
type: String,
default: '',
},
likes: {
type: Number | String,
default: 0,
},
comments: {
type: Number | String,
default: 0,
},
});
</script>
<style scoped lang="scss">
.moment {
padding: 20px;
border-bottom: 1px solid #333;
.user {
display: flex;
align-items: center;
margin-bottom: 15px;
.avatar {
width: 44px;
height: 44px;
border-radius: 20px;
margin-right: 10px;
background-color: #333;
}
.details {
flex: 1;
.username {
font-size: $font-size-medium-x;
margin-right: 20rpx;
}
.verification {
background-color: $color-theme;
font-size: $font-size-small;
color: black;
padding: 4rpx;
border-radius: 6rpx;
}
.info {
font-size: $font-size-small;
margin-top: 20rpx;
}
}
}
.content {
flex: 1;
padding: 0 20px;
margin-bottom: 15px;
.text {
line-height: 1.5;
}
.images {
margin-top: 20rpx;
.moment {
padding: 20px;
border-bottom: 1px solid #333;
.user {
display: flex;
gap: 10px;
flex-wrap: wrap;
.moment-image {
width: 100px;
height: 100px;
border-radius: 8px;
align-items: center;
margin-bottom: 15px;
.avatar {
width: 44px;
height: 44px;
border-radius: 20px;
margin-right: 10px;
background-color: #333;
}
.details {
flex: 1;
.username {
font-size: $font-size-medium-x;
margin-right: 20rpx;
}
.verification {
background-color: $color-theme;
font-size: $font-size-small;
color: black;
padding: 4rpx;
border-radius: 6rpx;
}
.info {
font-size: $font-size-small;
margin-top: 20rpx;
}
}
}
}
.address {
display: flex;
align-items: center;
gap: 10rpx;
padding: 0 20rpx;
margin-bottom: 20rpx;
.city {
font-size: $font-size-small;
.content {
flex: 1;
padding: 0 20px;
margin-bottom: 15px;
.text {
line-height: 1.5;
}
.images {
margin-top: 20rpx;
display: flex;
gap: 10px;
flex-wrap: wrap;
.moment-image {
width: 100px;
height: 100px;
border-radius: 8px;
background-color: #333;
}
}
}
}
.actions {
display: flex;
gap: 60rpx;
padding: 0 20rpx;
.action-btn {
.address {
display: flex;
align-items: center;
gap: 10rpx;
color: $color-text;
font-size: $font-size-medium;
.text {
padding: 0 20rpx;
margin-bottom: 20rpx;
.city {
font-size: $font-size-small;
}
}
.actions {
display: flex;
gap: 60rpx;
padding: 0 20rpx;
.action-btn {
display: flex;
align-items: center;
gap: 10rpx;
color: $color-text;
font-size: $font-size-medium;
.text {
font-size: $font-size-medium;
}
}
}
}
}
</style>
+59 -62
View File
@@ -1,39 +1,36 @@
<script setup lang="ts">
import { computed } from "vue";
<script setup>
import { computed } from 'vue';
const props = withDefaults(
defineProps<{
items?: string[];
modelValue?: number;
}>(),
{
items: () => [],
modelValue: 0,
}
);
const emit = defineEmits<{
(e: "update:modelValue", index: number): void;
(e: "onChange", target: { currentIndex: number }): void;
}>();
const activeStyle = computed(() => {
const x = 60 * props.modelValue;
return {
transform: `translate3d(${x}px, 0, 0)`,
};
});
const segmentedWidth = computed(() => {
return { width: `${props.items.length * 60}px` };
});
function segmentedItem(index: number) {
if (props.modelValue === index) return;
emit("update:modelValue", index);
emit("onChange", {
currentIndex: index,
const props = defineProps({
items: {
type: Array,
default: [],
},
modelValue: {
type: Number,
default: 0,
},
});
}
const emit = defineEmits(['update:modelValue', 'onChange']);
const activeStyle = computed(() => {
const x = 60 * props.modelValue;
return {
transform: `translate3d(${x}px, 0, 0)`,
};
});
const segmentedWidth = computed(() => {
return { width: `${props.items.length * 60}px` };
});
function segmentedItem(index) {
if (props.modelValue === index) return;
emit('update:modelValue', index);
emit('onChange', {
currentIndex: index,
});
}
</script>
<template>
@@ -52,35 +49,35 @@ function segmentedItem(index: number) {
</template>
<style scoped lang="scss">
.segmentedes {
display: flex;
position: relative;
align-items: center;
margin: 0 auto;
border: 1px solid $color-highlight-background;
border-radius: 5px;
.segmented-item {
.segmentedes {
display: flex;
position: relative;
z-index: 10;
flex: 1;
height: 30px;
line-height: 30px;
text-align: center;
font-size: $font-size-medium;
color: $color-text-d;
&.active {
color: $color-text;
align-items: center;
margin: 0 auto;
border: 1px solid $color-highlight-background;
border-radius: 5px;
.segmented-item {
position: relative;
z-index: 10;
flex: 1;
height: 30px;
line-height: 30px;
text-align: center;
font-size: $font-size-medium;
color: $color-text-d;
&.active {
color: $color-text;
}
}
.active-bar {
position: absolute;
left: 0;
top: 0;
width: 60px;
height: 30px;
transition: transform 0.3s;
border-radius: 5px;
background: $color-highlight-background;
}
}
.active-bar {
position: absolute;
left: 0;
top: 0;
width: 60px;
height: 30px;
transition: transform 0.3s;
border-radius: 5px;
background: $color-highlight-background;
}
}
</style>
@@ -1,28 +1,28 @@
import { onMounted, reactive, ref } from "vue";
import { onMounted, reactive, ref } from 'vue';
export default function useScroll(fetchList: (...arg: any) => Promise<any>) {
export default function useScroll(fetchList) {
const refresherTriggered = ref(false);
const loadMoreContent = reactive({
contentdown: " ",
contentrefresh: "正在加载...",
contentnomore: "没有更多数据了",
contentdown: ' ',
contentrefresh: '正在加载...',
contentnomore: '没有更多数据了',
});
const loadMoreStatus = ref<"more" | "loading" | "noMore">("more");
const listData = ref<any[]>([]);
const loadMoreStatus = ref('more'); // 'more' | 'loading' | 'noMore'
const listData = ref([]);
const pageInfo = reactive({
current: 1,
total: 0,
hasMore: true,
});
onMounted(() => {
loadData(false);
onMounted(async () => {
await loadData(false);
});
const loadData = async (isRefresh: boolean = false) => {
const loadData = async (isRefresh = false) => {
try {
if (pageInfo.current === 1) {
uni.showLoading({ title: "加载中..." });
uni.showLoading({ title: '加载中...' });
}
if (isRefresh) {
pageInfo.current = 1;
@@ -30,21 +30,21 @@ export default function useScroll(fetchList: (...arg: any) => Promise<any>) {
listData.value = data;
pageInfo.total = total;
} else {
const { total, data } = await fetchList(pageInfo.current);
listData.value.push(...data);
const { total, data = [] } = await fetchList(pageInfo.current);
listData.value = listData.value.concat(data);
pageInfo.total = total;
}
pageInfo.current = pageInfo.current + 1;
if (listData.value.length >= pageInfo.total) {
loadMoreStatus.value = "noMore";
loadMoreStatus.value = 'noMore';
} else {
loadMoreStatus.value = "more";
loadMoreStatus.value = 'more';
}
return true;
} catch (error) {
console.error("加载数据失败:", error);
console.error('加载数据失败:', error);
return false;
} finally {
uni.hideLoading();
@@ -57,28 +57,28 @@ export default function useScroll(fetchList: (...arg: any) => Promise<any>) {
const success = await loadData(true);
refresherTriggered.value = false;
if (success) {
uni.showToast({ title: "刷新成功", icon: "success" });
uni.showToast({ title: '刷新成功', icon: 'success' });
}
};
// 下拉刷新中止
const onRefresherAbort = () => {
console.log("下拉刷新中止");
console.log('下拉刷新中止');
refresherTriggered.value = false;
};
// 触发上拉加载
const onScrollToLower = async () => {
if (loadMoreStatus.value === "loading" || loadMoreStatus.value === "noMore") {
if (loadMoreStatus.value === 'loading' || loadMoreStatus.value === 'noMore') {
return;
}
loadMoreStatus.value = "loading";
loadMoreStatus.value = 'loading';
await loadData(false);
};
// 点击加载更多
const onClickLoadMore = () => {
if (loadMoreStatus.value === "more") {
if (loadMoreStatus.value === 'more') {
onScrollToLower();
}
};
+2 -2
View File
@@ -28,13 +28,13 @@ $uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
$uni-bg-color: #ffffff;
$uni-bg-color: #222222;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1; //点击状态颜色
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); //遮罩颜色
/* 边框颜色 */
$uni-border-color: #e5e5e5;
$uni-border-color: #333333;
/* 尺寸变量 */