Industrial-image-management.../src/views/project-management/projects/list/index.vue

268 lines
5.8 KiB
Vue

<template>
<GiPageLayout>
<div class="project-list-container">
<!-- 顶部搜索和操作区域 -->
<div class="header-section">
<div class="search-area">
<a-input-search
v-model="searchKeyword"
placeholder="搜索项目名称"
style="width: 300px"
@search="handleSearch"
@clear="handleClear"
allow-clear
>
<template #prefix>
<GiSvgIcon name="search" />
</template>
</a-input-search>
</div>
<div class="action-area">
<a-button @click="refreshData">
<template #icon>
<GiSvgIcon name="refresh" />
</template>
刷新
</a-button>
<a-button type="primary" @click="openAddModal">
<template #icon>
<GiSvgIcon name="plus" />
</template>
新建项目
</a-button>
</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="filteredProjects"
:loading="loading"
@view="handleView"
@enter="handleEnter"
/>
</a-tab-pane>
<a-tab-pane key="my" tab="我的项目" title="我的项目">
<ProjectTable
:data="myProjects"
:loading="loading"
@view="handleView"
@enter="handleEnter"
/>
</a-tab-pane>
<a-tab-pane key="pending" tab="待开工项目" title="待开工项目">
<ProjectTable
:data="pendingProjects"
:loading="loading"
@view="handleView"
@enter="handleEnter"
/>
</a-tab-pane>
</a-tabs>
</div>
</div>
</GiPageLayout>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { Message } from '@arco-design/web-vue'
import ProjectTable from './components/ProjectTable.vue'
import type { ProjectData, TabKey } from './types'
import { ProjectStatus } from './types'
defineOptions({ name: 'ProjectList' })
// 响应式数据
const loading = ref(false)
const activeTab = ref<TabKey>('all')
const searchKeyword = ref('')
// 项目数据
const projectList = ref<ProjectData[]>([
{
id: 1,
projectName: 'A风场2023年检查',
unitCount: 15,
startDate: '2023-10-01',
endDate: '2023-12-31',
status: ProjectStatus.IN_PROGRESS,
progress: 65,
manager: '张三',
isMyProject: true
},
{
id: 2,
projectName: 'B风场维修项目',
unitCount: 8,
startDate: '2023-09-15',
endDate: '2023-11-30',
status: ProjectStatus.IN_PROGRESS,
progress: 45,
manager: '李四',
isMyProject: false
},
{
id: 3,
projectName: 'C风场年度检查',
unitCount: 20,
startDate: '2023-08-01',
endDate: '2023-10-31',
status: ProjectStatus.COMPLETED,
progress: 100,
manager: '王五',
isMyProject: true
},
{
id: 4,
projectName: 'D风场初步检查',
unitCount: 12,
startDate: '2023-11-10',
endDate: '2023-12-15',
status: ProjectStatus.NOT_STARTED,
progress: 0,
manager: '赵六',
isMyProject: false
}
])
// 计算属性 - 根据搜索关键词过滤项目
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 pendingProjects = computed(() => {
return filteredProjects.value.filter(project => project.status === 'not_started')
})
// 搜索处理
const handleSearch = (value: string) => {
searchKeyword.value = value
}
const handleClear = () => {
searchKeyword.value = ''
}
// 选项卡切换
const handleTabChange = (key: string) => {
activeTab.value = key as TabKey
}
// 刷新数据
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 openAddModal = () => {
Message.info('打开新建项目对话框')
}
// 查看项目
const handleView = (project: any) => {
Message.info(`查看项目: ${project.projectName}`)
}
// 进入项目
const handleEnter = (project: any) => {
Message.info(`进入项目: ${project.projectName}`)
}
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: 24px;
padding: 0 4px;
}
.search-area {
flex: 1;
}
.action-area {
display: flex;
gap: 12px;
align-items: center;
}
.tabs-section {
flex: 1;
min-height: 0;
}
:deep(.arco-tabs-content) {
height: 100%;
}
:deep(.arco-tabs-pane) {
height: 100%;
}
: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>