feat: 完善对象存储上传与真实条码二维码引擎,彻底剥离保底Mock数据
这个提交包含在:
415
pwuniapp/components/CustomTabBar.vue
普通文件
415
pwuniapp/components/CustomTabBar.vue
普通文件
@@ -0,0 +1,415 @@
|
||||
<template>
|
||||
<view class="custom-tabbar-root" :class="{ 'theme-warm': currentThemeKey === 'warm' }">
|
||||
<!-- 底部占位块,确保内容不被导航栏遮挡 -->
|
||||
<view class="tabbar-placeholder"></view>
|
||||
|
||||
<!-- 固定在屏幕底部的导航栏实体 -->
|
||||
<view class="tabbar-container">
|
||||
<!-- 发单人端 (5 项:首页 / 附近活动 / 我要发单+ / 友圈 / 个人中心) -->
|
||||
<template v-if="currentRole === 'PLAYER'">
|
||||
<!-- 1. 首页 -->
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: current === 'home' }"
|
||||
@click="switchTab('home', '/pages/index/index')"
|
||||
>
|
||||
<image class="tab-icon-svg" :src="getTabIcon('home')" mode="aspectFit" />
|
||||
<text class="tab-label">首页</text>
|
||||
</view>
|
||||
|
||||
<!-- 2. 附近活动 (原本的活动项目) -->
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: current === 'activity' }"
|
||||
@click="switchTab('activity', '/pages/activity/list')"
|
||||
>
|
||||
<image class="tab-icon-svg" :src="getTabIcon('activity')" mode="aspectFit" />
|
||||
<text class="tab-label">附近活动</text>
|
||||
</view>
|
||||
|
||||
<!-- 3. 我要发单 (核心大圆圈 + 突出悬浮按键,居中对称) -->
|
||||
<view class="tab-item special-item-wrap btn-active" @click="handlePublish">
|
||||
<view class="plus-circle-btn">
|
||||
<image class="plus-svg" :src="ICONS.tabPlus" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="tab-label special-label">我要发单</text>
|
||||
</view>
|
||||
|
||||
<!-- 4. 友圈 -->
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: current === 'community' }"
|
||||
@click="switchTab('community', '/pages/community/index')"
|
||||
>
|
||||
<image class="tab-icon-svg" :src="getTabIcon('community')" mode="aspectFit" />
|
||||
<text class="tab-label">友圈</text>
|
||||
</view>
|
||||
|
||||
<!-- 5. 个人中心 -->
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: current === 'user' }"
|
||||
@click="switchTab('user', '/pages/user/index')"
|
||||
>
|
||||
<image class="tab-icon-svg" :src="getTabIcon('user')" mode="aspectFit" />
|
||||
<text class="tab-label">个人中心</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 接单人端 (3 项:接单大厅 / 我要接单[5km雷达] / 个人中心) -->
|
||||
<template v-else>
|
||||
<!-- 1. 接单大厅 -->
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: current === 'hall' }"
|
||||
@click="switchTab('hall', '/pages/expert/hall')"
|
||||
>
|
||||
<image class="tab-icon-svg" :src="getTabIcon('hall')" mode="aspectFit" />
|
||||
<text class="tab-label">接单大厅</text>
|
||||
</view>
|
||||
|
||||
<!-- 2. 我要接单 (核心特色:地图智能匹配附近5km定位) -->
|
||||
<view
|
||||
class="tab-item radar-item-wrap btn-active"
|
||||
:class="{ active: current === 'radar' }"
|
||||
@click="switchTab('radar', '/pages/expert/radar')"
|
||||
>
|
||||
<view class="radar-badge-box">
|
||||
<view class="radar-icon-box" :class="{ 'radar-anim': current === 'radar' }">
|
||||
<image class="radar-svg" :src="getTabIcon('radar')" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="radar-pill">附近5km</text>
|
||||
</view>
|
||||
<text class="tab-label radar-label">我要接单</text>
|
||||
</view>
|
||||
|
||||
<!-- 3. 个人中心 -->
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: current === 'user' }"
|
||||
@click="switchTab('user', '/pages/user/index')"
|
||||
>
|
||||
<image class="tab-icon-svg" :src="getTabIcon('user')" mode="aspectFit" />
|
||||
<text class="tab-label">个人中心</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { ICONS, getTabIconUri } from '@/common/icons.js';
|
||||
import { getCurrentThemeKey } from '@/common/theme.js';
|
||||
import { getCurrentRole, setCurrentRole, ROLE_TYPES, isExpertCertified } from '@/common/role.js';
|
||||
|
||||
const props = defineProps({
|
||||
current: {
|
||||
type: String,
|
||||
default: 'activity'
|
||||
}
|
||||
});
|
||||
|
||||
const currentRole = ref(getCurrentRole());
|
||||
const currentThemeKey = ref(getCurrentThemeKey());
|
||||
|
||||
const getTabIcon = (key) => {
|
||||
const isActive = props.current === key;
|
||||
const isWarm = currentThemeKey.value === 'warm';
|
||||
let color;
|
||||
if (isActive) {
|
||||
if (key === 'radar' || (currentRole.value === 'EXPERT' && key === 'hall')) {
|
||||
color = '#10B981';
|
||||
} else {
|
||||
color = isWarm ? '#EA580C' : '#6366F1';
|
||||
}
|
||||
} else {
|
||||
color = isWarm ? '#A8A29E' : '#64748B';
|
||||
}
|
||||
return getTabIconUri(key, color);
|
||||
};
|
||||
|
||||
const onRoleChanged = (newRole) => {
|
||||
if (newRole === 'EXPERT' && !isExpertCertified()) {
|
||||
currentRole.value = 'PLAYER';
|
||||
return;
|
||||
}
|
||||
currentRole.value = newRole;
|
||||
};
|
||||
|
||||
const onThemeChanged = (newTheme) => {
|
||||
currentThemeKey.value = newTheme;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
currentRole.value = getCurrentRole();
|
||||
if (currentRole.value === 'EXPERT' && !isExpertCertified()) {
|
||||
currentRole.value = 'PLAYER';
|
||||
}
|
||||
currentThemeKey.value = getCurrentThemeKey();
|
||||
try {
|
||||
uni.hideTabBar({ animation: false, fail: () => {} });
|
||||
} catch (e) {}
|
||||
uni.$on('role-change', onRoleChanged);
|
||||
uni.$on('theme-change', onThemeChanged);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
uni.$off('role-change', onRoleChanged);
|
||||
uni.$off('theme-change', onThemeChanged);
|
||||
});
|
||||
|
||||
const switchTab = (tabKey, targetPath) => {
|
||||
if (props.current === tabKey) return;
|
||||
if ((tabKey === 'hall' || tabKey === 'radar') && !isExpertCertified()) {
|
||||
uni.showToast({
|
||||
title: '尚未通过大神认证,无法访问接单端',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.reLaunch({
|
||||
url: targetPath
|
||||
});
|
||||
};
|
||||
|
||||
const handlePublish = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/activity/create'
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.custom-tabbar-root {
|
||||
width: 100%;
|
||||
|
||||
/* 底部占位高度,确保页面内容滚动到底部不被遮挡 */
|
||||
.tabbar-placeholder {
|
||||
width: 100%;
|
||||
height: calc(126rpx + env(safe-area-inset-bottom));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tabbar-container {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
max-width: 450px;
|
||||
box-sizing: border-box;
|
||||
height: calc(118rpx + env(safe-area-inset-bottom));
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-around;
|
||||
z-index: 999;
|
||||
|
||||
/* 冷光暗夜默认主题 */
|
||||
background: rgba(21, 29, 48, 0.95);
|
||||
backdrop-filter: blur(16px);
|
||||
border-top: 1rpx solid rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 118rpx;
|
||||
position: relative;
|
||||
|
||||
.tab-icon-svg {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.tab-label {
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
margin-top: 4rpx;
|
||||
font-weight: 500;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.tab-icon-svg {
|
||||
transform: scale(1.06);
|
||||
}
|
||||
.tab-label {
|
||||
color: #6366F1;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 核心:发单人端中间“我要发单”突出大圆圈 + */
|
||||
.special-item-wrap {
|
||||
position: relative;
|
||||
top: -24rpx;
|
||||
height: 118rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding-top: 2rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.plus-circle-btn {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
min-width: 96rpx;
|
||||
min-height: 96rpx;
|
||||
max-width: 96rpx;
|
||||
max-height: 96rpx;
|
||||
aspect-ratio: 1 / 1;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0;
|
||||
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 4rpx solid #FFFFFF;
|
||||
box-shadow: 0 8rpx 20rpx rgba(99, 102, 241, 0.45);
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
.plus-svg {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.special-label {
|
||||
margin-top: 4rpx;
|
||||
color: #A5B4FC;
|
||||
font-weight: 600;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&:active .plus-circle-btn {
|
||||
transform: scale(0.92);
|
||||
box-shadow: 0 4rpx 12rpx rgba(99, 102, 241, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 接单人端:“我要接单”雷达微标与动画 */
|
||||
.radar-item-wrap {
|
||||
height: 118rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.radar-badge-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.radar-icon-box {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.radar-svg {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.radar-pill {
|
||||
position: absolute;
|
||||
top: -12rpx;
|
||||
right: -40rpx;
|
||||
background: linear-gradient(135deg, #10B981, #059669);
|
||||
color: #FFFFFF;
|
||||
font-size: 18rpx;
|
||||
line-height: 1.2;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 12rpx;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 2rpx 8rpx rgba(16, 185, 129, 0.4);
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.radar-label {
|
||||
font-size: 23rpx;
|
||||
color: #94A3B8;
|
||||
margin-top: 4rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.radar-label {
|
||||
color: #10B981;
|
||||
font-weight: 600;
|
||||
}
|
||||
.radar-anim {
|
||||
animation: pulseRadar 1.8s infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulseRadar {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 浅色暖阳 (Warm Theme) 适配 */
|
||||
&.theme-warm {
|
||||
.tabbar-container {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-top: 1rpx solid #F1EFE9;
|
||||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
.tab-label {
|
||||
color: #78716C;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.tab-label {
|
||||
color: #EA580C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.special-item-wrap {
|
||||
.plus-circle-btn {
|
||||
background: linear-gradient(135deg, #EA580C 0%, #F59E0B 100%);
|
||||
border: 4rpx solid #FFFFFF;
|
||||
box-shadow: 0 8rpx 20rpx rgba(234, 88, 12, 0.4);
|
||||
}
|
||||
.special-label {
|
||||
color: #EA580C;
|
||||
}
|
||||
}
|
||||
|
||||
.radar-item-wrap {
|
||||
&.active {
|
||||
.radar-label {
|
||||
color: #10B981;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,664 @@
|
||||
<template>
|
||||
<view class="skeleton-wrap" :class="[themeClass]">
|
||||
<!-- 1. 首页骨架屏 (type === 'home') -->
|
||||
<view v-if="type === 'home'" class="skeleton-home">
|
||||
<!-- 轮播图骨架 -->
|
||||
<view class="skeleton-banner skeleton-shimmer" />
|
||||
|
||||
<!-- 金刚区 8 宫格骨架 -->
|
||||
<view class="skeleton-grid">
|
||||
<view v-for="i in 8" :key="i" class="skeleton-grid-item">
|
||||
<view class="skeleton-circle skeleton-shimmer" />
|
||||
<view class="skeleton-bar-sm skeleton-shimmer" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 推荐大神流骨架 -->
|
||||
<view class="skeleton-sec-header">
|
||||
<view class="skeleton-bar-md skeleton-shimmer" />
|
||||
<view class="skeleton-bar-sm skeleton-shimmer" />
|
||||
</view>
|
||||
|
||||
<view class="skeleton-card-list">
|
||||
<view v-for="i in 3" :key="i" class="skeleton-expert-card">
|
||||
<view class="card-top">
|
||||
<view class="card-avatar skeleton-shimmer" />
|
||||
<view class="card-info">
|
||||
<view class="skeleton-bar-lg skeleton-shimmer" />
|
||||
<view class="card-tags">
|
||||
<view class="skeleton-pill skeleton-shimmer" />
|
||||
<view class="skeleton-pill skeleton-shimmer" />
|
||||
</view>
|
||||
<view class="skeleton-bar-sm skeleton-shimmer" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-voice skeleton-shimmer" />
|
||||
<view class="card-bottom">
|
||||
<view class="skeleton-bar-md skeleton-shimmer" />
|
||||
<view class="skeleton-btn skeleton-shimmer" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 2. 活动大厅骨架屏 (type === 'activity') -->
|
||||
<view v-else-if="type === 'activity'" class="skeleton-activity">
|
||||
<!-- 搜索栏骨架 -->
|
||||
<view class="skeleton-search-bar skeleton-shimmer" />
|
||||
|
||||
<!-- 分类胶囊栏骨架 -->
|
||||
<view class="skeleton-capsules">
|
||||
<view v-for="i in 5" :key="i" class="skeleton-capsule-item skeleton-shimmer" />
|
||||
</view>
|
||||
|
||||
<!-- 组局卡片骨架列表 -->
|
||||
<view class="skeleton-activity-list">
|
||||
<view v-for="i in 3" :key="i" class="skeleton-activity-card">
|
||||
<view class="act-header">
|
||||
<view class="act-leader-avatar skeleton-shimmer" />
|
||||
<view class="act-header-info">
|
||||
<view class="skeleton-bar-lg skeleton-shimmer" />
|
||||
<view class="skeleton-bar-sm skeleton-shimmer" />
|
||||
</view>
|
||||
<view class="skeleton-pill skeleton-shimmer" />
|
||||
</view>
|
||||
<view class="act-location skeleton-shimmer" />
|
||||
<view class="act-footer">
|
||||
<view class="act-avatars-cluster">
|
||||
<view v-for="j in 3" :key="j" class="act-user-circle skeleton-shimmer" />
|
||||
</view>
|
||||
<view class="skeleton-bar-md skeleton-shimmer" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 3. 友圈广场骨架屏 (type === 'community') -->
|
||||
<view v-else-if="type === 'community'" class="skeleton-community">
|
||||
<!-- 分类标签骨架 -->
|
||||
<view class="skeleton-tabs">
|
||||
<view v-for="i in 4" :key="i" class="skeleton-tab-item skeleton-shimmer" />
|
||||
</view>
|
||||
|
||||
<!-- 动态卡片列表骨架 -->
|
||||
<view class="skeleton-post-list">
|
||||
<view v-for="i in 2" :key="i" class="skeleton-post-card">
|
||||
<view class="post-header">
|
||||
<view class="post-avatar skeleton-shimmer" />
|
||||
<view class="post-user-info">
|
||||
<view class="skeleton-bar-md skeleton-shimmer" />
|
||||
<view class="skeleton-bar-sm skeleton-shimmer" />
|
||||
</view>
|
||||
<view class="skeleton-pill skeleton-shimmer" />
|
||||
</view>
|
||||
<view class="post-text-lines">
|
||||
<view class="skeleton-bar-full skeleton-shimmer" />
|
||||
<view class="skeleton-bar-sub skeleton-shimmer" />
|
||||
</view>
|
||||
<view class="post-images-grid">
|
||||
<view class="post-img-box skeleton-shimmer" />
|
||||
<view class="post-img-box skeleton-shimmer" />
|
||||
</view>
|
||||
<view class="post-actions-bar">
|
||||
<view class="action-slot skeleton-shimmer" />
|
||||
<view class="action-slot skeleton-shimmer" />
|
||||
<view class="action-slot skeleton-shimmer" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 4. 大神主页骨架屏 (type === 'expert') -->
|
||||
<view v-else-if="type === 'expert'" class="skeleton-expert">
|
||||
<view class="expert-hero skeleton-shimmer" />
|
||||
<view class="expert-profile-box">
|
||||
<view class="expert-avatar-large skeleton-shimmer" />
|
||||
<view class="skeleton-bar-lg skeleton-shimmer" />
|
||||
<view class="expert-tags-row">
|
||||
<view class="skeleton-pill skeleton-shimmer" />
|
||||
<view class="skeleton-pill skeleton-shimmer" />
|
||||
<view class="skeleton-pill skeleton-shimmer" />
|
||||
</view>
|
||||
<view class="expert-voice-bar skeleton-shimmer" />
|
||||
</view>
|
||||
<view class="expert-services-box">
|
||||
<view class="skeleton-bar-md skeleton-shimmer" />
|
||||
<view class="skeleton-service-card skeleton-shimmer" />
|
||||
<view class="skeleton-service-card skeleton-shimmer" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { getCurrentThemeKey } from '@/common/theme.js';
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'home'
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const themeClass = computed(() => {
|
||||
const activeTheme = props.theme || getCurrentThemeKey();
|
||||
return activeTheme === 'warm' ? 'theme-warm' : 'theme-cool';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@keyframes skeletonShimmer {
|
||||
0% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-shimmer {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 255, 255, 0.04) 25%,
|
||||
rgba(255, 255, 255, 0.12) 37%,
|
||||
rgba(255, 255, 255, 0.04) 63%
|
||||
);
|
||||
background-size: 400% 100%;
|
||||
animation: skeletonShimmer 1.5s ease infinite;
|
||||
}
|
||||
|
||||
.skeleton-wrap {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// 1. 首页骨架屏样式
|
||||
.skeleton-home {
|
||||
padding: 16rpx 32rpx;
|
||||
|
||||
.skeleton-banner {
|
||||
width: 100%;
|
||||
height: 320rpx;
|
||||
border-radius: 24rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.skeleton-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 24rpx 16rpx;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx 16rpx;
|
||||
margin-bottom: 36rpx;
|
||||
|
||||
.skeleton-grid-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
|
||||
.skeleton-circle {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.skeleton-bar-sm {
|
||||
width: 80rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-sec-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.skeleton-bar-md {
|
||||
width: 180rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 18rpx;
|
||||
}
|
||||
.skeleton-bar-sm {
|
||||
width: 100rpx;
|
||||
height: 24rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-card-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
|
||||
.skeleton-expert-card {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18rpx;
|
||||
|
||||
.card-top {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
|
||||
.card-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
|
||||
.skeleton-bar-lg {
|
||||
width: 220rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.card-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
|
||||
.skeleton-pill {
|
||||
width: 90rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-bar-sm {
|
||||
width: 140rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-voice {
|
||||
width: 100%;
|
||||
height: 60rpx;
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
|
||||
.card-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.skeleton-bar-md {
|
||||
width: 140rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 18rpx;
|
||||
}
|
||||
.skeleton-btn {
|
||||
width: 150rpx;
|
||||
height: 56rpx;
|
||||
border-radius: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 活动大厅骨架屏样式
|
||||
.skeleton-activity {
|
||||
padding: 16rpx 32rpx;
|
||||
|
||||
.skeleton-search-bar {
|
||||
width: 100%;
|
||||
height: 76rpx;
|
||||
border-radius: 38rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.skeleton-capsules {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 32rpx;
|
||||
overflow-x: hidden;
|
||||
|
||||
.skeleton-capsule-item {
|
||||
width: 130rpx;
|
||||
height: 56rpx;
|
||||
border-radius: 28rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-activity-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
|
||||
.skeleton-activity-card {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18rpx;
|
||||
|
||||
.act-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
|
||||
.act-leader-avatar {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.act-header-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
|
||||
.skeleton-bar-lg {
|
||||
width: 320rpx;
|
||||
height: 30rpx;
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
.skeleton-bar-sm {
|
||||
width: 160rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-pill {
|
||||
width: 110rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.act-location {
|
||||
width: 80%;
|
||||
height: 26rpx;
|
||||
border-radius: 13rpx;
|
||||
}
|
||||
|
||||
.act-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 12rpx;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
|
||||
.act-avatars-cluster {
|
||||
display: flex;
|
||||
|
||||
.act-user-circle {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: -12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-bar-md {
|
||||
width: 160rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 18rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 友圈广场骨架屏样式
|
||||
.skeleton-community {
|
||||
padding: 16rpx 32rpx;
|
||||
|
||||
.skeleton-tabs {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
.skeleton-tab-item {
|
||||
width: 140rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-post-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28rpx;
|
||||
|
||||
.skeleton-post-card {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
|
||||
.post-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18rpx;
|
||||
|
||||
.post-avatar {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.post-user-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
|
||||
.skeleton-bar-md {
|
||||
width: 180rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 14rpx;
|
||||
}
|
||||
.skeleton-bar-sm {
|
||||
width: 120rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-pill {
|
||||
width: 110rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 18rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.post-text-lines {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
|
||||
.skeleton-bar-full {
|
||||
width: 100%;
|
||||
height: 26rpx;
|
||||
border-radius: 13rpx;
|
||||
}
|
||||
.skeleton-bar-sub {
|
||||
width: 70%;
|
||||
height: 26rpx;
|
||||
border-radius: 13rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.post-images-grid {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
|
||||
.post-img-box {
|
||||
width: 220rpx;
|
||||
height: 200rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.post-actions-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
|
||||
.action-slot {
|
||||
width: 120rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 大神详情骨架屏样式
|
||||
.skeleton-expert {
|
||||
.expert-hero {
|
||||
width: 100%;
|
||||
height: 420rpx;
|
||||
}
|
||||
|
||||
.expert-profile-box {
|
||||
padding: 32rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 18rpx;
|
||||
margin-top: -60rpx;
|
||||
background: rgba(21, 29, 48, 0.95);
|
||||
border-radius: 36rpx 36rpx 0 0;
|
||||
|
||||
.expert-avatar-large {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.skeleton-bar-lg {
|
||||
width: 240rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 18rpx;
|
||||
}
|
||||
|
||||
.expert-tags-row {
|
||||
display: flex;
|
||||
gap: 14rpx;
|
||||
|
||||
.skeleton-pill {
|
||||
width: 120rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 18rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.expert-voice-bar {
|
||||
width: 90%;
|
||||
height: 64rpx;
|
||||
border-radius: 32rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.expert-services-box {
|
||||
padding: 32rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
|
||||
.skeleton-bar-md {
|
||||
width: 180rpx;
|
||||
height: 34rpx;
|
||||
border-radius: 17rpx;
|
||||
}
|
||||
|
||||
.skeleton-service-card {
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 暖色系适配 (theme-warm)
|
||||
.theme-warm {
|
||||
.skeleton-shimmer {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
#F3EFEA 25%,
|
||||
#E5DFD7 37%,
|
||||
#F3EFEA 63%
|
||||
);
|
||||
background-size: 400% 100%;
|
||||
}
|
||||
|
||||
.skeleton-home {
|
||||
.skeleton-grid {
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.skeleton-card-list {
|
||||
.skeleton-expert-card {
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-activity {
|
||||
.skeleton-activity-list {
|
||||
.skeleton-activity-card {
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
.act-footer {
|
||||
border-top-color: #F5F2EC;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-community {
|
||||
.skeleton-post-list {
|
||||
.skeleton-post-card {
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
.post-actions-bar {
|
||||
border-top-color: #F5F2EC;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-expert {
|
||||
.expert-profile-box {
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
在新工单中引用
屏蔽一个用户