175 lines
3.5 KiB
Vue
175 lines
3.5 KiB
Vue
<template>
|
|
<el-card class="project-card" shadow="hover">
|
|
<div class="project-header">
|
|
<div class="project-info">
|
|
<h3 class="project-name">{{ project.name || '未命名项目' }}</h3>
|
|
<p class="project-description">{{ project.description || '暂无描述' }}</p>
|
|
</div>
|
|
<div class="project-status">
|
|
<el-tag
|
|
:type="getStatusType(project.status)"
|
|
:effect="project.status === 'success' ? 'light' : 'plain'"
|
|
>
|
|
{{ getStatusText(project.status) }}
|
|
</el-tag>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="project-details">
|
|
<div class="detail-item">
|
|
<el-icon><Folder /></el-icon>
|
|
<span>{{ project.path || '/' }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<el-icon><Clock /></el-icon>
|
|
<span>{{ formatTime(project.updatedAt || project.createdAt) }}</span>
|
|
</div>
|
|
<div class="detail-item" v-if="project.version">
|
|
<el-icon><PriceTag /></el-icon>
|
|
<span>v{{ project.version }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="project-actions">
|
|
<el-button size="small" @click="$emit('view', project)">
|
|
查看详情
|
|
</el-button>
|
|
<el-button
|
|
size="small"
|
|
type="primary"
|
|
@click="$emit('deploy', project)"
|
|
:loading="deploying"
|
|
>
|
|
{{ deploying ? '部署中...' : '部署' }}
|
|
</el-button>
|
|
</div>
|
|
</el-card>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
import { Folder, Clock, PriceTag } from '@element-plus/icons-vue'
|
|
import { formatDistanceToNow } from '@/utils/format'
|
|
|
|
const props = defineProps({
|
|
project: {
|
|
type: Object,
|
|
required: true
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['view', 'deploy'])
|
|
|
|
const deploying = ref(false)
|
|
|
|
// 获取状态类型
|
|
const getStatusType = (status) => {
|
|
const types = {
|
|
success: 'success',
|
|
running: 'warning',
|
|
failed: 'danger',
|
|
pending: 'info'
|
|
}
|
|
return types[status] || 'info'
|
|
}
|
|
|
|
// 获取状态文本
|
|
const getStatusText = (status) => {
|
|
const texts = {
|
|
success: '部署成功',
|
|
running: '部署中',
|
|
failed: '部署失败',
|
|
pending: '待部署'
|
|
}
|
|
return texts[status] || '未知状态'
|
|
}
|
|
|
|
// 格式化时间
|
|
const formatTime = (time) => {
|
|
if (!time) return '未知时间'
|
|
try {
|
|
return formatDistanceToNow(new Date(time))
|
|
} catch (error) {
|
|
return '时间格式错误'
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.project-card {
|
|
height: 100%;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.project-card:hover {
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.project-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.project-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.project-name {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #262626;
|
|
margin: 0 0 8px 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.project-description {
|
|
font-size: 14px;
|
|
color: #8c8c8c;
|
|
margin: 0;
|
|
line-height: 1.4;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.project-status {
|
|
flex-shrink: 0;
|
|
margin-left: 12px;
|
|
}
|
|
|
|
.project-details {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.detail-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-bottom: 8px;
|
|
font-size: 12px;
|
|
color: #8c8c8c;
|
|
}
|
|
|
|
.detail-item:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.detail-item span {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.project-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
justify-content: flex-end;
|
|
}
|
|
</style> |