信息检索、爬虫设置相关组件提交
This commit is contained in:
parent
2492a57b88
commit
11c9d33f94
|
@ -0,0 +1,431 @@
|
||||||
|
<template>
|
||||||
|
<div class="airport-management">
|
||||||
|
<div class="page-header">
|
||||||
|
<a-card class="general-card" title="机场管理">
|
||||||
|
<template #extra>
|
||||||
|
<a-button type="primary" @click="handleAdd">
|
||||||
|
<template #icon>
|
||||||
|
<icon-plus />
|
||||||
|
</template>
|
||||||
|
新建机场
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<a-row :gutter="16" style="margin-bottom: 16px">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input-search
|
||||||
|
v-model="searchForm.name"
|
||||||
|
placeholder="请输入机场名称"
|
||||||
|
@search="search"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-select
|
||||||
|
v-model="searchForm.status"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
allow-clear
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<a-option value="1">运营中</a-option>
|
||||||
|
<a-option value="0">停运</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button type="primary" @click="search">
|
||||||
|
<template #icon>
|
||||||
|
<icon-search />
|
||||||
|
</template>
|
||||||
|
查询
|
||||||
|
</a-button>
|
||||||
|
<a-button style="margin-left: 8px" @click="reset">
|
||||||
|
<template #icon>
|
||||||
|
<icon-refresh />
|
||||||
|
</template>
|
||||||
|
重置
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page-content">
|
||||||
|
<a-card class="general-card">
|
||||||
|
<a-table
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:pagination="pagination"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
|
>
|
||||||
|
<template #status="{ record }">
|
||||||
|
<a-tag :color="record.status === '1' ? 'green' : 'red'">
|
||||||
|
{{ record.status === '1' ? '运营中' : '停运' }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ record }">
|
||||||
|
<a-button type="text" size="small" @click="handleEdit(record)">
|
||||||
|
编辑
|
||||||
|
</a-button>
|
||||||
|
<a-button type="text" size="small" @click="handleView(record)">
|
||||||
|
查看详情
|
||||||
|
</a-button>
|
||||||
|
<a-popconfirm
|
||||||
|
content="确定要删除这个机场吗?"
|
||||||
|
@ok="handleDelete(record)"
|
||||||
|
>
|
||||||
|
<a-button type="text" size="small" status="danger">
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增/编辑机场弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
:title="modalTitle"
|
||||||
|
@ok="handleSave"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
width="800px"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="机场名称" field="name">
|
||||||
|
<a-input v-model="form.name" placeholder="请输入机场名称" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="机场代码" field="code">
|
||||||
|
<a-input v-model="form.code" placeholder="请输入机场代码" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="所在省份" field="province">
|
||||||
|
<a-input v-model="form.province" placeholder="请输入所在省份" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="所在城市" field="city">
|
||||||
|
<a-input v-model="form.city" placeholder="请输入所在城市" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="经度" field="longitude">
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.longitude"
|
||||||
|
placeholder="请输入经度"
|
||||||
|
:precision="6"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="纬度" field="latitude">
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.latitude"
|
||||||
|
placeholder="请输入纬度"
|
||||||
|
:precision="6"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="海拔高度(米)" field="altitude">
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.altitude"
|
||||||
|
placeholder="请输入海拔高度"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="状态" field="status">
|
||||||
|
<a-select v-model="form.status" placeholder="请选择状态">
|
||||||
|
<a-option value="1">运营中</a-option>
|
||||||
|
<a-option value="0">停运</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-form-item label="备注" field="remark">
|
||||||
|
<a-textarea
|
||||||
|
v-model="form.remark"
|
||||||
|
placeholder="请输入备注信息"
|
||||||
|
:auto-size="{ minRows: 3, maxRows: 5 }"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { IconPlus, IconSearch, IconRefresh } from '@arco-design/web-vue/es/icon'
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
name: '',
|
||||||
|
status: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '机场名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '机场代码',
|
||||||
|
dataIndex: 'code',
|
||||||
|
width: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '所在地区',
|
||||||
|
dataIndex: 'location',
|
||||||
|
width: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '经纬度',
|
||||||
|
dataIndex: 'coordinates',
|
||||||
|
width: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '海拔高度(米)',
|
||||||
|
dataIndex: 'altitude',
|
||||||
|
width: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
slotName: 'status',
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
width: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
slotName: 'actions',
|
||||||
|
width: 200,
|
||||||
|
fixed: 'right'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
const tableData = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '北京首都国际机场',
|
||||||
|
code: 'PEK',
|
||||||
|
province: '北京市',
|
||||||
|
city: '北京市',
|
||||||
|
location: '北京市朝阳区',
|
||||||
|
longitude: 116.584556,
|
||||||
|
latitude: 40.080111,
|
||||||
|
coordinates: '116.584556, 40.080111',
|
||||||
|
altitude: 35,
|
||||||
|
status: '1',
|
||||||
|
remark: '中国最大的国际机场之一',
|
||||||
|
createTime: '2024-01-01 10:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '上海浦东国际机场',
|
||||||
|
code: 'PVG',
|
||||||
|
province: '上海市',
|
||||||
|
city: '上海市',
|
||||||
|
location: '上海市浦东新区',
|
||||||
|
longitude: 121.805214,
|
||||||
|
latitude: 31.143378,
|
||||||
|
coordinates: '121.805214, 31.143378',
|
||||||
|
altitude: 4,
|
||||||
|
status: '1',
|
||||||
|
remark: '华东地区重要的国际航空枢纽',
|
||||||
|
createTime: '2024-01-02 14:30:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: '广州白云国际机场',
|
||||||
|
code: 'CAN',
|
||||||
|
province: '广东省',
|
||||||
|
city: '广州市',
|
||||||
|
location: '广州市白云区',
|
||||||
|
longitude: 113.298889,
|
||||||
|
latitude: 23.392436,
|
||||||
|
coordinates: '113.298889, 23.392436',
|
||||||
|
altitude: 15,
|
||||||
|
status: '1',
|
||||||
|
remark: '华南地区最大的民用机场',
|
||||||
|
createTime: '2024-01-03 09:15:00'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 分页配置
|
||||||
|
const pagination = reactive({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 3,
|
||||||
|
showTotal: true,
|
||||||
|
showPageSize: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const modalVisible = ref(false)
|
||||||
|
const modalTitle = ref('新增机场')
|
||||||
|
const formRef = ref()
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: null,
|
||||||
|
name: '',
|
||||||
|
code: '',
|
||||||
|
province: '',
|
||||||
|
city: '',
|
||||||
|
longitude: null,
|
||||||
|
latitude: null,
|
||||||
|
altitude: null,
|
||||||
|
status: '1',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = {
|
||||||
|
name: [{ required: true, message: '请输入机场名称' }],
|
||||||
|
code: [{ required: true, message: '请输入机场代码' }],
|
||||||
|
province: [{ required: true, message: '请输入所在省份' }],
|
||||||
|
city: [{ required: true, message: '请输入所在城市' }],
|
||||||
|
longitude: [{ required: true, message: '请输入经度' }],
|
||||||
|
latitude: [{ required: true, message: '请输入纬度' }],
|
||||||
|
status: [{ required: true, message: '请选择状态' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const search = () => {
|
||||||
|
loading.value = true
|
||||||
|
// 这里应该调用实际的API
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false
|
||||||
|
Message.success('查询成功')
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置搜索
|
||||||
|
const reset = () => {
|
||||||
|
searchForm.name = ''
|
||||||
|
searchForm.status = ''
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const handleAdd = () => {
|
||||||
|
modalTitle.value = '新增机场'
|
||||||
|
modalVisible.value = true
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (record: any) => {
|
||||||
|
modalTitle.value = '编辑机场'
|
||||||
|
modalVisible.value = true
|
||||||
|
Object.assign(form, record)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看详情
|
||||||
|
const handleView = (record: any) => {
|
||||||
|
Message.info(`查看机场详情: ${record.name}`)
|
||||||
|
// 这里可以跳转到详情页面或打开详情弹窗
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = (record: any) => {
|
||||||
|
Message.success(`删除机场: ${record.name}`)
|
||||||
|
// 这里应该调用删除API
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
const handleSave = async () => {
|
||||||
|
const valid = await formRef.value?.validate()
|
||||||
|
if (valid) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
// 这里应该调用保存API
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
|
||||||
|
Message.success(form.id ? '更新成功' : '新增成功')
|
||||||
|
modalVisible.value = false
|
||||||
|
search()
|
||||||
|
} catch (error) {
|
||||||
|
Message.error('操作失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消
|
||||||
|
const handleCancel = () => {
|
||||||
|
modalVisible.value = false
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
Object.keys(form).forEach(key => {
|
||||||
|
if (key === 'status') {
|
||||||
|
form[key] = '1'
|
||||||
|
} else {
|
||||||
|
form[key] = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
formRef.value?.clearValidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页改变
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
pagination.current = page
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePageSizeChange = (pageSize: number) => {
|
||||||
|
pagination.pageSize = pageSize
|
||||||
|
pagination.current = 1
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.airport-management {
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.general-card {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}</style>
|
|
@ -0,0 +1,344 @@
|
||||||
|
<template>
|
||||||
|
<div class="project-table-container">
|
||||||
|
<a-table
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="loading"
|
||||||
|
:pagination="false"
|
||||||
|
:scroll="{ x: '100%', y: '100%' }"
|
||||||
|
row-key="id"
|
||||||
|
:stripe="false"
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<!-- 招标项目 -->
|
||||||
|
<template #projectName="{ record }">
|
||||||
|
<div class="project-name">
|
||||||
|
<span class="name-text">{{ record.projectName }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 招标单位 -->
|
||||||
|
<template #biddingUnit="{ record }">
|
||||||
|
<span class="bidding-unit">{{ record.biddingUnit }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 预算金额 -->
|
||||||
|
<template #budgetAmount="{ record }">
|
||||||
|
<span class="budget-text">{{ record.budgetAmount }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 截止时间 -->
|
||||||
|
<template #deadline="{ record }">
|
||||||
|
<span class="date-text">{{ record.deadline }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 爬取时间 -->
|
||||||
|
<template #crawlingTime="{ record }">
|
||||||
|
<span class="date-text">{{ record.crawlingTime }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 来源平台 -->
|
||||||
|
<template #sourcePlatform="{ record }">
|
||||||
|
<span class="source-text">{{ record.sourcePlatform }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 招标文件 -->
|
||||||
|
<template #biddingDocuments="{ record }">
|
||||||
|
<span class="doc-text">{{ record.biddingDocuments }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 状态 -->
|
||||||
|
<template #status="{ record }">
|
||||||
|
<a-tag :color="getStatusColor(record.status)">
|
||||||
|
{{ getStatusText(record.status) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 操作 -->
|
||||||
|
<template #action="{ record }">
|
||||||
|
<div class="action-buttons">
|
||||||
|
<a-button size="small" @click="handleView(record)">
|
||||||
|
详情
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleEnter(record)"
|
||||||
|
>
|
||||||
|
报名
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import type { TableColumnData } from '@arco-design/web-vue'
|
||||||
|
import type { ProjectData } from '../types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: ProjectData[]
|
||||||
|
loading?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'view', project: ProjectData): void
|
||||||
|
(e: 'enter', project: ProjectData): void
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const columns: TableColumnData[] = [
|
||||||
|
{
|
||||||
|
title: '招标项目',
|
||||||
|
dataIndex: 'projectName',
|
||||||
|
slotName: 'projectName',
|
||||||
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
tooltip: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '招标单位',
|
||||||
|
dataIndex: ' biddingUnit',
|
||||||
|
slotName: 'biddingUnit',
|
||||||
|
width: 120,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '预算金额',
|
||||||
|
dataIndex: 'budgetAmount',
|
||||||
|
slotName: 'budgetAmount',
|
||||||
|
width: 120,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '截止时间',
|
||||||
|
dataIndex: 'deadline',
|
||||||
|
slotName: 'deadline',
|
||||||
|
width: 150,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '爬取时间',
|
||||||
|
dataIndex: 'crawlingTime',
|
||||||
|
slotName: 'crawlingTime',
|
||||||
|
width: 150,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '来源平台',
|
||||||
|
dataIndex: 'sourcePlatform',
|
||||||
|
slotName: 'sourcePlatform',
|
||||||
|
width: 150,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '招标文件',
|
||||||
|
dataIndex: ' biddingDocuments',
|
||||||
|
slotName: 'biddingDocuments',
|
||||||
|
width: 150,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
slotName: 'status',
|
||||||
|
width: 120,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
slotName: 'action',
|
||||||
|
width: 150,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 获取状态颜色
|
||||||
|
const getStatusColor = (status: string) => {
|
||||||
|
const colorMap: Record<string, string> = {
|
||||||
|
'not_started': 'gray',
|
||||||
|
'in_progress': 'blue',
|
||||||
|
'completed': 'green',
|
||||||
|
'paused': 'orange',
|
||||||
|
'cancelled': 'red'
|
||||||
|
}
|
||||||
|
return colorMap[status] || 'gray'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态文本
|
||||||
|
const getStatusText = (status: string) => {
|
||||||
|
const textMap: Record<string, string> = {
|
||||||
|
'not_started': '未开始',
|
||||||
|
'in_progress': '进行中',
|
||||||
|
'completed': '已完成',
|
||||||
|
'paused': '已暂停',
|
||||||
|
'cancelled': '已取消'
|
||||||
|
}
|
||||||
|
return textMap[status] || status
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取进度颜色
|
||||||
|
const getProgressColor = (progress: number) => {
|
||||||
|
if (progress === 0) return '#d9d9d9'
|
||||||
|
if (progress === 100) return '#52c41a'
|
||||||
|
if (progress >= 70) return '#1890ff'
|
||||||
|
if (progress >= 40) return '#faad14'
|
||||||
|
return '#ff4d4f'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件处理
|
||||||
|
const handleView = (record: ProjectData) => {
|
||||||
|
emit('view', record)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEnter = (record: ProjectData) => {
|
||||||
|
emit('enter', record)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.project-table-container {
|
||||||
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-name {
|
||||||
|
.name-text {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-count {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-text {
|
||||||
|
color: #666;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
font-weight: 500;
|
||||||
|
min-width: 35px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-table) {
|
||||||
|
.arco-table-thead {
|
||||||
|
.arco-table-th {
|
||||||
|
background-color: #fafafa;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-table-tbody {
|
||||||
|
.arco-table-tr {
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-table-td {
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
padding: 16px 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-tag) {
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-progress-line-outer) {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-progress-line-inner) {
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态标签样式
|
||||||
|
:deep(.arco-tag-gray) {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-color: #d9d9d9;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-tag-blue) {
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
border-color: #91d5ff;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-tag-green) {
|
||||||
|
background-color: #f6ffed;
|
||||||
|
border-color: #b7eb8f;
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-tag-orange) {
|
||||||
|
background-color: #fff7e6;
|
||||||
|
border-color: #ffd591;
|
||||||
|
color: #fa8c16;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-tag-red) {
|
||||||
|
background-color: #fff1f0;
|
||||||
|
border-color: #ffccc7;
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式处理
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
:deep(.arco-table-td) {
|
||||||
|
padding: 12px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-container {
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,415 @@
|
||||||
|
<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>
|
|
@ -0,0 +1,64 @@
|
||||||
|
// 项目状态枚举
|
||||||
|
export enum ProjectStatus {
|
||||||
|
NOT_STARTED = 'not_started',
|
||||||
|
IN_PROGRESS = 'in_progress',
|
||||||
|
COMPLETED = 'completed',
|
||||||
|
PAUSED = 'paused',
|
||||||
|
CANCELLED = 'cancelled'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 项目数据接口
|
||||||
|
export interface ProjectData {
|
||||||
|
id: number
|
||||||
|
projectName: string
|
||||||
|
unitCount: number
|
||||||
|
startDate: string
|
||||||
|
endDate: string
|
||||||
|
status: ProjectStatus
|
||||||
|
progress: number
|
||||||
|
manager: string
|
||||||
|
isMyProject: boolean
|
||||||
|
description?: string
|
||||||
|
location?: string
|
||||||
|
client?: string
|
||||||
|
budget?: number
|
||||||
|
team?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选项卡类型
|
||||||
|
export type TabKey = 'all' | 'my' | 'pending'
|
||||||
|
|
||||||
|
// 项目筛选条件
|
||||||
|
export interface ProjectFilter {
|
||||||
|
keyword?: string
|
||||||
|
status?: ProjectStatus
|
||||||
|
manager?: string
|
||||||
|
dateRange?: [string, string]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 项目统计信息
|
||||||
|
export interface ProjectStats {
|
||||||
|
total: number
|
||||||
|
inProgress: number
|
||||||
|
completed: number
|
||||||
|
notStarted: number
|
||||||
|
myProjects: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 招标详情
|
||||||
|
export interface BiddingDetail {
|
||||||
|
id: number
|
||||||
|
projectName: string
|
||||||
|
biddingUnit: string
|
||||||
|
budgetAmount: number
|
||||||
|
deadline: string
|
||||||
|
crawlingTime: string
|
||||||
|
projectLocation: string
|
||||||
|
projectDuration: string
|
||||||
|
biddingScope: string
|
||||||
|
qualificationRequirements: string
|
||||||
|
sourcePlatform: string
|
||||||
|
biddingDocuments: string
|
||||||
|
biddingContent: string
|
||||||
|
contentItems?: string[]
|
||||||
|
}
|
Loading…
Reference in New Issue