978 lines
23 KiB
Vue
978 lines
23 KiB
Vue
<template>
|
|
<div class="page-header-wrapper-grid-content-main custom-scrollbar">
|
|
<!-- 数据统计卡片 -->
|
|
<div class="data-stats animate-fade-in-up">
|
|
<div class="stat-card glow-border">
|
|
<div class="stat-number">{{ articleCount }}</div>
|
|
<div class="stat-title">发布文章</div>
|
|
</div>
|
|
<div class="stat-card glow-border">
|
|
<div class="stat-number">{{ viewCount }}</div>
|
|
<div class="stat-title">文章浏览</div>
|
|
</div>
|
|
<div class="stat-card glow-border">
|
|
<div class="stat-number">{{ likeCount }}</div>
|
|
<div class="stat-title">获得点赞</div>
|
|
</div>
|
|
<div class="stat-card glow-border">
|
|
<div class="stat-number">{{ matchs.length }}</div>
|
|
<div class="stat-title">参与比赛</div>
|
|
</div>
|
|
</div>
|
|
|
|
<a-row :gutter="24">
|
|
<a-col :md="24" :lg="8">
|
|
<a-card
|
|
:bordered="false"
|
|
class="hover-card animate-fade-in-up"
|
|
style="animation-delay: 0.1s"
|
|
>
|
|
<div class="account-center-avatarHolder">
|
|
<div class="avatar">
|
|
<img
|
|
src="https://onlinephoto.oss-cn-chengdu.aliyuncs.com/hangtian/touxiang.jpg"
|
|
data-src="//www.cmstui.com/wp-content/themes/zibll/img/avatar-default.png"
|
|
class="lazyload avatar avatar-id-1"
|
|
alt="用户头像"
|
|
/>
|
|
</div>
|
|
<div class="username">{{ $store.state.user.userName }}</div>
|
|
<div class="bio">{{ shenfen }}</div>
|
|
</div>
|
|
|
|
<a-divider />
|
|
|
|
<div class="account-center-tags">
|
|
<div class="tagsTitle">个人标签</div>
|
|
<div>
|
|
<template v-for="(tag, index) in tags">
|
|
<a-tooltip v-if="tag.length > 20" :key="tag" :title="tag">
|
|
<a-tag
|
|
:key="tag"
|
|
:closable="index !== 0"
|
|
@close="() => handleTagClose(tag)"
|
|
>{{ `${tag.slice(0, 20)}...` }}</a-tag
|
|
>
|
|
</a-tooltip>
|
|
<a-tag
|
|
v-else
|
|
:key="tag"
|
|
:closable="index !== 0"
|
|
@close="() => handleTagClose(tag)"
|
|
>{{ tag }}</a-tag
|
|
>
|
|
</template>
|
|
<a-input
|
|
v-if="tagInputVisible"
|
|
ref="tagInput"
|
|
type="text"
|
|
size="small"
|
|
:style="{ width: '78px' }"
|
|
:value="tagInputValue"
|
|
@change="handleInputChange"
|
|
@blur="handleTagInputConfirm"
|
|
@keyup.enter="handleTagInputConfirm"
|
|
/>
|
|
<a-tag
|
|
v-else
|
|
@click="showTagInput"
|
|
style="background: #fff; borderstyle: dashed"
|
|
>
|
|
<a-icon type="plus" />添加标签
|
|
</a-tag>
|
|
</div>
|
|
</div>
|
|
|
|
<a-divider :dashed="true" />
|
|
|
|
<div class="account-center-team">
|
|
<div class="teamTitle">我的比赛</div>
|
|
<a-empty v-if="matchs.length === 0" description="暂无参赛记录" />
|
|
<a-spin :spinning="teamSpinning">
|
|
<div class="members" v-for="(item, index) in matchs" :key="index">
|
|
<a-card
|
|
class="match-card"
|
|
:bordered="false"
|
|
:bodyStyle="{ padding: '12px' }"
|
|
>
|
|
<div class="match-title">
|
|
<a-icon
|
|
type="trophy"
|
|
:style="{ color: colors[index % colors.length] }"
|
|
/>
|
|
<span>{{ item.competitionName }}</span>
|
|
</div>
|
|
<div class="match-info">
|
|
<a-tag :color="colors[index % colors.length]">
|
|
{{ item.competitionType }}
|
|
</a-tag>
|
|
<span class="match-date">{{
|
|
formatDate(item.registrationTime)
|
|
}}</span>
|
|
</div>
|
|
</a-card>
|
|
</div>
|
|
</a-spin>
|
|
</div>
|
|
</a-card>
|
|
</a-col>
|
|
|
|
<a-col :md="24" :lg="16">
|
|
<a-card
|
|
class="animate-fade-in-up"
|
|
style="animation-delay: 0.2s; width: 100%"
|
|
:bordered="false"
|
|
:tabList="tabListNoTitle"
|
|
:activeTabKey="noTitleKey"
|
|
@tabChange="(key) => handleTabChange(key, 'noTitleKey')"
|
|
>
|
|
<div v-if="noTitleKey === 'article'">
|
|
<!-- 文章筛选和排序 -->
|
|
<div class="article-filter">
|
|
<a-radio-group
|
|
v-model="articleFilter"
|
|
buttonStyle="solid"
|
|
@change="filterArticles"
|
|
>
|
|
<a-radio-button value="all">全部文章</a-radio-button>
|
|
<a-radio-button value="hot">热门文章</a-radio-button>
|
|
<a-radio-button value="recent">最近发布</a-radio-button>
|
|
</a-radio-group>
|
|
|
|
<a-button type="primary" icon="plus" @click="createNewArticle">
|
|
发布文章
|
|
</a-button>
|
|
</div>
|
|
|
|
<!-- 文章列表 -->
|
|
<div class="article-list">
|
|
<a-empty v-if="articles.length === 0" description="暂无文章" />
|
|
<div v-else>
|
|
<div
|
|
class="article-item"
|
|
v-for="(article, index) in articles"
|
|
:key="index"
|
|
@click="viewArticle(article.id)"
|
|
>
|
|
<div class="article-cover">
|
|
<img :src="getArticleImage(article)" :alt="article.title" />
|
|
</div>
|
|
<div class="article-content">
|
|
<div class="article-title">{{ article.articleTitle }}</div>
|
|
<div
|
|
class="article-description"
|
|
v-html="getArticleExcerpt(article.articleContent)"
|
|
></div>
|
|
<div class="article-meta">
|
|
<div class="article-date">
|
|
<a-icon type="calendar" />
|
|
{{ formatDate(article.publishTime) }}
|
|
</div>
|
|
<div class="article-stats">
|
|
<span
|
|
><a-icon type="eye" />
|
|
{{ article.viewCount || 0 }}</span
|
|
>
|
|
<span style="margin-left: 16px"
|
|
><a-icon type="like" />
|
|
{{ article.likeCount || 0 }}</span
|
|
>
|
|
<span style="margin-left: 16px"
|
|
><a-icon type="message" />
|
|
{{ article.commentCount || 0 }}</span
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 分页 -->
|
|
<div class="pagination-container" v-if="articles.length > 0">
|
|
<a-pagination
|
|
:current="current"
|
|
:pageSize="pageSize"
|
|
:total="total"
|
|
@change="handlePageChange"
|
|
showQuickJumper
|
|
:showSizeChanger="true"
|
|
:pageSizeOptions="['6', '12', '24', '36']"
|
|
@showSizeChange="onShowSizeChange"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 其他标签页内容 -->
|
|
<div v-else-if="noTitleKey === 'app'">
|
|
<a-empty description="功能开发中..." />
|
|
</div>
|
|
|
|
<div v-else-if="noTitleKey === 'project'">
|
|
<a-empty description="功能开发中..." />
|
|
</div>
|
|
</a-card>
|
|
</a-col>
|
|
</a-row>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import PageView from "../../../layouts/PageView.vue";
|
|
import RouteView from "../../../layouts/RouteView.vue";
|
|
import { AppPage, ArticlePage, ProjectPage } from "./page";
|
|
import { mapGetters } from "vuex";
|
|
|
|
export default {
|
|
components: {
|
|
RouteView,
|
|
PageView,
|
|
AppPage,
|
|
ArticlePage,
|
|
ProjectPage,
|
|
},
|
|
data() {
|
|
return {
|
|
userimg: this.$store.state.avatar,
|
|
tags: ["技术爱好者", "前端开发", "生活记录"],
|
|
tagInputVisible: false,
|
|
tagInputValue: "",
|
|
teamSpinning: true,
|
|
shenfen: "",
|
|
tabListNoTitle: [
|
|
{
|
|
key: "article",
|
|
tab: "我的文章",
|
|
},
|
|
{
|
|
key: "app",
|
|
tab: "我的收藏",
|
|
},
|
|
{
|
|
key: "project",
|
|
tab: "我的评论",
|
|
},
|
|
],
|
|
noTitleKey: "article",
|
|
matchs: [],
|
|
colors: ["#f50", "#2db7f5", "#87d068", "#108ee9", "#722ed1"],
|
|
|
|
// 文章相关数据
|
|
articles: [],
|
|
current: 1,
|
|
pageSize: 6,
|
|
total: 0,
|
|
articleFilter: "all",
|
|
|
|
// 统计数据
|
|
articleCount: 0,
|
|
viewCount: 0,
|
|
likeCount: 0,
|
|
};
|
|
},
|
|
computed: {
|
|
...mapGetters(["nickname", "avatar"]),
|
|
},
|
|
mounted() {
|
|
// 设置用户身份
|
|
if (this.$store.state.user.userPrivileges == 0) {
|
|
this.shenfen = "管理员";
|
|
} else if (this.$store.state.user.userPrivileges == 1) {
|
|
this.shenfen = "教师";
|
|
} else {
|
|
this.shenfen = "学生";
|
|
}
|
|
|
|
// 获取比赛信息
|
|
this.fetchCompetitions();
|
|
|
|
// 获取文章列表
|
|
this.fetchArticles();
|
|
|
|
// 获取统计数据
|
|
this.fetchStats();
|
|
},
|
|
methods: {
|
|
// 获取比赛信息
|
|
fetchCompetitions() {
|
|
this.teamSpinning = true;
|
|
this.$api
|
|
.RegistrationAll()
|
|
.then((res) => {
|
|
this.matchs = res.data.filter((match) => {
|
|
return match.studentId == this.$store.state.user.userId;
|
|
});
|
|
this.teamSpinning = false;
|
|
})
|
|
.catch((err) => {
|
|
console.error("获取比赛信息失败:", err);
|
|
this.teamSpinning = false;
|
|
});
|
|
},
|
|
|
|
// 获取文章列表
|
|
fetchArticles() {
|
|
this.$api
|
|
.AllArticle()
|
|
.then((res) => {
|
|
// 过滤出当前用户的文章
|
|
const userArticles = res.data.filter((article) => {
|
|
return article.userId === this.$store.state.user.userId;
|
|
});
|
|
|
|
// 根据筛选条件排序
|
|
this.filterArticlesByCondition(userArticles);
|
|
|
|
// 设置总数
|
|
this.total = userArticles.length;
|
|
this.articleCount = userArticles.length;
|
|
|
|
// 计算总浏览量和点赞数
|
|
this.calculateStats(userArticles);
|
|
})
|
|
.catch((err) => {
|
|
console.error("获取文章列表失败:", err);
|
|
});
|
|
},
|
|
|
|
// 根据筛选条件过滤文章
|
|
filterArticlesByCondition(articles) {
|
|
let filteredArticles = [...articles];
|
|
|
|
// 应用筛选条件
|
|
switch (this.articleFilter) {
|
|
case "hot":
|
|
// 按热度(浏览量)排序
|
|
filteredArticles.sort(
|
|
(a, b) => (b.viewCount || 0) - (a.viewCount || 0)
|
|
);
|
|
break;
|
|
case "recent":
|
|
// 按发布时间排序
|
|
filteredArticles.sort(
|
|
(a, b) => new Date(b.publishTime) - new Date(a.publishTime)
|
|
);
|
|
break;
|
|
default:
|
|
// 默认按发布时间排序
|
|
filteredArticles.sort(
|
|
(a, b) => new Date(b.publishTime) - new Date(a.publishTime)
|
|
);
|
|
}
|
|
|
|
// 分页处理
|
|
const startIndex = (this.current - 1) * this.pageSize;
|
|
const endIndex = startIndex + this.pageSize;
|
|
this.articles = filteredArticles.slice(startIndex, endIndex);
|
|
},
|
|
|
|
// 计算统计数据
|
|
calculateStats(articles) {
|
|
this.viewCount = articles.reduce(
|
|
(sum, article) => sum + (article.viewCount || 0),
|
|
0
|
|
);
|
|
this.likeCount = articles.reduce(
|
|
(sum, article) => sum + (article.likeCount || 0),
|
|
0
|
|
);
|
|
},
|
|
|
|
// 获取统计数据
|
|
fetchStats() {
|
|
// 如果有专门的API可以在这里调用
|
|
},
|
|
|
|
// 筛选文章
|
|
filterArticles() {
|
|
this.current = 1; // 重置页码
|
|
this.fetchArticles();
|
|
},
|
|
|
|
// 分页处理
|
|
handlePageChange(page) {
|
|
this.current = page;
|
|
this.fetchArticles();
|
|
},
|
|
|
|
// 每页显示数量变化
|
|
onShowSizeChange(current, size) {
|
|
this.current = 1;
|
|
this.pageSize = size;
|
|
this.fetchArticles();
|
|
},
|
|
|
|
// 获取文章封面图
|
|
getArticleImage(article) {
|
|
if (!article.articleContent) return require("@/assets/bg.svg");
|
|
|
|
// 从文章内容中提取图片
|
|
const imageRegex = /(http[s]?:\/\/[^(\s|")]+\.(png|jpg|jpeg|gif|webp))/gi;
|
|
const matches = article.articleContent.match(imageRegex);
|
|
|
|
// 如果找到图片,返回第一张
|
|
if (matches && matches.length > 0) {
|
|
return matches[0];
|
|
}
|
|
|
|
const randomSeed = Math.floor(Math.random() * 10000);
|
|
|
|
// 如果没有找到匹配的图片,可以使用随机图片
|
|
return "https://tu.ltyuanfang.cn/api/fengjing.php?" + randomSeed;
|
|
},
|
|
|
|
// 获取文章摘要
|
|
getArticleExcerpt(content) {
|
|
if (!content) return "";
|
|
|
|
// 移除HTML标签
|
|
const plainText = content.replace(/<[^>]+>/g, "");
|
|
|
|
// 截取前100个字符作为摘要
|
|
return (
|
|
plainText.substring(0, 100) + (plainText.length > 100 ? "..." : "")
|
|
);
|
|
},
|
|
|
|
// 格式化日期
|
|
formatDate(dateString) {
|
|
if (!dateString) return "";
|
|
|
|
const date = new Date(dateString);
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
|
|
2,
|
|
"0"
|
|
)}-${String(date.getDate()).padStart(2, "0")}`;
|
|
},
|
|
|
|
// 查看文章详情
|
|
viewArticle(id) {
|
|
this.$router.push(`/community/pages?id=${id}`);
|
|
},
|
|
|
|
// 创建新文章
|
|
createNewArticle() {
|
|
this.$router.push("/write");
|
|
},
|
|
|
|
// 标签相关方法
|
|
handleTabChange(key, type) {
|
|
this[type] = key;
|
|
},
|
|
|
|
handleTagClose(removeTag) {
|
|
const tags = this.tags.filter((tag) => tag !== removeTag);
|
|
this.tags = tags;
|
|
},
|
|
|
|
showTagInput() {
|
|
this.tagInputVisible = true;
|
|
this.$nextTick(() => {
|
|
this.$refs.tagInput.focus();
|
|
});
|
|
},
|
|
|
|
handleInputChange(e) {
|
|
this.tagInputValue = e.target.value;
|
|
},
|
|
|
|
handleTagInputConfirm() {
|
|
const inputValue = this.tagInputValue;
|
|
let tags = this.tags;
|
|
if (inputValue && !tags.includes(inputValue)) {
|
|
tags = [...tags, inputValue];
|
|
}
|
|
|
|
Object.assign(this, {
|
|
tags,
|
|
tagInputVisible: false,
|
|
tagInputValue: "",
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
/* 论坛个人中心美化样式 */
|
|
|
|
/* 整体页面背景 */
|
|
.page-header-wrapper-grid-content-main {
|
|
width: 100%;
|
|
min-height: 100vh;
|
|
background-color: #f6f8fa;
|
|
background-image: linear-gradient(
|
|
to bottom,
|
|
rgba(240, 244, 248, 0.8),
|
|
rgba(255, 255, 255, 0.8)
|
|
);
|
|
padding: 24px;
|
|
}
|
|
|
|
/* 卡片通用样式 */
|
|
.ant-card {
|
|
border-radius: 16px;
|
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.06);
|
|
overflow: hidden;
|
|
transition: all 0.4s ease;
|
|
margin-bottom: 24px;
|
|
border: none;
|
|
}
|
|
|
|
.ant-card:hover {
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
|
transform: translateY(-5px);
|
|
}
|
|
|
|
/* 左侧个人信息卡片 */
|
|
.account-center-avatarHolder {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 32px 0 16px;
|
|
position: relative;
|
|
}
|
|
|
|
/* 背景装饰 */
|
|
.account-center-avatarHolder::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 120px;
|
|
background: linear-gradient(135deg, #3690cf, #2a78b8);
|
|
z-index: 0;
|
|
border-radius: 16px 16px 50% 50% / 16px 16px 25% 25%;
|
|
}
|
|
|
|
/* 头像样式 */
|
|
.account-center-avatarHolder .avatar {
|
|
width: 120px;
|
|
height: 120px;
|
|
border-radius: 50%;
|
|
margin-bottom: 24px;
|
|
border: 5px solid #fff;
|
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
|
overflow: hidden;
|
|
position: relative;
|
|
z-index: 1;
|
|
background: #fff;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.account-center-avatarHolder .avatar:hover {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.account-center-avatarHolder .avatar img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
transition: all 0.5s ease;
|
|
}
|
|
|
|
.account-center-avatarHolder .avatar:hover img {
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
/* 用户名样式 */
|
|
.account-center-avatarHolder .username {
|
|
color: #2c3e50;
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
margin-bottom: 8px;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* 身份标签 */
|
|
.account-center-avatarHolder .bio {
|
|
display: inline-block;
|
|
padding: 4px 12px;
|
|
background: rgba(54, 144, 207, 0.1);
|
|
color: #3690cf;
|
|
border-radius: 20px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
margin-bottom: 8px;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* 分割线美化 */
|
|
.ant-divider {
|
|
margin: 24px 0;
|
|
border-color: rgba(0, 0, 0, 0.06);
|
|
}
|
|
|
|
.ant-divider-dashed {
|
|
border-style: dashed;
|
|
border-color: rgba(0, 0, 0, 0.09);
|
|
}
|
|
|
|
/* 标签区域 */
|
|
.account-center-tags {
|
|
padding: 0 16px;
|
|
}
|
|
|
|
.account-center-tags .tagsTitle {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
margin-bottom: 16px;
|
|
position: relative;
|
|
padding-left: 12px;
|
|
}
|
|
|
|
.account-center-tags .tagsTitle::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
height: 100%;
|
|
width: 4px;
|
|
background: #3690cf;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
/* 标签样式 */
|
|
.ant-tag {
|
|
margin: 0 8px 8px 0;
|
|
padding: 4px 12px;
|
|
border-radius: 16px;
|
|
font-size: 13px;
|
|
border: none;
|
|
background: #f0f2f5;
|
|
color: #555;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.ant-tag:hover {
|
|
background: #e6f7ff;
|
|
color: #3690cf;
|
|
}
|
|
|
|
/* 新增标签按钮 */
|
|
.ant-tag-dashed {
|
|
border: 1px dashed #d9d9d9;
|
|
background: transparent;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.ant-tag-dashed:hover {
|
|
border-color: #3690cf;
|
|
color: #3690cf;
|
|
}
|
|
|
|
/* 比赛区域 */
|
|
.account-center-team {
|
|
padding: 0 16px;
|
|
}
|
|
|
|
.account-center-team .teamTitle {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
margin-bottom: 16px;
|
|
position: relative;
|
|
padding-left: 12px;
|
|
}
|
|
|
|
.account-center-team .teamTitle::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
height: 100%;
|
|
width: 4px;
|
|
background: #3690cf;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
/* 比赛卡片样式 */
|
|
.members {
|
|
margin-bottom: 16px;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.members .ant-tag {
|
|
display: inline-block;
|
|
margin: 5px;
|
|
padding: 6px 12px;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.members .ant-tag:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
}
|
|
|
|
/* 右侧选项卡样式 */
|
|
.ant-tabs-bar {
|
|
border-bottom: none;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.ant-tabs-nav {
|
|
margin: 0 auto 16px;
|
|
width: fit-content;
|
|
background: #f5f5f5;
|
|
border-radius: 50px;
|
|
padding: 4px;
|
|
}
|
|
|
|
.ant-tabs-tab {
|
|
margin: 0 !important;
|
|
padding: 8px 24px !important;
|
|
border-radius: 50px !important;
|
|
transition: all 0.3s;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.ant-tabs-tab-active {
|
|
background: #3690cf !important;
|
|
color: white !important;
|
|
}
|
|
|
|
/* 文章列表样式 */
|
|
.article-list {
|
|
padding: 16px;
|
|
}
|
|
|
|
.article-item {
|
|
display: flex;
|
|
margin-bottom: 20px;
|
|
background: white;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.article-item:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.article-cover {
|
|
width: 200px;
|
|
height: 134px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.article-cover img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
transition: transform 0.5s;
|
|
}
|
|
|
|
.article-item:hover .article-cover img {
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
.article-content {
|
|
flex: 1;
|
|
padding: 16px;
|
|
position: relative;
|
|
}
|
|
|
|
.article-title {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
margin-bottom: 8px;
|
|
color: #333;
|
|
}
|
|
|
|
.article-description {
|
|
font-size: 14px;
|
|
color: #666;
|
|
margin-bottom: 16px;
|
|
line-height: 1.6;
|
|
display: -webkit-box;
|
|
-webkit-box-orient: vertical;
|
|
-webkit-line-clamp: 2;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.article-meta {
|
|
position: absolute;
|
|
bottom: 16px;
|
|
left: 16px;
|
|
right: 16px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
font-size: 12px;
|
|
color: #999;
|
|
}
|
|
|
|
/* 数据统计卡片 */
|
|
.data-stats {
|
|
display: flex;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.stat-card {
|
|
flex: 1;
|
|
background: white;
|
|
border-radius: 12px;
|
|
padding: 16px;
|
|
text-align: center;
|
|
margin: 0 8px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.stat-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.stat-number {
|
|
font-size: 32px;
|
|
font-weight: 600;
|
|
color: #3690cf;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.stat-title {
|
|
font-size: 14px;
|
|
color: #666;
|
|
}
|
|
|
|
/* 响应式调整 */
|
|
@media (max-width: 768px) {
|
|
.article-cover {
|
|
width: 120px;
|
|
}
|
|
|
|
.stat-card {
|
|
padding: 12px;
|
|
}
|
|
|
|
.stat-number {
|
|
font-size: 24px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.article-item {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.article-cover {
|
|
width: 100%;
|
|
height: 180px;
|
|
}
|
|
|
|
.data-stats {
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.stat-card {
|
|
flex: 0 0 calc(50% - 16px);
|
|
margin-bottom: 16px;
|
|
}
|
|
}
|
|
|
|
/* 悬浮效果 */
|
|
.hover-card {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.hover-card::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(255, 255, 255, 0.1);
|
|
transform: translateX(-100%) rotate(45deg);
|
|
transition: transform 0.6s;
|
|
}
|
|
|
|
.hover-card:hover::after {
|
|
transform: translateX(100%) rotate(45deg);
|
|
}
|
|
|
|
/* 动画效果 */
|
|
@keyframes fadeInUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.animate-fade-in-up {
|
|
animation: fadeInUp 0.6s ease forwards;
|
|
}
|
|
|
|
/* 更多自定义样式 */
|
|
.custom-scrollbar::-webkit-scrollbar {
|
|
width: 6px;
|
|
height: 6px;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-track {
|
|
background: #f1f1f1;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
background: #c1c1c1;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
background: #3690cf;
|
|
}
|
|
|
|
/* 闪光边框效果 */
|
|
.glow-border {
|
|
position: relative;
|
|
}
|
|
|
|
.glow-border::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: -2px;
|
|
left: -2px;
|
|
right: -2px;
|
|
bottom: -2px;
|
|
background: linear-gradient(45deg, #3690cf, #6dc6ff, #3690cf);
|
|
background-size: 200% 200%;
|
|
z-index: -1;
|
|
border-radius: 18px;
|
|
animation: glowingBorder 3s ease-in-out infinite;
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.glow-border:hover::before {
|
|
opacity: 1;
|
|
}
|
|
|
|
@keyframes glowingBorder {
|
|
0% {
|
|
background-position: 0% 50%;
|
|
}
|
|
50% {
|
|
background-position: 100% 50%;
|
|
}
|
|
100% {
|
|
background-position: 0% 50%;
|
|
}
|
|
}
|
|
</style>
|