2026-01-13 21:03:52 +08:00
|
|
|
<template>
|
|
|
|
|
<el-card class="box-card" shadow="hover">
|
|
|
|
|
<template #header>
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<span>Pined</span>
|
|
|
|
|
<el-tag type="danger">{{ pinnedBids.length }} 个置顶</el-tag>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<div v-if="pinnedLoading" style="text-align: center; padding: 40px;">
|
|
|
|
|
<el-icon class="is-loading" :size="30"><Loading /></el-icon>
|
|
|
|
|
<p style="margin-top: 10px; color: #909399;">加载中...</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else-if="pinnedBids.length === 0" style="text-align: center; padding: 40px; color: #909399;">
|
|
|
|
|
<el-icon :size="40"><InfoFilled /></el-icon>
|
|
|
|
|
<p style="margin-top: 10px;">暂无置顶项目</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else>
|
|
|
|
|
<el-table :data="pinnedBids" style="width: 100%" size="small">
|
|
|
|
|
<el-table-column label="Pin" width="60" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-icon
|
|
|
|
|
:style="{
|
|
|
|
|
color: '#f56c6c',
|
|
|
|
|
cursor: 'pointer',
|
|
|
|
|
fontSize: '18px'
|
|
|
|
|
}"
|
|
|
|
|
@click="togglePin(scope.row)"
|
|
|
|
|
>
|
|
|
|
|
<Paperclip />
|
|
|
|
|
</el-icon>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="title" label="项目名称">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<a :href="scope.row.url" target="_blank">{{ scope.row.title }}</a>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="source" label="来源" width="200" />
|
|
|
|
|
<el-table-column prop="publishDate" label="发布日期" width="180">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ formatDate(scope.row.publishDate) }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
|
import axios from 'axios'
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
import { Loading, InfoFilled, Paperclip } from '@element-plus/icons-vue'
|
|
|
|
|
|
2026-01-13 21:16:23 +08:00
|
|
|
const emit = defineEmits<{
|
|
|
|
|
pinChanged: [title: string]
|
|
|
|
|
}>()
|
|
|
|
|
|
2026-01-13 21:03:52 +08:00
|
|
|
const pinnedBids = ref<any[]>([])
|
|
|
|
|
const pinnedLoading = ref(false)
|
|
|
|
|
|
|
|
|
|
// 加载置顶项目
|
|
|
|
|
const loadPinnedBids = async () => {
|
|
|
|
|
pinnedLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.get('/api/bids/pinned')
|
|
|
|
|
pinnedBids.value = response.data
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to load pinned bids:', error)
|
|
|
|
|
} finally {
|
|
|
|
|
pinnedLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 切换置顶列表的 Pin 状态
|
|
|
|
|
const togglePin = async (item: any) => {
|
|
|
|
|
try {
|
|
|
|
|
await axios.patch(`/api/bids/${encodeURIComponent(item.title)}/pin`, { pin: false })
|
|
|
|
|
const index = pinnedBids.value.findIndex(b => b.title === item.title)
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
pinnedBids.value.splice(index, 1)
|
|
|
|
|
}
|
|
|
|
|
ElMessage.success('已取消置顶')
|
2026-01-13 21:16:23 +08:00
|
|
|
// 通知父组件 pin 状态已改变,传递 title
|
|
|
|
|
emit('pinChanged', item.title)
|
2026-01-13 21:03:52 +08:00
|
|
|
} catch (error) {
|
|
|
|
|
ElMessage.error('操作失败')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 格式化日期,只显示年月日
|
|
|
|
|
const formatDate = (dateStr: string) => {
|
|
|
|
|
if (!dateStr) return ''
|
|
|
|
|
const date = new Date(dateStr)
|
|
|
|
|
const year = date.getFullYear()
|
|
|
|
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
|
|
const day = String(date.getDate()).padStart(2, '0')
|
|
|
|
|
return `${year}-${month}-${day}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化时加载置顶项目
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
loadPinnedBids()
|
|
|
|
|
})
|
2026-01-13 21:16:23 +08:00
|
|
|
|
|
|
|
|
// 暴露方法给父组件调用
|
|
|
|
|
defineExpose({
|
|
|
|
|
loadPinnedBids
|
|
|
|
|
})
|
2026-01-13 21:03:52 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.card-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.box-card {
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a {
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
color: inherit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a:hover {
|
|
|
|
|
color: #409eff;
|
|
|
|
|
}
|
|
|
|
|
</style>
|