VUE3自定义文章排行榜的简单界面

文章目录

一、代码展示

html 复制代码
<template>
  <div class="article-ranking">
    <div class="header">
      <h2 class="title">{{ title }}</h2>
    </div>
    <div class="ranking-list">
      <div v-for="(article, index) in articles" :key="index" class="article-item">
        <div class="article-info">
          <h3 class="article-title">{{ truncateTitle(article.title, 25) }}</h3>
          <p class="article-content">{{ truncateContent(article.summary, 50) }}</p>
          <div class="details">
            <div class="info-row">
              <p class="info-text">
                时间: <span class="time">{{ formatPublishTime(article.createTime) }}</span> &nbsp;|&nbsp;
                浏览量: <span class="count">{{ formatViews(article.likeCount).formattedValue }}</span>
<!--               -->
              </p>
            </div>
          </div>
        </div>
        <div class="divider"></div> <!-- 分隔线 -->
      </div>
    </div>
    <div class="footer">
      <a @click="viewFullRanking" href="#" class="full-ranking-link">查看完整榜单</a>
    </div>
  </div>
</template>

<script setup>
import { defineProps } from 'vue';

const props = defineProps(['title', 'articles']);

const viewFullRanking = () => {
  console.log('View Full Ranking');
};

const truncateContent = (content, maxLength) => {
  return content.length > maxLength ? content.substring(0, maxLength) + '...' : content;
};

const truncateTitle = (title, maxLength) => {
  return title.length > maxLength ? title.substring(0, maxLength) + '...' : title;
};

const formatPublishTime = (publishTime) => {
  const currentDate = new Date();
  const articleDate = new Date(publishTime);
  const timeDiff = currentDate - articleDate;

  const oneDay = 24 * 60 * 60 * 1000;
  const oneMonth = oneDay * 30;

  if (timeDiff < oneDay) {
    const hours = Math.floor(timeDiff / (60 * 60 * 1000));
    return `${hours}小时前`;
  } else if (timeDiff < oneMonth) {
    const days = Math.floor(timeDiff / oneDay);
    return `${days}天前`;
  } else {
    const months = Math.floor(timeDiff / oneMonth);
    return `${months}个月前`;
  }
};

const formatAbbreviation = (value) => {
  if (value >= 10000) {
    return {
      formattedValue: Math.floor(value / 1000) + 'w+',
      isLargeCount: true,
    };
  } else {
    return {
      formattedValue: value,
      isLargeCount: false,
    };
  }
};

const formatViews = (views) => formatAbbreviation(views);

const formatLikes = (likes) => formatAbbreviation(likes);
</script>

<style scoped>
.article-ranking {
  width: 300px;
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 16px;
  margin: 16px;
  font-family: 'Arial', sans-serif;
}



.article-title {
  font-size: 18px;
  margin-bottom: 8px;
  color: #333;
  text-align: left;  /* Align article title to the left */
}

.article-content {
  font-size: 14px;
  color: #777;
  margin-bottom: 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: left;  /* Align article content to the left */
}

.ranking-list {
  display: flex;
  flex-direction: column;
}

.article-item {
  padding: 8px;
}

.article-info {
  display: flex;
  flex-direction: column;
}

.details {
  flex-grow: 1;
}

.info-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

.time {
  font-weight: bold;
  color: #1890ff;
}

.count {
  font-weight: bold;
  color: #1890ff;
}

.large-count {
  font-size: 12px; /* 调整较大计数的字体大小 */
}

.divider {
  height: 1px;
  background-color: #ddd;
  margin: 8px 0;
}

.footer {
  text-align: center;
  margin-top: 16px;
}

.full-ranking-link {
  font-size: 14px;
  color: #1890ff;
  text-decoration: none;
}

.full-ranking-link:hover {
  text-decoration: underline;
}
</style>

二、代码解读

  1. <template> 部分:

    • 整个模板包含一个名为 "article-ranking" 的 div,宽度为300像素,具有圆角边框和一些内外边距,呈现为一个简单的排行榜容器。
    • 模板包含标题("header")、排行列表("ranking-list")、文章项("article-item")、文章信息("article-info")、详细信息("details")、分隔线("divider")和页脚("footer")。
  2. <script setup> 部分:

    • 使用 import { defineProps } from 'vue'; 导入 defineProps 方法,以定义组件的属性。
    • 使用 defineProps(['title', 'articles']); 定义了两个属性:title 和 articles。
    • 定义了一个 viewFullRanking 方法,用于在点击 "查看完整榜单" 链接时输出一条日志。
    • 定义了 truncateContent 和 truncateTitle 方法,用于截断文章内容和标题,以确保它们不会超过指定的长度。
    • 定义了 formatPublishTime 方法,用于根据发布时间计算并返回相对于当前时间的时间差,以便显示多久前发布的文章。
    • 定义了 formatAbbreviation 方法,用于根据数值的大小返回格式化后的数值,并标记是否为较大的计数。
    • 定义了 formatViews 和 formatLikes 方法,这两个方法分别使用 formatAbbreviation 处理浏览量和点赞数。
  3. <style scoped> 部分:

    • 对排行榜容器及其子元素进行样式定义。
    • 调整了标题和文章内容的样式,使其居左对齐。
    • 使用了 flex 布局来组织文章项和详细信息。
    • 设置了一些通用的样式,如字体大小、颜色、边框等。
    • 使用了一些特定样式,如 divider 类,用于添加分隔线效果。
    • 样式中还包含了一些交互效果,如链接的鼠标悬停样式。

三、结果展示

相关推荐
青柠之夏cc22 分钟前
前端拖拽功能原生实现,不引入拖拽库完成业务
开发语言·前端·python
liangshanbo12151 小时前
前端大文件切片上传怎么做?
前端
Sand(ContextGate)1 小时前
Python Agent 测试实战:测试与评估,让 Agent 像传统软件一样可交付
前端·javascript·python·microsoft·ai
广州华水科技2 小时前
单北斗GNSS变形监测系统在城市安全与地质灾害中的应用前景
前端
颜进强2 小时前
20 · NestJs循环依赖与 forwardRef:容器为什么在环面前会死,"占位再补齐"怎么救
前端·后端·ai编程
颜进强2 小时前
19 · NestJs @Global 落地账:装饰器与 `isGlobal` 参数,各在什么场景上岗
前端·后端·ai编程
码艺-Alimjan2 小时前
Tauri 网站To桌面应用实战总结
前端·javascript·vue
颜进强2 小时前
18 · NestJS动态模块与 forRoot:`imports: [ConfigModule.forRoot({...})]` 到底在 import 什么
前端·后端·ai编程
YZ1225522 小时前
【Docker专题】使用Docker部署Vue-Flask项目前后端分离版【前端Docker部署】
前端·vue.js·docker
liangshanbo12152 小时前
浏览器渲染过程:高级前端面试题
前端