Industrial-image-management.../src/views/project-management/bidding/information-retrieval/index.vue

415 lines
11 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>
<GiPageLayout>
<div class="project-list-container">
<!-- 顶部搜索和操作区域 -->
<div class="header-section">
<div class="search-area">
<a-input-search
v-model="searchKeyword"
placeholder="搜索项目名称"
allow-clear
@search="handleSearch"
@clear="handleClear"
style="width: 300px"
/>
</div>
<div class="action-area">
<a-space>
<a-button type="primary" @click="startCrawler">
<template #icon><icon-play-arrow /></template>
开始爬虫
</a-button>
<a-button @click="refreshData">
<template #icon><icon-refresh /></template>
刷新数据
</a-button>
<a-button @click="exportData">
<template #icon><icon-download /></template>
导出数据
</a-button>
<a-button @click="openCrawlerSettings">
<template #icon><icon-settings /></template>
爬虫设置
</a-button>
</a-space>
</div>
</div>
<!-- 选项卡区域 -->
<div class="tabs-section">
<a-tabs
v-model:active-key="activeTab"
type="line"
size="large"
@change="handleTabChange"
>
<a-tab-pane key="all" tab="信息检索" title="信息检索">
<ProjectTable
:data="pagedData"
:loading="loading"
@view="handleView"
@enter="handleEnter"
/>
<div class="pagination-wrapper">
<a-pagination
v-model:current="currentPage"
v-model:page-size="pageSize"
:total="totalItems"
show-total
show-jumper
show-page-size
:page-size-options="[10, 20, 50, 100]"
@change="handlePageChange"
/>
</div>
</a-tab-pane>
<a-tab-pane key="my" tab="投标响应" title="投标响应">
<ProjectTable
:data="myPagedData"
:loading="loading"
@view="handleView"
@enter="handleEnter"
/>
<div class="pagination-wrapper">
<a-pagination
v-model:current="myCurrentPage"
v-model:page-size="pageSize"
:total="myTotalItems"
show-total
show-jumper
show-page-size
:page-size-options="[10, 20, 50, 100]"
@change="handleMyPageChange"
/>
</div>
</a-tab-pane>
</a-tabs>
</div>
<CrawlerSettings
v-model:visible="crawlerSettingsVisible"
@save="handleSaveSettings"
/>
</div>
</GiPageLayout>
<BiddingDetailModal
v-model:visible="detailVisible"
:detail="currentDetail"
:uploading="uploading"
@upload="handleUploadDocument"
/>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { Message } from '@arco-design/web-vue'
import ProjectTable from './components/InformationTable.vue'
import CrawlerSettings from './components/CrawlerSettings.vue'
import BiddingDetailModal from './components/BiddingDetailModal.vue'
import type { ProjectData, TabKey,BiddingDetail } from './types'
import { ProjectStatus } from './types'
defineOptions({ name: 'ProjectList' })
// 响应式数据
const loading = ref(false)
const activeTab = ref<TabKey>('all')
const searchKeyword = ref('')
const crawlerSettingsVisible = ref(false)
// 分页相关数据
const currentPage = ref(1)
const myCurrentPage = ref(1)
const pageSize = ref(10)
// 添加状态
const detailVisible = ref(false)
const uploading = ref(false)
const currentDetail = ref<BiddingDetail>({
id: 0,
projectName: '',
biddingUnit: '',
budgetAmount: 0,
deadline: '',
crawlingTime: '',
projectLocation: '',
projectDuration: '',
biddingScope: '',
qualificationRequirements: '',
sourcePlatform: '',
biddingDocuments: '',
biddingContent: '',
contentItems: []
})
// 项目数据
const projectList = ref<ProjectData[]>([
{
id: 1,
projectName: 'A风场2023年检查',
biddingUnit: 15,
budgetAmount:111,
deadline: '2023-10-01',
crawlingTime: '2023-12-31',
sourcePlatform:"中国招标投标网",
biddingDocuments:"333.pdf",
status: ProjectStatus.IN_PROGRESS,
manager: '张三',
isMyProject: true
},
// 添加更多示例数据用于测试分页
...Array.from({ length: 25 }, (_, i) => ({
id: i + 2,
projectName: `项目${i + 2}`,
biddingUnit: Math.floor(Math.random() * 20) + 1,
budgetAmount: Math.floor(Math.random() * 1000) + 100,
deadline: `2023-${(Math.floor(Math.random() * 12) + 1).toString().padStart(2, '0')}-${(Math.floor(Math.random() * 28) + 1).toString().padStart(2, '0')}`,
crawlingTime: `2023-${(Math.floor(Math.random() * 12) + 1).toString().padStart(2, '0')}-${(Math.floor(Math.random() * 28) + 1).toString().padStart(2, '0')}`,
sourcePlatform: "中国招标投标网",
biddingDocuments: `文档${i + 2}.pdf`,
status: Math.random() > 0.5 ? ProjectStatus.IN_PROGRESS : ProjectStatus.COMPLETED,
manager: ['张三', '李四', '王五'][Math.floor(Math.random() * 3)],
isMyProject: Math.random() > 0.7
}))
])
const handleSaveSettings = (settings: any) => {
console.log('Saved settings:', settings)
}
// 处理查看详情
const handleView = (project: ProjectData) => {
// 这里应该根据项目ID获取详细数据这里简化处理
currentDetail.value = {
id: project.id,
projectName: project.projectName,
biddingUnit: project.biddingUnit.toString(),
budgetAmount: project.budgetAmount,
deadline: project.deadline,
crawlingTime: project.crawlingTime,
projectLocation: '内蒙古自治区', // 示例数据
projectDuration: '6个月', // 示例数据
biddingScope: '风电场50台机组叶片检查服务', // 示例数据
qualificationRequirements: '具备风电叶片检查资质', // 示例数据
sourcePlatform: project.sourcePlatform,
biddingDocuments: project.biddingDocuments,
biddingContent: '本项目为某风电场2023年叶片检查服务采购包括但不限于',
contentItems: [
'叶片外观检查',
'无损检测',
'缺陷记录与评估',
'检查报告编制'
]
}
detailVisible.value = true
}
// 计算属性 - 根据搜索关键词过滤项目
const filteredProjects = computed(() => {
if (!searchKeyword.value) {
return projectList.value
}
return projectList.value.filter(project =>
project.projectName.toLowerCase().includes(searchKeyword.value.toLowerCase())
)
})
// 计算属性 - 我的项目
const myProjects = computed(() => {
return filteredProjects.value.filter(project => project.isMyProject)
})
// 分页相关计算属性
const totalItems = computed(() => filteredProjects.value.length)
const myTotalItems = computed(() => myProjects.value.length)
const pagedData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return filteredProjects.value.slice(start, end)
})
const myPagedData = computed(() => {
const start = (myCurrentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return myProjects.value.slice(start, end)
})
// 搜索处理
const handleSearch = (value: string) => {
searchKeyword.value = value
currentPage.value = 1 // 搜索时重置到第一页
myCurrentPage.value = 1
}
const handleClear = () => {
searchKeyword.value = ''
currentPage.value = 1 // 清空搜索时重置到第一页
myCurrentPage.value = 1
}
// 选项卡切换
const handleTabChange = (key: string) => {
activeTab.value = key as TabKey
}
// 分页变化处理
const handlePageChange = (page: number) => {
currentPage.value = page
}
const handleMyPageChange = (page: number) => {
myCurrentPage.value = page
}
// 刷新数据
const refreshData = async () => {
loading.value = true
try {
// 模拟API请求
await new Promise(resolve => setTimeout(resolve, 800))
Message.success('数据已刷新')
} catch (error) {
Message.error('刷新失败')
} finally {
loading.value = false
}
}
// 开始爬虫
const startCrawler = () => {
Message.info('开始爬虫任务')
// 这里添加爬虫启动逻辑
}
// 导出数据
const exportData = () => {
Message.info('导出数据')
// 这里添加数据导出逻辑
}
// 打开爬虫设置
const openCrawlerSettings = () => {
crawlerSettingsVisible.value = true
}
const handleUploadDocument = async (file: File) => {
uploading.value = true
try {
const formData = new FormData()
formData.append('file', file)
formData.append('projectId', currentDetail.value.id.toString())
// 替换为实际API调用
const response = await uploadBiddingDocument(formData)
Message.success('文件上传成功')
currentDetail.value.biddingDocuments = response.data.fileUrl
} catch (error) {
Message.error(`文件上传失败: ${error.message}`)
} finally {
uploading.value = false
}
}
// 模拟上传API
const uploadBiddingDocument = (formData: FormData) => {
return new Promise((resolve) => {
setTimeout(() => {
const file = formData.get('file') as File
const projectId = formData.get('projectId')
const extension = file.name.split('.').pop()
resolve({
data: {
fileUrl: `https://your-api-domain.com/uploads/${projectId}.${extension}`
}
})
}, 1500)
})
}
onMounted(() => {
// 初始化加载数据
})
</script>
<style scoped lang="less">
.project-list-container {
height: 100%;
display: flex;
flex-direction: column;
}
.header-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding: 0 4px;
}
.search-area {
flex: 1;
}
.action-area {
display: flex;
gap: 12px;
align-items: center;
}
.tabs-section {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.pagination-wrapper {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
:deep(.arco-tabs-content) {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
:deep(.arco-tabs-pane) {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
:deep(.arco-tabs-nav-tab) {
font-weight: 500;
font-size: 16px;
}
:deep(.arco-tabs-nav-tab-list) {
padding: 0 4px;
}
// 响应式处理
@media (max-width: 768px) {
.header-section {
flex-direction: column;
gap: 16px;
align-items: stretch;
}
.search-area {
flex: none;
}
.action-area {
justify-content: center;
}
}
</style>