프로젝트 목록 추가

This commit is contained in:
yoon 2025-02-10 14:45:13 +09:00
parent 8427dce4cc
commit 8a15c9c1cc
5 changed files with 226 additions and 0 deletions

View File

@ -0,0 +1,125 @@
<template>
<div class="card mb-3 shadow-sm">
<div class="row g-0">
<!-- 게시물 내용 섹션 -->
<div :class="contentColClass">
<div class="card-body">
<!-- 제목 -->
<h5 class="card-title">
{{ title }}
<span class="text-muted me-3" v-if="attachment">
<i class="fa-solid fa-paperclip"></i>
</span>
</h5>
<!-- 본문 -->
<div class="card-text str_wrap my-5">{{ content }}</div>
<!-- 날짜 -->
<div class="d-flex flex-column flex-sm-row justify-content-between align-items-start">
<small class="text-muted">{{ formattedDate }}</small>
<!-- 조회수, 좋아요, 댓글 -->
<div class="d-flex mt-2 mt-sm-0">
<span class="text-muted me-3">
<i class="fa-regular fa-eye"></i> {{ views || 0 }}
</span>
<span class="text-muted me-3" v-if="likes != null">
<i class="bx bx-like"></i> {{ likes }}
</span>
<span class="text-muted" v-if="comments !== null">
<i class="bx bx-comment"></i> {{ comments }}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue';
import { defineProps } from 'vue';
// Props
const props = defineProps({
category: {
type: String,
required: false,
},
title: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
date: {
type: String,
required: true,
},
views: {
type: Number,
default: 0,
},
likes: {
type: Number,
default: null,
},
comments: {
type: Number,
default: null,
},
attachment: {
type: Boolean,
default: false,
}
});
// computed
const contentColClass = computed(() => {
return props.img ? 'col-sm-10 col-12' : 'col-sm-12';
});
// formattedDate computed
const formattedDate = computed(() => {
const date = new Date(props.date);
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(
date.getDate()
).padStart(2, "0")} ${String(date.getHours()).padStart(2, "0")}:${String(
date.getMinutes()
).padStart(2, "0")}`;
});
</script>
<style>
/* 카드 스타일 */
.card {
border: 1px solid #e6e6e6;
border-radius: 8px;
transition: transform 0.2s ease-in-out;
}
.card:hover {
transform: scale(1.02);
}
/* 텍스트 줄임 표시 */
.str_wrap {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
/* 이미지 스타일 */
.img-fluid {
border-radius: 8px 0 0 8px;
}
/* 태그 배지 스타일 */
.badge {
font-size: 0.8rem;
padding: 5px 10px;
}
</style>

View File

@ -0,0 +1,67 @@
<template>
<div class="mt-4">
<div v-if="posts.length === 0" class="text-center">
<p class="text-muted mt-4">게시물이 없습니다.</p>
</div>
<div v-for="post in posts" :key="post.id" @click="goDetail(post.id)">
<ProjectCard
:category="post.category || ''"
:title="post.title"
:content="post.content"
:date="post.date"
:views="post.views || 0"
v-bind="getProjectCardProps(post)"
:attachment="post.attachment || false"
/>
</div>
</div>
</template>
<script setup>
import { ref, defineEmits } from 'vue';
import ProjectCard from './ProjectCard.vue';
//
const posts = ref([
{
id: 1,
title: 'Vue 3 Composition API 소개',
content: 'Composition API를 사용하여 Vue 3에서 효율적으로 개발하는 방법을 알아봅니다.',
date: '2025-02-10',
views: 120,
likes: 15,
comments: 4,
attachment: true
},
{
id: 2,
title: 'Spring Boot로 REST API 만들기',
content: 'Spring Boot를 사용하여 간단한 RESTful API를 구현하는 방법을 다룹니다.',
date: '2025-02-09',
views: 89,
likes: 8,
comments: 2,
attachment: false
}
]);
const emit = defineEmits(['click']);
//
const goDetail = (id) => {
emit('click', id);
};
//
const getProjectCardProps = (post) => {
const projectCardProps = {};
if ('likes' in post) {
projectCardProps.likes = post.likes;
}
if ('comments' in post) {
projectCardProps.comments = post.comments;
}
return projectCardProps;
};
</script>

View File

@ -61,6 +61,12 @@
<div class="text-truncate">vacation</div>
</RouterLink>
</li>
<li class="menu-item" :class="$route.path.includes('/projectlist') ? 'active' : ''">
<RouterLink class="menu-link" to="/projectlist">
<i class="menu-icon icon-base bx bx-list-ul"></i>
<div class="text-truncate">project</div>
</RouterLink>
</li>
<li class="menu-item" :class="$route.path.includes('/sample') ? 'active' : ''">
<RouterLink class="menu-link" to="/sample"> <i class="bi "></i>
<i class="menu-icon tf-icons bx bx-calendar"></i>

View File

@ -76,6 +76,10 @@ const routes = [
]
},
{
path: '/projectlist',
component: () => import('@v/projectlist/TheProjectList.vue'),
},
{
path: '/sample',
component: () => import('@c/calendar/SampleCalendar.vue'),

View File

@ -0,0 +1,24 @@
<template>
<div class="container-xxl flex-grow-1 container-p-y">
<SearchBar />
<CategoryBtn :lists="testData" />
<ProjectCardList :category="selectedCategory" />
</div>
</template>
<script setup>
import SearchBar from '@c/search/SearchBar.vue';
import ProjectCardList from '@c/list/ProjectCardList.vue';
import CategoryBtn from '@c/category/CategoryBtn.vue';
import { ref } from 'vue';
const selectedCategory = ref(null);
//
const testData = [
{ CMNCODVAL: 'cat1', CMNCODNAM: 'All' },
{ CMNCODVAL: 'cat2', CMNCODNAM: '2024' },
{ CMNCODVAL: 'cat3', CMNCODNAM: '2025' },
];
</script>