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

932 lines
28 KiB
Vue
Raw Normal View History

2025-06-30 09:14:46 +08:00
<!--
项目管理页面
已完成接口对接:
1. 项目列表查询 (listProject) - 支持分页和条件查询
2. 项目新增 (addProject)
3. 项目修改 (updateProject)
4. 项目删除 (deleteProject)
5. 项目导出 (exportProject)
6. 项目导入 (importProject)
所有API调用都已添加错误处理和类型安全检查
-->
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 }">
2025-07-14 11:11:33 +08:00
<a-tag :color="getStatusColor(record.status)">{{ PROJECT_STATUS_MAP[record.status] || '未知状态' }}</a-tag>
2025-06-27 19:54:42 +08:00
</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"
2025-07-14 11:11:33 +08:00
:ok-button-props="{ loading: submitLoading }"
@ok="handleSubmit"
2025-06-27 19:54:42 +08:00
width="800px"
2025-07-14 11:11:33 +08:00
modal-class="project-form-modal"
2025-06-27 19:54:42 +08:00
>
2025-07-14 11:11:33 +08:00
<a-form
ref="formRef"
:model="form"
:rules="formRules"
layout="vertical"
:style="{ maxHeight: '70vh', overflow: 'auto', padding: '0 10px' }"
>
<!-- 基本信息 -->
<a-divider orientation="left">基本信息</a-divider>
2025-06-27 19:54:42 +08:00
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="projectName" label="项目名称" required>
2025-07-14 11:11:33 +08:00
<a-input v-model="form.projectName" placeholder="请输入项目名称" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="projectManagerId" label="项目经理" required>
<a-select v-model="form.projectManagerId" placeholder="请选择项目经理" :loading="userLoading">
<a-option v-for="user in userOptions" :key="user.value" :value="user.value">
{{ user.label }}
</a-option>
</a-select>
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="status" label="项目状态" >
<a-select v-model="form.status" placeholder="请选择状态">
<a-option v-for="option in PROJECT_STATUS_OPTIONS" :key="option.value" :value="option.value">
{{ option.label }}
</a-option>
</a-select>
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="scale" label="项目规模" >
<a-input v-model="form.scale" placeholder="请输入项目规模" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="startDate" label="开始时间" >
<a-date-picker v-model="form.startDate" style="width: 100%" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="endDate" label="结束时间" >
<a-date-picker v-model="form.endDate" style="width: 100%" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
</a-row>
2025-07-14 11:11:33 +08:00
<a-row :gutter="16">
<a-col :span="24">
<a-form-item field="coverUrl" label="项目封面">
<a-input v-model="form.coverUrl" placeholder="请输入项目封面URL" />
</a-form-item>
</a-col>
</a-row>
<!-- 委托方信息 -->
<a-divider orientation="left">委托方信息</a-divider>
2025-06-27 19:54:42 +08:00
<a-row :gutter="16">
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="client" label="委托单位" >
<a-input v-model="form.client" placeholder="请输入委托单位" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="clientContact" label="委托单位联系人" >
<a-input v-model="form.clientContact" placeholder="请输入联系人" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="clientPhone" label="委托单位联系电话" >
<a-input v-model="form.clientPhone" placeholder="请输入联系电话" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
</a-row>
2025-07-14 11:11:33 +08:00
<!-- 检查方信息 -->
<a-divider orientation="left">检查方信息</a-divider>
2025-06-27 19:54:42 +08:00
<a-row :gutter="16">
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="inspectionUnit" label="检查单位" >
<a-input v-model="form.inspectionUnit" placeholder="请输入检查单位" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="inspectionContact" label="检查单位联系人" >
<a-input v-model="form.inspectionContact" placeholder="请输入联系人" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="inspectionPhone" label="检查单位联系电话" >
<a-input v-model="form.inspectionPhone" placeholder="请输入联系电话" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
2025-07-14 11:11:33 +08:00
</a-row>
<!-- 风场信息 -->
<a-divider orientation="left">风场信息</a-divider>
<a-row :gutter="16">
2025-06-27 19:54:42 +08:00
<a-col :span="12">
2025-07-14 11:11:33 +08:00
<a-form-item field="farmName" label="风场名称" >
<a-input v-model="form.farmName" placeholder="请输入风场名称" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="farmAddress" label="风场地址" >
<a-input v-model="form.farmAddress" placeholder="请输入风场地址" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
2025-07-14 11:11:33 +08:00
<a-col :span="12">
<a-form-item field="turbineModel" label="风机型号" >
<a-input v-model="form.turbineModel" placeholder="请输入风机型号" />
2025-06-27 19:54:42 +08:00
</a-form-item>
</a-col>
</a-row>
2025-07-14 11:11:33 +08:00
<!-- 项目团队 -->
<a-divider orientation="left">项目团队</a-divider>
2025-06-27 19:54:42 +08:00
<a-row :gutter="16">
2025-07-14 11:11:33 +08:00
<a-col :span="12">
<a-form-item field="constructionTeamLeaderId" label="施工组长" >
<a-select v-model="form.constructionTeamLeaderId" placeholder="请选择施工组长" :loading="userLoading">
<a-option v-for="user in userOptions" :key="user.value" :value="user.value">
{{ user.label }}
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="constructorIds" label="施工人员" >
<a-select v-model="form.constructorIds" multiple placeholder="请选择施工人员" :loading="userLoading">
<a-option v-for="user in userOptions" :key="user.value" :value="user.value">
{{ user.label }}
</a-option>
2025-06-27 19:54:42 +08:00
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
2025-07-14 11:11:33 +08:00
<a-col :span="12">
<a-form-item field="qualityOfficerId" label="质量员" >
<a-select v-model="form.qualityOfficerId" placeholder="请选择质量员" :loading="userLoading">
<a-option v-for="user in userOptions" :key="user.value" :value="user.value">
{{ user.label }}
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="auditorId" label="安全员" >
<a-select v-model="form.auditorId" placeholder="请选择安全员" :loading="userLoading">
<a-option v-for="user in userOptions" :key="user.value" :value="user.value">
{{ user.label }}
</a-option>
</a-select>
2025-06-27 19:54:42 +08:00
</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'
2025-06-30 09:14:46 +08:00
import { addProject, deleteProject, listProject, updateProject, exportProject, importProject } from '@/apis/project'
2025-06-27 19:54:42 +08:00
import { isMobile } from '@/utils'
import has from '@/utils/has'
2025-07-14 11:11:33 +08:00
import http from '@/utils/http'
2025-06-27 19:54:42 +08:00
import type { ColumnItem } from '@/components/GiForm'
import type { TableColumnData } from '@arco-design/web-vue'
2025-06-30 09:14:46 +08:00
import type { ProjectResp, ProjectPageQuery } from '@/apis/project/type'
2025-07-14 11:11:33 +08:00
import * as T from '@/apis/project/type'
2025-06-27 19:54:42 +08:00
defineOptions({ name: 'ProjectManagement' })
2025-06-30 09:14:46 +08:00
// 项目状态常量定义 (API返回数字类型)
const PROJECT_STATUS = {
NOT_STARTED: 0, // 未开始/待施工
IN_PROGRESS: 1, // 施工中
COMPLETED: 2, // 已完成
} as const
// 项目状态映射
const PROJECT_STATUS_MAP = {
0: '待施工',
1: '施工中',
2: '已完成'
} as const
// 项目状态选项
const PROJECT_STATUS_OPTIONS = [
{ label: '待施工', value: 0 },
{ label: '施工中', value: 1 },
{ label: '已完成', value: 2 }
]
// 项目类别常量定义
const PROJECT_CATEGORY = {
EXTERNAL_WORK: '外部工作',
INTERNAL_PROJECT: '内部项目',
TECHNICAL_SERVICE: '技术服务'
} as const
// 项目类别选项
const PROJECT_CATEGORY_OPTIONS = [
{ label: PROJECT_CATEGORY.EXTERNAL_WORK, value: PROJECT_CATEGORY.EXTERNAL_WORK },
{ label: PROJECT_CATEGORY.INTERNAL_PROJECT, value: PROJECT_CATEGORY.INTERNAL_PROJECT },
{ label: PROJECT_CATEGORY.TECHNICAL_SERVICE, value: PROJECT_CATEGORY.TECHNICAL_SERVICE }
]
2025-06-27 19:54:42 +08:00
const router = useRouter()
const formRef = ref()
const loading = ref(false)
const addModalVisible = ref(false)
const importModalVisible = ref(false)
const isEdit = ref(false)
2025-06-30 09:14:46 +08:00
const currentId = ref<string | null>(null)
2025-06-27 19:54:42 +08:00
const fileList = ref([])
2025-07-14 11:11:33 +08:00
const dataList = ref<T.ProjectResp[]>([])
const userLoading = ref(false)
const userOptions = ref<{ label: string; value: string }[]>([])
2025-06-27 19:54:42 +08:00
2025-07-14 11:11:33 +08:00
let searchForm = reactive<Partial<ProjectPageQuery>>({
2025-06-27 19:54:42 +08:00
projectName: '',
status: undefined,
2025-07-14 11:11:33 +08:00
fieldName: '', // 保持使用fieldName因为API类型定义中使用的是这个字段名
2025-06-27 19:54:42 +08:00
})
const queryFormColumns: ColumnItem[] = reactive([
{
type: 'input',
label: '项目名称',
field: 'projectName',
span: { xs: 24, sm: 8, xxl: 8 },
props: {
placeholder: '请输入项目名称',
},
},
{
type: 'input',
label: '风场名称',
2025-07-14 11:11:33 +08:00
field: 'fieldName', // 保持使用fieldName因为API类型定义中使用的是这个字段名
2025-06-27 19:54:42 +08:00
span: { xs: 24, sm: 8, xxl: 8 },
props: {
placeholder: '请输入风场名称',
},
},
{
type: 'select',
label: '状态',
field: 'status',
span: { xs: 24, sm: 8, xxl: 8 },
props: {
2025-06-30 09:14:46 +08:00
options: PROJECT_STATUS_OPTIONS,
2025-06-27 19:54:42 +08:00
placeholder: '请选择状态',
},
},
])
const form = reactive({
2025-07-14 11:11:33 +08:00
projectId: '', // 项目id
projectName: '', // 项目名称
projectManagerId: '', // 项目经理id
client: '', // 委托单位
clientContact: '', // 委托单位联系人
clientPhone: '', // 委托单位联系电话
inspectionUnit: '', // 检查单位
inspectionContact: '', // 检查单位联系人
inspectionPhone: '', // 检查单位联系电话
farmName: '', // 风场名称
farmAddress: '', // 风场地址
scale: '', // 项目规模
turbineModel: '', // 风机型号
status: 0, // 状态0待施工1施工中2已完工3已审核4已验收
startDate: '', // 开始时间
endDate: '', // 结束时间
coverUrl: '', // 项目封面
constructionTeamLeaderId: '', // 施工组长id
constructorIds: '', // 施工人员id
qualityOfficerId: '', // 质量员id
auditorId: '' // 安全员id
2025-06-27 19:54:42 +08:00
})
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,
},
2025-06-30 09:14:46 +08:00
{
title: '项目编号',
dataIndex: 'projectCode',
width: 120,
ellipsis: true,
tooltip: true,
fixed: !isMobile() ? 'left' : undefined,
},
2025-06-27 19:54:42 +08:00
{
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 ? '编辑项目' : '新增项目')
2025-07-14 11:11:33 +08:00
const getStatusColor = (status: number) => {
2025-06-27 19:54:42 +08:00
switch (status) {
2025-06-30 09:14:46 +08:00
case PROJECT_STATUS.IN_PROGRESS:
2025-06-27 19:54:42 +08:00
return 'blue'
2025-06-30 09:14:46 +08:00
case PROJECT_STATUS.COMPLETED:
2025-06-27 19:54:42 +08:00
return 'green'
2025-06-30 09:14:46 +08:00
case PROJECT_STATUS.NOT_STARTED:
2025-06-27 19:54:42 +08:00
return 'orange'
default:
return 'gray'
}
}
const fetchData = async () => {
loading.value = true
try {
2025-06-30 09:14:46 +08:00
const params: ProjectPageQuery = {
2025-06-27 19:54:42 +08:00
...searchForm,
page: pagination.current,
size: pagination.pageSize
2025-06-30 09:14:46 +08:00
}
const res = await listProject(params)
if (res.success && res.data) {
// API直接返回数组数据
const projects = Array.isArray(res.data) ? res.data : []
// 数据映射和兼容性处理
2025-07-14 11:11:33 +08:00
dataList.value = projects.map((item: any) => {
const mappedItem: T.ProjectResp = {
2025-06-30 09:14:46 +08:00
...item,
// 添加别名字段以保持兼容性
id: item.projectId,
fieldName: item.farmName,
fieldLocation: item.farmAddress,
commissionUnit: item.client,
commissionContact: item.clientContact,
commissionPhone: item.clientPhone,
orgNumber: item.turbineModel,
projectManager: item.projectManagerName,
2025-07-14 11:11:33 +08:00
projectScale: item.scale,
2025-06-30 09:14:46 +08:00
// 处理项目周期
2025-07-14 11:11:33 +08:00
projectPeriod: item.startDate && item.endDate ? [item.startDate, item.endDate] : []
};
return mappedItem;
})
2025-06-30 09:14:46 +08:00
// 由于API没有返回total使用当前数据长度
// 如果是完整数据可以用数据长度如果有分页需要从其他地方获取total
pagination.total = projects.length
// 如果返回的数据少于每页大小,说明已经是最后一页
if (projects.length < pagination.pageSize) {
pagination.total = (pagination.current - 1) * pagination.pageSize + projects.length
}
} else {
Message.error(res.msg || '获取数据失败')
dataList.value = []
pagination.total = 0
}
2025-06-27 19:54:42 +08:00
} catch (error) {
2025-06-30 09:14:46 +08:00
console.error('获取项目列表失败:', error)
2025-06-27 19:54:42 +08:00
Message.error('获取数据失败')
2025-06-30 09:14:46 +08:00
dataList.value = []
pagination.total = 0
2025-06-27 19:54:42 +08:00
} finally {
loading.value = false
}
}
const search = () => {
pagination.current = 1
fetchData()
}
const reset = () => {
2025-06-30 09:14:46 +08:00
// 重置搜索表单
Object.assign(searchForm, {
projectName: '',
2025-07-14 11:11:33 +08:00
fieldName: '', // 保持使用fieldName因为API类型定义中使用的是这个字段名
2025-06-30 09:14:46 +08:00
status: undefined,
})
// 重置分页并重新搜索
pagination.current = 1
2025-06-27 19:54:42 +08:00
search()
}
const onPageChange = (current: number) => {
pagination.current = current
fetchData()
}
const onPageSizeChange = (pageSize: number) => {
pagination.pageSize = pageSize
pagination.current = 1
fetchData()
}
const resetForm = () => {
2025-07-14 11:11:33 +08:00
// 重置表单字段
Object.assign(form, {
projectId: '', // 项目id
projectName: '', // 项目名称
projectManagerId: '', // 项目经理id
client: '', // 委托单位
clientContact: '', // 委托单位联系人
clientPhone: '', // 委托单位联系电话
inspectionUnit: '', // 检查单位
inspectionContact: '', // 检查单位联系人
inspectionPhone: '', // 检查单位联系电话
farmName: '', // 风场名称
farmAddress: '', // 风场地址
scale: '', // 项目规模
turbineModel: '', // 风机型号
status: 0, // 状态0待施工1施工中2已完工3已审核4已验收
startDate: '', // 开始时间
endDate: '', // 结束时间
coverUrl: '', // 项目封面
constructionTeamLeaderId: '', // 施工组长id
constructorIds: '', // 施工人员id
qualityOfficerId: '', // 质量员id
auditorId: '' // 安全员id
})
2025-06-27 19:54:42 +08:00
isEdit.value = false
currentId.value = null
}
const openAddModal = () => {
resetForm()
addModalVisible.value = true
}
2025-07-14 11:11:33 +08:00
const openEditModal = (record: T.ProjectResp) => {
2025-06-27 19:54:42 +08:00
isEdit.value = true
2025-06-30 09:14:46 +08:00
currentId.value = record.id || record.projectId || null
2025-07-14 11:11:33 +08:00
// 重置表单
2025-06-27 19:54:42 +08:00
Object.keys(form).forEach(key => {
2025-07-14 11:11:33 +08:00
// @ts-ignore
form[key] = ''
})
// 填充表单数据
Object.keys(form).forEach(key => {
if (key in record && record[key as keyof T.ProjectResp] !== undefined) {
2025-06-30 09:14:46 +08:00
// @ts-ignore - 这里需要处理类型转换
2025-07-14 11:11:33 +08:00
form[key] = record[key as keyof T.ProjectResp]
2025-06-27 19:54:42 +08:00
}
})
2025-06-30 09:14:46 +08:00
2025-07-14 11:11:33 +08:00
// 处理特殊字段映射
if (record.farmName) form.farmName = record.farmName
if (record.farmAddress) form.farmAddress = record.farmAddress
if (record.client) form.client = record.client
if (record.clientContact) form.clientContact = record.clientContact
if (record.clientPhone) form.clientPhone = record.clientPhone
if (record.turbineModel) form.turbineModel = record.turbineModel
if (record.scale) form.scale = record.scale
// 处理日期字段
if (record.startDate) form.startDate = record.startDate
if (record.endDate) form.endDate = record.endDate
2025-06-27 19:54:42 +08:00
addModalVisible.value = true
}
2025-07-14 11:11:33 +08:00
// 添加表单验证规则
const formRules = {
projectName: [{ required: true, message: '请输入项目名称' }],
}
2025-06-27 19:54:42 +08:00
2025-07-14 11:11:33 +08:00
// 添加提交加载状态
const submitLoading = ref(false)
const handleSubmit = async () => {
console.log('表单提交开始', form)
submitLoading.value = true
2025-06-27 19:54:42 +08:00
try {
2025-07-14 11:11:33 +08:00
// 表单验证
console.log('开始验证表单', formRef.value)
await formRef.value.validate()
// 准备提交的数据
const submitData = {
...form,
// 确保projectId字段正确
projectId: isEdit.value && currentId.value ? currentId.value : form.projectId,
// 处理日期格式 - 确保是字符串格式 YYYY-MM-DD
startDate: form.startDate ? (typeof form.startDate === 'string' ? form.startDate : new Date(form.startDate).toISOString().split('T')[0]) : '',
endDate: form.endDate ? (typeof form.endDate === 'string' ? form.endDate : new Date(form.endDate).toISOString().split('T')[0]) : '',
// 处理施工人员ID - 如果是数组,转换为逗号分隔的字符串
constructorIds: Array.isArray(form.constructorIds) ? form.constructorIds.join(',') : form.constructorIds
}
console.log('提交数据:', submitData)
2025-06-30 09:14:46 +08:00
let res
2025-06-27 19:54:42 +08:00
if (isEdit.value && currentId.value) {
2025-07-14 11:11:33 +08:00
// 编辑模式
console.log('编辑模式提交', currentId.value)
res = await updateProject(submitData, currentId.value)
2025-06-27 19:54:42 +08:00
Message.success('更新成功')
} else {
2025-07-14 11:11:33 +08:00
// 新增模式
console.log('新增模式提交')
res = await addProject(submitData)
2025-06-27 19:54:42 +08:00
Message.success('添加成功')
}
2025-06-30 09:14:46 +08:00
2025-07-14 11:11:33 +08:00
console.log('API响应结果:', res)
// 检查操作是否成功
2025-06-30 09:14:46 +08:00
if (res && res.success === false) {
Message.error(res.msg || '操作失败')
2025-07-14 11:11:33 +08:00
submitLoading.value = false
return
2025-06-30 09:14:46 +08:00
}
2025-07-14 11:11:33 +08:00
addModalVisible.value = false
2025-06-27 19:54:42 +08:00
fetchData()
2025-07-14 11:11:33 +08:00
} catch (error: any) {
2025-06-30 09:14:46 +08:00
console.error('项目操作失败:', error)
2025-07-14 11:11:33 +08:00
if (error && error.type === 'form') {
// 表单验证失败
console.log('表单验证失败')
} else {
Message.error(error?.message || '操作失败')
}
} finally {
submitLoading.value = false
2025-06-27 19:54:42 +08:00
}
}
2025-07-14 11:11:33 +08:00
const confirmDelete = (record: T.ProjectResp) => {
2025-06-27 19:54:42 +08:00
Modal.warning({
title: '确认删除',
content: `确定要删除项目"${record.projectName}"吗?`,
onOk: () => deleteItem(record),
})
}
2025-07-14 11:11:33 +08:00
const deleteItem = async (record: T.ProjectResp) => {
2025-06-30 09:14:46 +08:00
const projectId = record.id || record.projectId
if (!projectId) {
Message.error('项目ID不存在')
return
}
2025-06-27 19:54:42 +08:00
try {
2025-06-30 09:14:46 +08:00
const res = await deleteProject(projectId)
// 检查删除操作是否成功
if (res && res.success === false) {
Message.error(res.msg || '删除失败')
return
}
2025-06-27 19:54:42 +08:00
Message.success('删除成功')
fetchData()
} catch (error) {
2025-06-30 09:14:46 +08:00
console.error('删除项目失败:', error)
2025-06-27 19:54:42 +08:00
Message.error('删除失败')
}
}
2025-07-14 11:11:33 +08:00
const viewDetail = (record: T.ProjectResp) => {
2025-06-30 09:14:46 +08:00
const projectId = record.id || record.projectId
if (!projectId) {
Message.error('项目ID不存在')
return
}
2025-06-27 19:54:42 +08:00
router.push({
name: 'ProjectDetail',
params: {
2025-06-30 09:14:46 +08:00
id: projectId.toString()
2025-06-27 19:54:42 +08:00
}
})
}
const openImportModal = () => {
fileList.value = []
importModalVisible.value = true
}
const handleFileChange = (files: any) => {
fileList.value = files
}
const handleCancelImport = () => {
fileList.value = []
importModalVisible.value = false
}
2025-06-30 09:14:46 +08:00
const handleImport = async () => {
2025-06-27 19:54:42 +08:00
if (fileList.value.length === 0) {
Message.warning('请选择文件')
return false
}
2025-06-30 09:14:46 +08:00
try {
const fileItem = fileList.value[0] as any
const file = fileItem?.file || fileItem
if (!file) {
Message.warning('请选择有效的文件')
return false
}
// 调用导入API
const res = await importProject(file)
if (res && res.success === false) {
Message.error(res.msg || '导入失败')
return false
}
Message.success('导入成功')
handleCancelImport()
fetchData() // 重新获取数据
return true
} catch (error) {
console.error('导入项目失败:', error)
Message.error('导入失败')
return false
}
2025-06-27 19:54:42 +08:00
}
2025-06-30 09:14:46 +08:00
const exportData = async () => {
try {
const params = {
projectName: searchForm.projectName,
status: searchForm.status,
2025-07-14 11:11:33 +08:00
fieldName: searchForm.fieldName, // 保持使用fieldName因为API类型定义中使用的是这个字段名
2025-06-30 09:14:46 +08:00
}
await exportProject(params)
Message.success('导出成功')
} catch (error) {
console.error('导出项目失败:', error)
Message.error('导出失败')
}
2025-06-27 19:54:42 +08:00
}
2025-07-14 11:11:33 +08:00
// 获取用户列表
const fetchUserList = async () => {
userLoading.value = true
try {
// 调用用户列表接口
const res = await http.get('/user/list')
if (res.data && Array.isArray(res.data)) {
userOptions.value = res.data.map(item => ({
label: item.userName || item.username || item.name || item.nickName || item.account || '未命名用户',
value: item.userId || item.id || ''
}))
} else {
userOptions.value = []
}
} catch (error) {
console.error('获取用户列表失败:', error)
Message.error('获取用户列表失败')
userOptions.value = []
} finally {
userLoading.value = false
}
}
2025-06-27 19:54:42 +08:00
onMounted(() => {
fetchData()
2025-07-14 11:11:33 +08:00
fetchUserList() // 获取用户列表
2025-06-27 19:54:42 +08:00
})
</script>
<style scoped>
:deep(.arco-tag) {
margin-right: 0;
}
2025-07-14 11:11:33 +08:00
:deep(.project-form-modal) {
.arco-form-item {
margin-bottom: 16px;
}
.arco-divider {
margin: 16px 0;
font-weight: 500;
color: var(--color-text-2);
}
}
2025-06-27 19:54:42 +08:00
</style>