Files
OpenIsle/open-isle-cli/src/views/HomePageView.vue
2025-07-06 11:52:35 +08:00

325 lines
7.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="home-page">
<div class="search-container">
<div class="search-title">Where possible begins</div>
<div class="search-subtitle">希望你喜欢这里有问题请提问或搜索现有帖子</div>
<div class="search-input">
<i class="search-input-icon fas fa-search"></i>
<input type="text" placeholder="Search">
</div>
</div>
<div class="topic-container">
<div class="topic-item-container">
<div v-for="topic in topics" :key="topic" class="topic-item" :class="{ selected: topic === selectedTopic }">
{{ topic }}
</div>
<CategorySelect v-model="selectedCategory" />
<TagSelect v-model="selectedTags" />
</div>
</div>
<div class="article-container">
<div class="header-container">
<div class="header-item main-item">
<div class="header-item-text">话题</div>
</div>
<div class="header-item avatars">
<div class="header-item-text">参与人员</div>
</div>
<div class="header-item">
<div class="header-item-text">回复</div>
</div>
<div class="header-item">
<div class="header-item-text">浏览</div>
</div>
<div class="header-item">
<div class="header-item-text">活动</div>
</div>
</div>
<div class="article-item" v-for="article in articles" :key="article.id">
<div class="article-main-container">
<router-link class="article-item-title main-item" :to="`/posts/${article.id}`">
{{ article.title }}
</router-link>
<div class="article-item-description main-item">{{ sanitizeDescription(article.description) }}</div>
<div class="article-info-container main-item">
<div class="article-info-item">
<i class="fas fa-user"></i>
<div class="article-info-item-text">{{ article.category }}</div>
</div>
<div class="article-tags-container">
<div class="article-tag-item" v-for="tag in article.tags" :key="tag">
<i class="fas fa-tag"></i>
<div class="article-tag-item-text">{{ tag }}</div>
</div>
</div>
</div>
</div>
<div class="article-member-avatars-container">
<div class="article-member-avatar-item" v-for="(avatar, idx) in article.members" :key="idx">
<img class="article-member-avatar-item-img" :src="avatar" alt="avatar">
</div>
</div>
<div class="article-comments">
{{ article.comments }}
</div>
<div class="article-views">
{{ article.views }}
</div>
<div class="article-time">
{{ article.time }}
</div>
</div>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
import { stripMarkdown } from '../utils/markdown'
import { API_BASE_URL } from '../main'
import CategorySelect from '../components/CategorySelect.vue'
import TagSelect from '../components/TagSelect.vue'
export default {
name: 'HomePageView',
components: {
CategorySelect,
TagSelect
},
data() {
return {
selectedCategory: '',
selectedTags: []
}
},
setup() {
const topics = ref(['最新', '排行榜', '热门', '类别'])
const selectedTopic = ref('最新')
const articles = ref([])
const fetchPosts = async () => {
try {
const res = await fetch(`${API_BASE_URL}/api/posts`)
if (!res.ok) return
const data = await res.json()
articles.value = data.map(p => ({
id: p.id,
title: p.title,
description: p.content,
category: p.category,
tags: p.tags || [],
members: [],
comments: (p.comments || []).length,
views: p.views,
time: new Date(p.createdAt).toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' })
}))
} catch (e) {
console.error(e)
}
}
onMounted(fetchPosts)
const sanitizeDescription = (text) => stripMarkdown(text)
return { topics, selectedTopic, articles, sanitizeDescription }
}
}
</script>
<style scoped>
.home-page {
background-color: white;
color: black;
height: calc(100vh - var(--header-height));
display: flex;
flex-direction: column;
align-items: center;
overflow-y: auto;
/* width variables shared between header and article rows */
--main-width: 60%;
--avatars-width: 20%;
--comments-width: 5%;
--views-width: 5%;
--activity-width: 10%;
}
.search-container {
margin-top: 100px;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.search-title {
font-size: 32px;
font-weight: bold;
}
.search-subtitle {
font-size: 16px;
}
.search-input {
display: flex;
align-items: center;
border: 1px solid lightgray;
border-radius: 10px;
padding: 10px;
width: 100%;
max-width: 600px;
margin-top: 20px;
}
.search-input input {
border: none;
outline: none;
font-size: 16px;
width: 100%;
margin-left: 10px;
}
.topic-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
width: 100%;
padding: 20px 0;
}
.topic-item-container {
margin-left: 20px;
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
.topic-item {
padding: 2px 10px;
}
.topic-item.selected {
color: var(--primary-color);
border-bottom: 2px solid var(--primary-color);
}
.article-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 100%;
}
.header-container {
display: grid;
grid-template-columns: var(--main-width) var(--avatars-width) var(--comments-width) var(--views-width) var(--activity-width);
align-items: center;
width: 100%;
color: gray;
border-bottom: 1px solid lightgray;
padding-bottom: 10px;
}
.article-item {
display: grid;
grid-template-columns: var(--main-width) var(--avatars-width) var(--comments-width) var(--views-width) var(--activity-width);
align-items: center;
width: 100%;
border-bottom: 1px solid lightgray;
}
.header-item.avatars {
margin-left: 20px;
}
.main-item {
padding-left: 20px;
}
.article-item-title {
font-size: 20px;
text-decoration: none;
color: black;
}
.article-item-title:hover {
color: var(--primary-color);
text-decoration: underline;
}
.article-item-description {
margin-top: 10px;
font-size: 14px;
color: gray;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.article-info-container {
margin-top: 10px;
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
.article-info-item {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
.article-tags-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
.article-tag-item {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
.article-member-avatars-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 3px;
margin-left: 20px;
}
.article-member-avatar-item {
width: 25px;
height: 25px;
border-radius: 50%;
overflow: hidden;
}
.article-member-avatar-item-img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>