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

594 lines
16 KiB
Vue
Raw Normal View History

2025-06-27 19:54:42 +08:00
<template>
<GiPageLayout>
<GiTable
row-key="id"
:data="dataList"
:columns="tableColumns"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }"
:pagination="pagination"
:disabled-tools="['size']"
@page-change="onPageChange"
@page-size-change="onPageSizeChange"
@refresh="search"
>
<template #top>
<GiForm v-model="searchForm" search :columns="queryFormColumns" size="medium" @search="search" @reset="reset"></GiForm>
</template>
<template #toolbar-left>
<a-button v-permission="['project:create']" type="primary" @click="openAddModal">
<template #icon><icon-plus /></template>
<template #default>新增项目</template>
</a-button>
<a-button v-permission="['project:import']" @click="openImportModal">
<template #icon><icon-upload /></template>
<template #default>导入</template>
</a-button>
</template>
<template #toolbar-right>
<a-button v-permission="['project:export']" @click="exportData">
<template #icon><icon-download /></template>
<template #default>导出</template>
</a-button>
</template>
<template #status="{ record }">
<a-tag :color="getStatusColor(record.status)">{{ record.status }}</a-tag>
</template>
<template #fieldInfo="{ record }">
<div>{{ record.fieldName }}</div>
<div class="text-xs text-gray-500">{{ record.fieldLocation }}</div>
</template>
<template #commissionInfo="{ record }">
<div>{{ record.commissionContact }}</div>
<div class="text-xs text-gray-500">{{ record.commissionPhone }}</div>
</template>
<template #inspectionInfo="{ record }">
<div>{{ record.inspectionContact }}</div>
<div class="text-xs text-gray-500">{{ record.inspectionPhone }}</div>
</template>
<template #projectPeriod="{ record }">
<span v-if="record.projectPeriod?.length === 2">
{{ record.projectPeriod[0] }} {{ record.projectPeriod[1] }}
</span>
</template>
<template #projectManager="{ record }">
<div>{{ record.projectManager }}</div>
<div class="text-xs text-gray-500">{{ record.projectStaff?.join(', ') }}</div>
</template>
<template #action="{ record }">
<a-space>
<a-link v-permission="['project:detail']" title="详情" @click="viewDetail(record)">详情</a-link>
<a-link v-permission="['project:update']" title="修改" @click="openEditModal(record)">修改</a-link>
<a-link
v-permission="['project:delete']"
status="danger"
title="删除"
@click="confirmDelete(record)"
>
删除
</a-link>
</a-space>
</template>
</GiTable>
<!-- 新增/编辑项目弹窗 -->
<a-modal
v-model:visible="addModalVisible"
:title="modalTitle"
@cancel="resetForm"
@before-ok="handleSubmit"
width="800px"
>
<a-form ref="formRef" :model="form" label-position="left" :label-col-props="{ span: 8 }" :wrapper-col-props="{ span: 16 }">
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="projectName" label="项目名称" required>
<a-input v-model="form.projectName" placeholder="请输入" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="fieldName" label="风场名称" required>
<a-input v-model="form.fieldName" placeholder="请输入" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="fieldLocation" label="风场地址" required>
<a-input v-model="form.fieldLocation" placeholder="请输入" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="commissionUnit" label="委托单位" required>
<a-input v-model="form.commissionUnit" placeholder="请输入" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="commissionContact" label="委托单位联系人" required>
<a-input v-model="form.commissionContact" placeholder="请输入" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="commissionPhone" label="委托单位联系电话" required>
<a-input v-model="form.commissionPhone" placeholder="请输入" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="inspectionUnit" label="检查单位" required>
<a-input v-model="form.inspectionUnit" placeholder="请输入" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="inspectionContact" label="检查单位联系人" required>
<a-input v-model="form.inspectionContact" placeholder="请输入" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="inspectionPhone" label="检查单位联系电话" required>
<a-input v-model="form.inspectionPhone" placeholder="请输入" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="projectScale" label="项目规模" required>
<a-input v-model="form.projectScale" placeholder="请输入" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="orgNumber" label="机组型号" required>
<a-input v-model="form.orgNumber" placeholder="请输入" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="projectCategory" label="项目类型/服务" required>
<a-select v-model="form.projectCategory" placeholder="请选择">
<a-option value="外部工作">外部工作</a-option>
<a-option value="内部项目">内部项目</a-option>
<a-option value="技术服务">技术服务</a-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="projectManager" label="项目经理" required>
<a-select v-model="form.projectManager" placeholder="请选择">
<a-option value="请选择">请选择</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="status" label="状态" required>
<a-select v-model="form.status" placeholder="请选择">
<a-option value="施工中">施工中</a-option>
<a-option value="已完成">已完成</a-option>
<a-option value="未开始">未开始</a-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="24">
<a-form-item field="projectIntro" label="项目描述">
<a-textarea v-model="form.projectIntro" placeholder="请输入" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="24">
<a-form-item field="projectStaff" label="施工人员" required>
<a-select v-model="form.projectStaff" multiple placeholder="请选择">
<a-option value="全部员工">全部员工</a-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="24">
<a-form-item field="projectPeriod" label="项目周期" required>
<a-range-picker v-model="form.projectPeriod" style="width: 100%" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-modal>
<!-- 导入项目弹窗 -->
<a-modal
v-model:visible="importModalVisible"
title="导入文件"
@cancel="handleCancelImport"
@before-ok="handleImport"
>
<div class="flex flex-col items-center justify-center p-8">
<div class="text-primary text-4xl mb-4">
<icon-file-upload />
</div>
<div class="text-lg font-medium mb-2">批量导入文件</div>
<div class="text-gray-500 mb-4">拖动文件到此处或点击下方按钮上传</div>
<a-upload
:file-list="fileList"
:limit="1"
@change="handleFileChange"
>
<template #upload-button>
<a-button type="primary">选择文件</a-button>
</template>
</a-upload>
</div>
</a-modal>
</GiPageLayout>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { Message, Modal } from '@arco-design/web-vue'
import { addProject, deleteProject, listProject, updateProject, exportProject } from '@/apis/project'
import { isMobile } from '@/utils'
import has from '@/utils/has'
import type { ColumnItem } from '@/components/GiForm'
import type { TableColumnData } from '@arco-design/web-vue'
import type { ProjectResp } from '@/apis/project/type'
defineOptions({ name: 'ProjectManagement' })
const router = useRouter()
const formRef = ref()
const loading = ref(false)
const addModalVisible = ref(false)
const importModalVisible = ref(false)
const isEdit = ref(false)
const currentId = ref<number | null>(null)
const fileList = ref([])
const dataList = ref<ProjectResp[]>([])
const searchForm = reactive({
projectName: '',
status: undefined,
fieldName: '',
})
const queryFormColumns: ColumnItem[] = reactive([
{
type: 'input',
label: '项目名称',
field: 'projectName',
span: { xs: 24, sm: 8, xxl: 8 },
props: {
placeholder: '请输入项目名称',
},
},
{
type: 'input',
label: '风场名称',
field: 'fieldName',
span: { xs: 24, sm: 8, xxl: 8 },
props: {
placeholder: '请输入风场名称',
},
},
{
type: 'select',
label: '状态',
field: 'status',
span: { xs: 24, sm: 8, xxl: 8 },
props: {
options: [
{ label: '施工中', value: '施工中' },
{ label: '已完成', value: '已完成' },
{ label: '未开始', value: '未开始' }
],
placeholder: '请选择状态',
},
},
])
const form = reactive({
projectName: '',
projectIntro: '',
fieldName: '',
fieldLocation: '',
commissionUnit: '',
commissionContact: '',
commissionPhone: '',
inspectionUnit: '',
inspectionContact: '',
inspectionPhone: '',
projectScale: '',
orgNumber: '',
projectCategory: '',
projectManager: '',
projectStaff: [] as string[],
projectPeriod: [] as string[],
status: '施工中'
})
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
showTotal: true,
showJumper: true,
showPageSize: true
})
const tableColumns = ref<TableColumnData[]>([
{
title: '序号',
width: 66,
align: 'center',
render: ({ rowIndex }) => rowIndex + 1 + (pagination.current - 1) * pagination.pageSize,
fixed: !isMobile() ? 'left' : undefined,
},
{
title: '项目名称',
dataIndex: 'projectName',
minWidth: 140,
ellipsis: true,
tooltip: true,
fixed: !isMobile() ? 'left' : undefined,
},
{
title: '风场名称/风场地址',
slotName: 'fieldInfo',
minWidth: 180,
ellipsis: true,
tooltip: true
},
{
title: '状态',
dataIndex: 'status',
slotName: 'status',
align: 'center',
width: 100
},
{
title: '委托单位',
dataIndex: 'commissionUnit',
minWidth: 140,
ellipsis: true,
tooltip: true
},
{
title: '委托单位联系人/电话',
slotName: 'commissionInfo',
minWidth: 160,
ellipsis: true,
tooltip: true
},
{
title: '检查单位',
dataIndex: 'inspectionUnit',
minWidth: 140,
ellipsis: true,
tooltip: true
},
{
title: '检查单位联系人/电话',
slotName: 'inspectionInfo',
minWidth: 160,
ellipsis: true,
tooltip: true
},
{
title: '项目规模',
dataIndex: 'projectScale',
width: 100,
ellipsis: true,
tooltip: true
},
{
title: '机组型号',
dataIndex: 'orgNumber',
width: 100,
ellipsis: true,
tooltip: true
},
{
title: '项目经理/施工人员',
slotName: 'projectManager',
minWidth: 160,
ellipsis: true,
tooltip: true
},
{
title: '项目周期',
slotName: 'projectPeriod',
minWidth: 180,
ellipsis: true,
tooltip: true
},
{
title: '操作',
dataIndex: 'action',
slotName: 'action',
width: 180,
fixed: !isMobile() ? 'right' : undefined,
},
])
const modalTitle = computed(() => isEdit.value ? '编辑项目' : '新增项目')
const getStatusColor = (status: string) => {
switch (status) {
case '施工中':
return 'blue'
case '已完成':
return 'green'
case '未开始':
return 'orange'
default:
return 'gray'
}
}
const fetchData = async () => {
loading.value = true
try {
const res = await listProject({
...searchForm,
page: pagination.current,
size: pagination.pageSize
})
dataList.value = res.data?.list || []
pagination.total = res.data?.total || 0
} catch (error) {
console.error(error)
Message.error('获取数据失败')
} finally {
loading.value = false
}
}
const search = () => {
pagination.current = 1
fetchData()
}
const reset = () => {
searchForm.projectName = ''
searchForm.fieldName = ''
searchForm.status = undefined
search()
}
const onPageChange = (current: number) => {
pagination.current = current
fetchData()
}
const onPageSizeChange = (pageSize: number) => {
pagination.pageSize = pageSize
pagination.current = 1
fetchData()
}
const resetForm = () => {
formRef.value?.resetFields()
isEdit.value = false
currentId.value = null
}
const openAddModal = () => {
resetForm()
addModalVisible.value = true
}
const openEditModal = (record: any) => {
isEdit.value = true
currentId.value = record.id
Object.keys(form).forEach(key => {
if (key in record) {
form[key] = record[key]
}
})
addModalVisible.value = true
}
const handleSubmit = async () => {
const valid = await formRef.value?.validate()
if (!valid) return false
try {
if (isEdit.value && currentId.value) {
await updateProject(form, currentId.value)
Message.success('更新成功')
} else {
await addProject(form)
Message.success('添加成功')
}
fetchData()
return true
} catch (error) {
console.error(error)
Message.error('操作失败')
return false
}
}
const confirmDelete = (record: any) => {
Modal.warning({
title: '确认删除',
content: `确定要删除项目"${record.projectName}"吗?`,
onOk: () => deleteItem(record),
})
}
const deleteItem = async (record: any) => {
try {
await deleteProject(record.id)
Message.success('删除成功')
fetchData()
} catch (error) {
console.error(error)
Message.error('删除失败')
}
}
const viewDetail = (record: any) => {
router.push({
name: 'ProjectDetail',
params: {
id: record.id
}
})
}
const openImportModal = () => {
fileList.value = []
importModalVisible.value = true
}
const handleFileChange = (files: any) => {
fileList.value = files
}
const handleCancelImport = () => {
fileList.value = []
importModalVisible.value = false
}
const handleImport = () => {
if (fileList.value.length === 0) {
Message.warning('请选择文件')
return false
}
// 导入逻辑
Message.success('导入成功')
handleCancelImport()
return true
}
const exportData = () => {
exportProject(searchForm)
Message.success('导出成功')
}
onMounted(() => {
fetchData()
})
</script>
<style scoped>
:deep(.arco-tag) {
margin-right: 0;
}
</style>