Files
msh-system/msh_single_uniapp/pages/ai-generate/inspiration.vue

1093 lines
29 KiB
Vue
Raw Normal View History

<template>
<view class="inspiration-container">
<!-- 顶部导航 -->
<view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-content">
<text class="nav-title">发现</text>
</view>
<!-- 分类标签 -->
<scroll-view scroll-x class="category-tabs" :show-scrollbar="false">
<view
v-for="(cat, index) in categories"
:key="index"
:class="['tab-item', selectedCategory === cat.value ? 'active' : '']"
@click="selectCategory(cat.value)"
>
<text class="tab-text">{{ cat.label }}</text>
</view>
</scroll-view>
</view>
<!-- 瀑布流内容 -->
<scroll-view
scroll-y
class="content-scroll"
:style="{ top: scrollViewTop, bottom: scrollViewBottom, height: scrollViewHeight }"
@scrolltolower="loadMore"
@scroll="onScroll"
>
<!-- AI生成内容标识 -->
<view class="ai-notice">
<text class="ai-notice-text">内容由AI生成</text>
</view>
<!-- 骨架屏加载 -->
<view class="skeleton-container" v-if="loading && leftColumnData.length === 0">
<view class="skeleton-waterfall">
<!-- 左列骨架 -->
<view class="skeleton-column">
<view class="skeleton-card" v-for="n in 3" :key="'left-' + n">
<view class="skeleton-image"></view>
<view class="skeleton-info">
<view class="skeleton-title"></view>
<view class="skeleton-meta">
<view class="skeleton-avatar"></view>
<view class="skeleton-text"></view>
</view>
</view>
</view>
</view>
<!-- 右列骨架 -->
<view class="skeleton-column">
<view class="skeleton-card" v-for="n in 3" :key="'right-' + n">
<view class="skeleton-image" :style="{ height: (n % 2 === 0 ? '200' : '150') + 'px' }"></view>
<view class="skeleton-info">
<view class="skeleton-title"></view>
<view class="skeleton-meta">
<view class="skeleton-avatar"></view>
<view class="skeleton-text"></view>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 实际内容 -->
<view class="waterfall-container" v-else>
<!-- 左列 -->
<view class="waterfall-column">
<view
v-for="(item, index) in leftColumnData"
:key="item.id"
class="inspiration-card"
@click="viewDetail(item)"
>
<!-- 图片 -->
<view class="card-image-wrapper">
<image
:src="item.image"
mode="widthFix"
class="card-image"
lazy-load
></image>
<view class="type-tag" :class="item.type === 'video' ? 'video' : 'image'">
{{ item.type === 'video' ? '视频' : '图文' }}
</view>
</view>
<!-- 信息 -->
<view class="card-info">
<text class="card-title">{{ item.title }}</text>
<view class="card-meta">
<view class="author-info">
<image
:src="item.author.avatar || item.authorAvatar || '/static/images/avatar-default.png'"
class="author-avatar"
mode="aspectFill"
@error="handleAvatarError($event, item)"
></image>
<text class="author-name">{{ item.author.name }}</text>
</view>
<view class="card-stats">
<text class="iconfont icon-aixin"></text>
<text class="stat-text">{{ formatNumber(item.likeCount) }}</text>
</view>
</view>
</view>
<!-- 对比按钮 -->
<view class="card-action" v-if="selectedCategory === 'comparison'">
<button class="action-btn" @click.stop="goToEffect(item)">对比</button>
</view>
</view>
</view>
<!-- 右列 -->
<view class="waterfall-column">
<view
v-for="(item, index) in rightColumnData"
:key="item.id"
class="inspiration-card"
@click="viewDetail(item)"
>
<!-- 图片 -->
<view class="card-image-wrapper">
<image
:src="item.image"
mode="widthFix"
class="card-image"
lazy-load
></image>
<view class="type-tag" :class="item.type === 'video' ? 'video' : 'image'">
{{ item.type === 'video' ? '视频' : '图文' }}
</view>
</view>
<!-- 信息 -->
<view class="card-info">
<text class="card-title">{{ item.title }}</text>
<view class="card-meta">
<view class="author-info">
<image
:src="item.author.avatar || item.authorAvatar || '/static/images/avatar-default.png'"
class="author-avatar"
mode="aspectFill"
@error="handleAvatarError($event, item)"
></image>
<text class="author-name">{{ item.author.name }}</text>
</view>
<view class="card-stats">
<text class="iconfont icon-aixin"></text>
<text class="stat-text">{{ formatNumber(item.likeCount) }}</text>
</view>
</view>
</view>
<!-- 对比按钮 -->
<view class="card-action" v-if="selectedCategory === 'comparison'">
<button class="action-btn" @click.stop="goToEffect(item)">对比</button>
</view>
</view>
</view>
</view>
<!-- 加载更多 -->
<view class="loading-more" v-if="loading">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 没有更多 -->
<view class="no-more" v-if="noMore">
<text class="no-more-text">没有更多了</text>
</view>
</scroll-view>
<!-- 底部导航栏 -->
<view class="bottom-tabbar">
<view
v-for="(tab, index) in tabBar"
:key="index"
:class="['tab-item', tab.active ? 'active' : '']"
@click="switchTab(tab.path)"
>
<!-- 想象图标 - 使用纯CSS绘制 -->
<view v-if="tab.label === '想象'" class="tab-icon-imagine">
<view class="snowflake">
<view class="snowflake-line snowflake-line-1"></view>
<view class="snowflake-line snowflake-line-2"></view>
<view class="snowflake-line snowflake-line-3"></view>
</view>
</view>
<!-- 字体图标 -->
<text v-else class="iconfont" :class="tab.icon"></text>
<text class="tab-label">{{ tab.label }}</text>
</view>
</view>
</view>
</template>
<script>
import api from '@/api/models-api.js'
export default {
data() {
return {
statusBarHeight: 0,
scrollViewHeight: '100vh',
scrollViewTop: '0px',
scrollViewBottom: '0px',
// 分类
selectedCategory: 'hot',
categories: [
{ label: '热门模版', value: 'hot', icon: '🔥' },
{ label: '设计案例', value: 'cases', icon: '✨' },
{ label: '装修前后', value: 'comparison', icon: '📷' },
{ label: '视频案例', value: 'videocases', icon: '' },
{ label: '营销活动', value: 'activity', icon: '' }
],
// 瀑布流数据
allData: [],
leftColumnData: [],
rightColumnData: [],
// 加载状态
loading: false,
noMore: false,
page: 1,
pageSize: 20,
// 底部导航
tabBar: [
{ label: '推荐', icon: 'icon-shouye', path: '/pages/index/index', active: false },
{ label: '想象', path: '/pages/ai-generate/index', active: true },
{ label: '智能体', icon: 'icon-kefujiedai', path: '/pages/ai-generate/agent', active: false },
{ label: '我的', icon: 'icon-wode', path: '/pages/user/index', active: false }
]
};
},
onLoad() {
this.initPage();
this.loadData();
},
methods: {
initPage() {
const systemInfo = uni.getSystemInfoSync();
this.statusBarHeight = systemInfo.statusBarHeight || 0;
// 计算滚动区域位置和高度
const navHeight = 44 + 50; // 导航(44px) + 分类标签(50px)
const tabbarHeight = 50; // 底部导航栏
// 设置 scroll-view 的位置
this.scrollViewTop = `${this.statusBarHeight + navHeight}px`;
this.scrollViewBottom = `${tabbarHeight}px`;
// 计算高度
const totalHeight = this.statusBarHeight + navHeight + tabbarHeight;
this.scrollViewHeight = `calc(100vh - ${totalHeight}px)`;
},
// 判断URL是否为图片格式
isImageUrl(url) {
if (!url) return false;
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg'];
const lowerUrl = (url.split('?')[0].split('#')[0] || '').toLowerCase();
return imageExtensions.some(ext => lowerUrl.endsWith(ext));
},
// 处理头像URL添加前缀
getAvatarUrl(avatarUrl) {
if (!avatarUrl || avatarUrl === '/static/images/avatar-default.png') {
return '/static/images/avatar-default.png';
}
// 如果已经是完整URL直接返回
if (avatarUrl.startsWith('http://') || avatarUrl.startsWith('https://')) {
return avatarUrl;
}
// 如果是相对路径,添加前缀
const prefix = 'https://uthink2025.oss-cn-shanghai.aliyuncs.com/';
// 如果URL已经以/开头,去掉开头的/
const cleanUrl = avatarUrl.startsWith('/') ? avatarUrl.substring(1) : avatarUrl;
return prefix + cleanUrl;
},
isVideoUrl(url) {
if (!url) return false;
const videoExtensions = ['.mp4', '.mov', '.m3u8', '.webm', '.avi', '.wmv', '.flv', '.mkv'];
const lowerUrl = (url.split('?')[0].split('#')[0] || '').toLowerCase();
return videoExtensions.some(ext => lowerUrl.endsWith(ext));
},
getArticleType(article) {
const url = article.videoUrl || '';
return this.isVideoUrl(url) ? 'video' : 'image';
},
// 加载数据
async loadData() {
if (this.loading || this.noMore) return;
this.loading = true;
try {
const params = {
page: this.page,
size: this.pageSize,
statusTask:1
};
// 如果是装修前后分类添加type参数
if (this.selectedCategory === 'comparison') {
params.type = 1;
} else if (this.selectedCategory === 'videocases') {
// 如果是视频案例分类添加type参数
params.type = 2;
}
const response = await api.searchArticles(params);
if (response.code === 200 && response.data) {
const articles = response.data.records || [];
// 转换文章数据为瀑布流数据格式
const newData = articles.map(article => {
// 先获取类型
const articleType = this.getArticleType(article);
return {
id: article.id,
title: article.title || '未命名作品',
// 如果类型为image使用videoUrl否则使用imageInput
image: articleType === 'image'
? (article.videoUrl || '/static/images/placeholder.png')
: (article.imageInput || '/static/images/placeholder.png'),
type: articleType, // 根据videoUrl后缀智能判断类型
articleType: article.type,
template: true, // 所有文章都可以作为模板使用
likeCount: parseInt(article.visit) || 0,
author: {
name: article.authorName || '创作者',
avatar: this.getAvatarUrl(article.authorAvatar) || '/static/images/avatar-default.png'
},
// 保留原始 authorAvatar 属性,方便直接访问(已处理前缀)
authorAvatar: this.getAvatarUrl(article.authorAvatar) || '/static/images/avatar-default.png',
// 额外的文章信息
imageInput: article.imageInput, // 添加 imageInput
prompt: article.prompt,
synopsis: article.synopsis,
videoUrl: article.videoUrl,
taskId: article.taskId,
aspectRatio: article.aspectRatio,
createTime: article.createTime
};
});
// 分配数据到瀑布流
this.distributeData(newData);
// 更新分页状态
if (newData.length < this.pageSize) {
this.noMore = true;
}
this.page++;
} else {
uni.showToast({
title: '加载失败',
icon: 'none'
});
}
} catch (error) {
console.error('加载文章列表失败:', error);
uni.showToast({
title: '网络错误,请重试',
icon: 'none'
});
} finally {
this.loading = false;
}
},
// 分配数据到左右列(瀑布流)
distributeData(newData) {
newData.forEach(item => {
// 简化算法:交替分配
if (this.leftColumnData.length <= this.rightColumnData.length) {
this.leftColumnData.push(item);
} else {
this.rightColumnData.push(item);
}
});
},
// 选择分类
selectCategory(value) {
this.selectedCategory = value;
// 重置数据
this.leftColumnData = [];
this.rightColumnData = [];
this.page = 1;
this.noMore = false;
this.loadData();
},
// 滚动事件
onScroll(e) {
// 可以添加滚动逻辑,如返回顶部按钮显示等
},
// 加载更多
loadMore() {
this.loadData();
},
// 查看详情
viewDetail(item) {
console.log('点击文章详情:', item);
// 根据类型跳转到不同页面
if (item.type === 'video' && item.videoUrl) {
uni.navigateTo({
url: `/pages/ai-generate/video?id=${item.id}`
});
} else if (item.type === 'image') {
uni.navigateTo({
url: `/pages/ai-generate/image?id=${item.id}`
});
} else {
uni.showToast({
title: `查看${item.title}`,
icon: 'none'
});
}
},
// 跳转到对比效果页
goToEffect(item) {
const beforeImage = encodeURIComponent(item.imageInput || '');
const afterImage = encodeURIComponent(item.videoUrl || '');
const promptText = encodeURIComponent(item.prompt || item.synopsis || '');
uni.navigateTo({
url: `/pages/ai-generate/effect?id=${item.id}&beforeImage=${beforeImage}&afterImage=${afterImage}&promptText=${promptText}`
});
},
// 使用模板
useTemplate(item) {
uni.showModal({
title: '使用模板',
content: '是否使用此模板生成图片?',
success: (res) => {
if (res.confirm) {
// 跳转到生成页面,并传递模板信息
uni.navigateTo({
url: `/pages/ai-generate/index?templateId=${item.id}`
});
}
}
});
},
// 跳转搜索
goToSearch() {
uni.navigateTo({
url: '/pages/ai-generate/search'
});
},
// 切换Tab
switchTab(path) {
if (path === '/pages/ai-generate/inspiration') {
return; // 当前页面
}
// 推荐页首页和我的页使用switchTab
if (path === '/pages/index/index' || path === '/pages/user/index') {
this.tabBar = this.tabBar.map(t => ({ ...t, active: t.path === path }))
uni.switchTab({ url: path });
} else {
// 想象页AI生成使用navigateTo
this.tabBar = this.tabBar.map(t => ({ ...t, active: t.path === path }))
uni.navigateTo({ url: path });
}
},
// 格式化数字
formatNumber(num) {
if (num >= 10000) {
return (num / 10000).toFixed(1) + 'w';
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'k';
}
return num.toString();
},
// 处理头像加载错误
handleAvatarError(e, item) {
console.log('头像加载失败,重置为默认头像:', e);
if (item) {
if (item.author) {
item.author.avatar = '/static/images/avatar-default.png';
}
item.authorAvatar = '/static/images/avatar-default.png';
}
}
}
};
</script>
<style lang="scss" scoped>
.inspiration-container {
width: 100%;
min-height: 100vh;
background: linear-gradient(180deg, #000000 0%, #1a1a1a 100%);
overflow-x: hidden;
}
// 顶部导航
.nav-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
background: rgba(10, 14, 26, 0.95);
backdrop-filter: blur(20px);
z-index: 999;
.nav-content {
display: flex;
align-items: center;
justify-content: center;
height: 44px;
padding: 0 16px;
position: relative;
.nav-title {
font-size: 16px;
font-weight: 600;
color: #ffffff;
text-align: center;
}
.nav-right {
position: absolute;
right: 16px;
.search-btn {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
.iconfont {
font-size: 20px;
color: #ffffff;
}
}
}
}
// 分类标签
.category-tabs {
position: relative;
white-space: nowrap;
padding: 0 16px 12px;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 0px;
background: linear-gradient(90deg, rgba(255,255,255,0.18), rgba(255,255,255,0.06), rgba(255,255,255,0.18));
opacity: 0.6;
pointer-events: none;
}
.tab-item {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 6px 16px;
margin-right: 12px; margin-top: 2px;
background: linear-gradient(180deg, rgba(50, 56, 72, 0.55) 0%, rgba(36, 40, 54, 0.65) 100%);
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.06);
box-shadow: inset 0 1px 2px rgba(255,255,255,0.12), 0 4px 8px rgba(0,0,0,0.25);
transition: all 180ms ease;
.tab-icon {
font-size: 14px;
}
.tab-text {
font-size: 14px;
color: #8f9bb3;
transition: color 180ms ease, text-shadow 180ms ease;
}
&.active {
background: radial-gradient(circle at 50% 20%, rgba(255,255,255,0.16) 0%, rgba(255,255,255,0.06) 45%, rgba(255,255,255,0) 62%), linear-gradient(180deg, rgba(255,255,255,0.08) 0%, rgba(255,255,255,0.02) 100%);
border-color: #42ca4d;
box-shadow: inset 0 2px 6px rgba(255,255,255,0.12), 0 6px 12px rgba(0,0,0,0.35);
transform: translateY(-1px);
.tab-text {
color: #42ca4d;
text-shadow: 0 0 8px rgba(66, 202, 77, 0.45);
}
}
}
}
}
// 内容滚动区
.content-scroll {
position: fixed;
left: 0;
right: 0;
padding: 0px 12px;
box-sizing: border-box;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
// AI生成内容标识 - 在滚动区域内
.ai-notice {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 6px 16px;
margin: 0 0 16px 0;
background: rgba(66, 202, 77, 0.1);
border: 1px solid rgba(66, 202, 77, 0.3);
border-radius: 8px;
backdrop-filter: blur(10px);
.ai-notice-icon {
font-size: 14px;
}
.ai-notice-text {
font-size: 11px;
color: #42ca4d;
line-height: 1.4;
}
}
}
// 骨架屏容器
.skeleton-container {
width: 100%;
padding: 0;
}
.skeleton-waterfall {
display: flex;
width: 100%;
gap: 12px;
.skeleton-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
}
}
.skeleton-card {
width: 100%;
background: rgba(26, 31, 46, 0.8);
border-radius: 16px;
overflow: hidden;
.skeleton-image {
width: 100%;
height: 180px;
background: linear-gradient(90deg,
rgba(255, 255, 255, 0.05) 25%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0.05) 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
.skeleton-info {
padding: 12px;
.skeleton-title {
width: 80%;
height: 16px;
background: linear-gradient(90deg,
rgba(255, 255, 255, 0.05) 25%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0.05) 75%);
background-size: 200% 100%;
border-radius: 4px;
margin-bottom: 12px;
animation: shimmer 1.5s infinite;
animation-delay: 0.1s;
}
.skeleton-meta {
display: flex;
align-items: center;
gap: 8px;
.skeleton-avatar {
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(90deg,
rgba(255, 255, 255, 0.05) 25%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0.05) 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
animation-delay: 0.2s;
}
.skeleton-text {
flex: 1;
height: 12px;
background: linear-gradient(90deg,
rgba(255, 255, 255, 0.05) 25%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0.05) 75%);
background-size: 200% 100%;
border-radius: 4px;
animation: shimmer 1.5s infinite;
animation-delay: 0.3s;
}
}
}
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
// 瀑布流容器
.waterfall-container {
display: flex;
width: 100%; margin-top: 30rpx;
gap: 12px;
align-items: flex-start;
justify-content: space-between;
.waterfall-column {
flex: 1;
min-width: 0;
max-width: calc(50% - 6px);
display: flex;
flex-direction: column;
gap: 12px;
}
}
// 灵感卡片
.inspiration-card {
width: 100%;
background: rgba(26, 31, 46, 0.8);
border-radius: 6px;
overflow: hidden;
break-inside: avoid;
.card-image-wrapper {
position: relative;
width: 100%;
overflow: hidden;
background: rgba(0, 0, 0, 0.2);
.card-image {
width: 100%;
height: auto;
min-height: 100px;
display: block;
// border-radius: 16px 16px 0 0;
}
.media-tag {
position: absolute;
top: 8px;
right: 8px;
width: 28px;
height: 28px;
background: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(10px);
.iconfont {
font-size: 14px;
color: #ffffff;
}
}
.type-tag {
position: absolute;
top: 8px;
left: 8px;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
line-height: 18px;
color: #ffffff;
background: rgba(0,0,0,0.5);
border: 1px solid rgba(255,255,255,0.25);
backdrop-filter: blur(8px);
&.video {
border-color: rgba(33,150,243,0.5);
}
&.image {
border-color: rgba(66,202,77,0.5);
}
}
}
.card-info {
padding: 12px;
.card-title {
display: block;
font-size: 14px;
color: #ffffff;
margin-bottom: 8px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
line-clamp: 2;
word-break: break-all;
}
.card-meta {
display: flex;
align-items: center;
justify-content: space-between;
.author-info {
display: flex;
align-items: center;
gap: 6px;
flex: 1;
min-width: 0;
.author-avatar {
width: 20px;
height: 20px;
border-radius: 50%;
flex-shrink: 0;
}
.author-name {
font-size: 12px;
color: #8f9bb3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.card-stats {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
.iconfont {
font-size: 14px;
color: #8f9bb3;
}
.stat-text {
font-size: 12px;
color: #8f9bb3;
}
}
}
}
.card-action {
padding: 0 12px 12px;
.action-btn {
width: 100%;
height: 32px;
background: linear-gradient(135deg, #42ca4d 0%, #38b045 100%);
border-radius: 8px;
font-size: 13px;
color: #ffffff;
border: none;
line-height: 32px;
}
}
}
// 加载更多
.loading-more {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 20px;
width: 100%;
clear: both;
.loading-spinner {
width: 20px;
height: 20px;
border: 2px solid rgba(255, 255, 255, 0.2);
border-top-color: #42ca4d;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.loading-text {
font-size: 13px;
color: #8f9bb3;
}
}
// 没有更多
.no-more {
padding: 20px;
text-align: center;
width: 100%;
clear: both;
.no-more-text {
font-size: 13px;
color: #8f9bb3;
}
}
// 底部导航栏
.bottom-tabbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
height: calc(50px + env(safe-area-inset-bottom));
box-sizing: border-box;
align-items: center;
background: linear-gradient(180deg, rgba(20, 24, 34, 0.96) 0%, rgba(10, 12, 18, 0.98) 100%);
backdrop-filter: blur(20px);
border-top: 1px solid rgba(255, 255, 255, 0.06);
padding-bottom: env(safe-area-inset-bottom);
z-index: 999;
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.35), 0 -12px 24px rgba(0, 0, 0, 0.25);
position: fixed;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, rgba(255,255,255,0.18), rgba(255,255,255,0.06), rgba(255,255,255,0.18));
opacity: 0.6;
pointer-events: none;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
border-radius: 12px;
padding: 2px 6px; margin:0px 20px;
transition: all 180ms ease;
position: relative;
.iconfont {
font-size: 22px;
color: #8f9bb3;
transition: color 180ms ease, transform 180ms ease, text-shadow 180ms ease;
}
.tab-icon-imagine {
width: 22px;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
.snowflake {
width: 22px;
height: 22px;
position: relative;
.snowflake-line {
position: absolute;
top: 50%;
left: 50%;
width: 18px;
height: 2px;
background: #8f9bb3;
transform-origin: center;
&::before,
&::after {
content: '';
position: absolute;
width: 6px;
height: 2px;
background: #8f9bb3;
}
&::before {
top: -4px;
left: 2px;
transform: rotate(-45deg);
}
&::after {
top: 4px;
left: 2px;
transform: rotate(45deg);
}
}
.snowflake-line-1 {
transform: translate(-50%, -50%) rotate(0deg);
}
.snowflake-line-2 {
transform: translate(-50%, -50%) rotate(60deg);
}
.snowflake-line-3 {
transform: translate(-50%, -50%) rotate(120deg);
}
}
}
.tab-label {
font-size: 10px;
color: #8f9bb3;
transition: color 180ms ease, text-shadow 180ms ease;
}
&.active {
background: radial-gradient(circle at 50% 20%, rgba(255,255,255,0.16) 0%, rgba(255,255,255,0.06) 42%, rgba(255,255,255,0) 60%),
linear-gradient(180deg, rgba(255,255,255,0.10) 0%, rgba(255,255,255,0.02) 100%);
box-shadow: inset 0 2px 6px rgba(255,255,255,0.12), 0 6px 12px rgba(0,0,0,0.35);
border: 1px solid rgba(255,255,255,0.08);
transform: translateY(-1px);
.iconfont,
.tab-label {
color: #42ca4d;
text-shadow: 0 0 8px rgba(66, 202, 77, 0.45);
}
.tab-icon-imagine {
.snowflake-line {
background: #42ca4d;
&::before,
&::after {
background: #42ca4d;
}
}
}
}
}
}
// 动画
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>