fix:施工立项项目内容中,实现了项目详情的查看以及项目编辑的内容,在项目详情的和编辑项目内容的时候都可以看到项目中的任务

This commit is contained in:
Maple 2025-08-14 17:14:41 +08:00
parent ef44f64861
commit 0f4c985a4b
1 changed files with 488 additions and 37 deletions

View File

@ -332,6 +332,249 @@
</a-form>
</a-modal>
<!-- 编辑项目弹窗与新增分离 -->
<a-modal
v-model:visible="editModalVisible"
title="编辑项目"
:ok-button-props="{ loading: submitLoading }"
width="800px"
modal-class="project-form-modal"
@cancel="() => { editModalVisible.value = false }"
@ok="handleEditSubmit"
>
<a-form
ref="editFormRef"
:model="editForm"
:rules="formRules"
layout="vertical"
:style="{ maxHeight: '70vh', overflow: 'auto', padding: '0 10px' }"
>
<a-divider orientation="left">基本信息</a-divider>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="projectName" label="项目名称" required>
<a-input v-model="editForm.projectName" placeholder="请输入项目名称" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="farmAddress" label="地址">
<a-input v-model="editForm.farmAddress" placeholder="请输入地址" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="projectManagerId" label="项目经理" required>
<a-select v-model="editForm.projectManagerId" 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-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="inspectionUnit" label="业主">
<a-input v-model="editForm.inspectionUnit" placeholder="请输入业主单位" @input="(val) => (editForm.farmName = val)" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="inspectionContact" label="业主单位联系人">
<a-input v-model="editForm.inspectionContact" placeholder="请输入联系人" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="inspectionPhone" label="业主单位联系电话">
<a-input v-model="editForm.inspectionPhone" placeholder="请输入联系电话" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="client" label="委托单位">
<a-input v-model="editForm.client" placeholder="请输入委托单位" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="clientContact" label="委托单位联系人">
<a-input v-model="editForm.clientContact" placeholder="请输入联系人" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="clientPhone" label="委托单位联系电话">
<a-input v-model="editForm.clientPhone" placeholder="请输入联系电话" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="projectOrigin" label="项目来源" :rules="[{ required: true, message: '请输入项目来源' }]">
<a-input v-model="editForm.projectOrigin" placeholder="请输入项目来源" />
</a-form-item>
</a-col>
</a-row>
<a-divider orientation="left">任务设置</a-divider>
<div class="mb-2">
<a-button type="dashed" size="small" @click="addEditTask">
<template #icon><icon-plus /></template>
新增任务
</a-button>
</div>
<div v-if="editForm.tasks.length === 0" class="text-gray-500 mb-2">暂无任务</div>
<a-space direction="vertical" fill>
<a-card v-for="(task, tIndex) in editForm.tasks" :key="tIndex" size="small">
<template #title>
<div class="flex items-center justify-between">
<span>任务 {{ tIndex + 1 }}</span>
<a-space>
<a-button size="mini" @click="addEditSubtask(tIndex)">新增子任务</a-button>
<a-button size="mini" status="danger" @click="removeEditTask(tIndex)">删除</a-button>
</a-space>
</div>
</template>
<a-row :gutter="12">
<a-col :span="8">
<a-form-item :field="`tasks.${tIndex}.taskName`" label="任务名称" required>
<a-input v-model="task.taskName" placeholder="请输入任务名称" />
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item :field="`tasks.${tIndex}.taskCode`" label="任务编号">
<a-input v-model="task.taskCode" placeholder="编号" />
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item :field="`tasks.${tIndex}.mainUserId`" label="负责人">
<a-select v-model="task.mainUserId" placeholder="选择负责人" :loading="userLoading">
<a-option v-for="u in userOptions" :key="u.value" :value="u.value">{{ u.label }}</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="4">
<a-form-item :field="`tasks.${tIndex}.scales`" label="工量">
<a-input-number v-model="task.scales" :min="0" :max="9999" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :span="8">
<a-form-item :field="`tasks.${tIndex}.planStartDate`" label="计划开始">
<a-date-picker v-model="task.planStartDate" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item :field="`tasks.${tIndex}.planEndDate`" label="计划结束">
<a-date-picker v-model="task.planEndDate" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item :field="`tasks.${tIndex}.taskGroupId`" label="任务组">
<a-input-number v-model="task.taskGroupId" :min="0" placeholder="可选" style="width: 100%" />
</a-form-item>
</a-col>
</a-row>
<!-- 子任务 -->
<div v-if="task.children && task.children.length">
<a-divider orientation="left">子任务</a-divider>
<a-card v-for="(sub, sIndex) in task.children" :key="sIndex" size="small" class="mb-2">
<template #title>
<div class="flex items-center justify-between">
<span>子任务 {{ tIndex + 1 }}-{{ sIndex + 1 }}</span>
<a-button size="mini" status="danger" @click="removeEditSubtask(tIndex, sIndex)">删除</a-button>
</div>
</template>
<a-row :gutter="12">
<a-col :span="8">
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.taskName`" label="任务名称" required>
<a-input v-model="sub.taskName" placeholder="请输入任务名称" />
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.taskCode`" label="任务编号">
<a-input v-model="sub.taskCode" placeholder="编号" />
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.mainUserId`" label="负责人">
<a-select v-model="sub.mainUserId" placeholder="选择负责人" :loading="userLoading">
<a-option v-for="u in userOptions" :key="u.value" :value="u.value">{{ u.label }}</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="4">
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.scales`" label="工量">
<a-input-number v-model="sub.scales" :min="0" :max="9999" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :span="8">
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.planStartDate`" label="计划开始">
<a-date-picker v-model="sub.planStartDate" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.planEndDate`" label="计划结束">
<a-date-picker v-model="sub.planEndDate" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.taskGroupId`" label="任务组">
<a-input-number v-model="sub.taskGroupId" :min="0" placeholder="可选" style="width: 100%" />
</a-form-item>
</a-col>
</a-row>
</a-card>
</div>
</a-card>
</a-space>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="status" label="项目状态">
<a-select v-model="editForm.status" placeholder="请选择状态">
<a-option v-for="option in PROJECT_STATUS_OPTIONS" :key="option.value" :value="option.value">
{{ option.label }}
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="scale" label="项目规模">
<a-input-number v-model="editForm.scale" placeholder="请输入项目规模" :min="0" :max="999" :step="1" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="startDate" label="开始时间">
<a-date-picker v-model="editForm.startDate" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item field="endDate" label="结束时间">
<a-date-picker v-model="editForm.endDate" 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">
@ -391,6 +634,16 @@
</a-typography-paragraph>
</a-descriptions-item>
</a-descriptions>
<a-divider orientation="left">任务列表</a-divider>
<a-table
:data="detailTasks"
:columns="detailTaskColumns as any"
:pagination="false"
size="small"
:bordered="false"
/>
</a-spin>
</a-modal>
@ -398,6 +651,53 @@
</template>
<script setup lang="ts">
const editModalVisible = ref(false)
const editFormRef = ref()
//
const editForm = reactive({
projectId: '',
projectName: '',
projectManagerId: '',
client: '',
clientContact: '',
clientPhone: '',
inspectionUnit: '',
inspectionContact: '',
inspectionPhone: '',
farmName: '',
farmAddress: '',
projectOrigin: '',
scale: '',
turbineModel: '',
status: 0,
startDate: '',
endDate: '',
constructionTeamLeaderId: '',
constructorIds: '',
qualityOfficerId: '',
auditorId: '',
tasks: [] as Array<{
taskName: string
taskCode?: string
mainUserId?: string | number
planStartDate?: string
planEndDate?: string
scales?: number
taskGroupId?: number | string
children?: Array<{
taskName: string
taskCode?: string
mainUserId?: string | number
planStartDate?: string
planEndDate?: string
scales?: number
taskGroupId?: number | string
}>
}>,
turbineList: [] as { id: number, turbineNo: string, lat?: number, lng?: number, status: 0 | 1 | 2 }[],
})
import { computed, onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { Message, Modal } from '@arco-design/web-vue'
@ -408,6 +708,18 @@ import { isMobile } from '@/utils'
import http from '@/utils/http'
import type { ColumnItem } from '@/components/GiForm'
import type { ProjectPageQuery } from '@/apis/project/type'
//
const detailTasks = ref<any[]>([])
const detailTaskColumns = [
{ title: '任务名称', dataIndex: 'taskName', ellipsis: true, tooltip: true },
{ title: '负责人', dataIndex: 'mainUserName', width: 120 },
{ title: '计划开始', dataIndex: 'planStartDate', width: 120 },
{ title: '计划结束', dataIndex: 'planEndDate', width: 120 },
{ title: '工量', dataIndex: 'scales', width: 80 },
{ title: '状态', dataIndex: 'statusLabel', width: 100 },
]
import type * as T from '@/apis/project/type'
defineOptions({ name: 'ProjectManagement' })
@ -734,6 +1046,94 @@ const fetchData = async () => {
}
} else {
Message.error(res.msg || '获取数据失败')
const handleEditSubmit = async () => {
submitLoading.value = true
try {
//
await editFormRef.value?.validate()
const normalizeDate = (d: any) => (d ? (typeof d === 'string' ? d : new Date(d).toISOString().split('T')[0]) : '')
const pickTaskFields = (t: any) => ({
mainUserId: t.mainUserId ?? '',
planEndDate: normalizeDate(t.planEndDate),
planStartDate: normalizeDate(t.planStartDate),
scales: t.scales ?? 0,
taskCode: t.taskCode ?? '',
taskGroupId: t.taskGroupId ?? '',
taskName: t.taskName ?? '',
})
const flattenTasks = (tasks: any[]) => {
const result: any[] = []
;(tasks || []).forEach((t) => {
result.push(pickTaskFields(t))
;(t.children || []).forEach((c: any) => result.push(pickTaskFields(c)))
})
return result
}
const submitData = {
auditorId: (editForm as any).auditorId || '',
bonusProvision: (editForm as any).bonusProvision ?? 0,
client: editForm.client || '',
clientContact: editForm.clientContact || '',
clientPhone: editForm.clientPhone || '',
constructionTeamLeaderId: editForm.constructionTeamLeaderId || '',
constructorIds: Array.isArray(editForm.constructorIds) ? editForm.constructorIds.join(',') : (editForm.constructorIds || ''),
coverUrl: (editForm as any).coverUrl || '',
duration: (editForm as any).duration ?? 0,
endDate: normalizeDate(editForm.endDate),
equipmentAmortization: (editForm as any).equipmentAmortization ?? 0,
farmAddress: editForm.farmAddress || '',
farmName: editForm.farmName || '',
inspectionContact: editForm.inspectionContact || '',
inspectionPhone: editForm.inspectionPhone || '',
inspectionUnit: editForm.inspectionUnit || '',
laborCost: (editForm as any).laborCost ?? 0,
othersCost: (editForm as any).othersCost ?? 0,
projectBudget: (editForm as any).projectBudget ?? 0,
projectId: editForm.projectId || currentId.value || '',
projectManagerId: editForm.projectManagerId || '',
projectName: editForm.projectName,
projectOrigin: (editForm as any).projectOrigin || '',
qualityOfficerId: editForm.qualityOfficerId || '',
scale: editForm.scale || '',
startDate: normalizeDate(editForm.startDate),
status: (editForm as any).status ?? 0,
tasks: flattenTasks(editForm.tasks as any[]),
transAccomMeals: (editForm as any).transAccomMeals ?? 0,
turbineModel: editForm.turbineModel || '',
}
if (!currentId.value && submitData.projectId) currentId.value = submitData.projectId
if (!currentId.value) {
Message.error('缺少项目ID无法更新')
submitLoading.value = false
return
}
const res = await updateProject(submitData, currentId.value)
if (res && res.success === false) {
Message.error(res.msg || '更新失败')
submitLoading.value = false
return
}
Message.success('更新成功')
editModalVisible.value = false
fetchData()
} catch (e: any) {
if (e && e.type === 'form') {
//
} else {
console.error('编辑提交失败', e)
Message.error(e?.message || '编辑失败')
}
} finally {
submitLoading.value = false
}
}
dataList.value = []
pagination.total = 0
}
@ -810,7 +1210,7 @@ const resetForm = () => {
const openAddModal = () => {
resetForm()
addModalVisible.value = true
editModalVisible.value = true
}
// /使
const addTask = () => {
@ -830,49 +1230,77 @@ const removeSubtask = (parentIndex: number, index: number) => {
}
const openEditModal = (record: T.ProjectResp) => {
const openEditModal = async (record: T.ProjectResp) => {
isEdit.value = true
currentId.value = record.id || record.projectId || null
//
//
// { code, data, msg } res.data
// await 使 async async
// try {
// const res = await getProjectDetail(currentId.value as any)
// const detail = (res as any).data || res
// Object.assign(form, {
// ...form,
// ...detail,
// })
// } catch (e) { console.error('', e) }
// tasksturbineList
resetForm()
//
Object.keys(form).forEach((key) => {
if (key in record && record[key as keyof T.ProjectResp] !== undefined) {
// @ts-expect-error -
form[key] = record[key as keyof T.ProjectResp]
}
//
Object.assign(editForm, {
projectId: '', projectName: '', projectManagerId: '', client: '', clientContact: '', clientPhone: '',
inspectionUnit: '', inspectionContact: '', inspectionPhone: '', farmName: '', farmAddress: '', projectOrigin: '',
scale: '', turbineModel: '', status: 0, startDate: '', endDate: '', constructionTeamLeaderId: '', constructorIds: '',
qualityOfficerId: '', auditorId: '', tasks: [], turbineList: [],
})
//
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
try {
if (currentId.value) {
const res = await getProjectDetail(currentId.value as any)
const detail = (res as any).data || res || {}
//
if (record.startDate) form.startDate = record.startDate
if (record.endDate) form.endDate = record.endDate
//
Object.keys(editForm).forEach((key) => {
if (key in detail && (detail as any)[key] !== undefined) {
// @ts-expect-error
editForm[key] = (detail as any)[key]
}
})
addModalVisible.value = true
//
editForm.farmName = detail.farmName || record.farmName || editForm.farmName
editForm.farmAddress = detail.farmAddress || record.farmAddress || editForm.farmAddress
editForm.client = detail.client || record.client || editForm.client
editForm.clientContact = detail.clientContact || record.clientContact || editForm.clientContact
editForm.clientPhone = detail.clientPhone || record.clientPhone || editForm.clientPhone
editForm.turbineModel = detail.turbineModel || record.turbineModel || editForm.turbineModel
editForm.scale = detail.scale || record.scale || editForm.scale
editForm.startDate = detail.startDate || record.startDate || editForm.startDate
editForm.endDate = detail.endDate || record.endDate || editForm.endDate
//
const mapTask = (t: any): any => ({
taskName: t.taskName ?? t.name ?? '',
taskCode: t.taskCode ?? t.code ?? '',
mainUserId: t.mainUserId ?? t.mainUser?.id ?? t.mainUser ?? t.ownerId ?? '',
planStartDate: t.planStartDate ?? t.startDate ?? '',
planEndDate: t.planEndDate ?? t.endDate ?? '',
scales: t.scales ?? t.workload ?? 0,
taskGroupId: t.taskGroupId ?? t.groupId ?? '',
children: Array.isArray(t.children) ? t.children.map(mapTask) : [],
})
const tasksSource: any[] = Array.isArray(detail.tasks)
? detail.tasks
: (Array.isArray((detail as any).taskList) ? (detail as any).taskList : [])
if (Array.isArray(tasksSource) && tasksSource.length) {
;(editForm.tasks as any[]) = tasksSource.map(mapTask)
}
// projectId
editForm.projectId = (detail.projectId ?? currentId.value ?? record.projectId ?? '').toString()
}
} catch (e) {
console.error('获取项目详情失败', e)
// 退使
Object.keys(editForm).forEach((key) => {
if (key in record && (record as any)[key] !== undefined) {
// @ts-expect-error
editForm[key] = (record as any)[key]
}
})
}
//
editModalVisible.value = true
}
//
@ -1049,6 +1477,29 @@ const viewDetail = async (record: T.ProjectResp) => {
data.statusLabel = PROJECT_STATUS_MAP[data.status]
}
detailData.value = data
// tasks taskList
const rawTasks: any[] = Array.isArray(data.tasks) ? data.tasks : (Array.isArray(data.taskList) ? data.taskList : [])
//
const flatten = (tasks: any[]): any[] => {
const result: any[] = []
;(tasks || []).forEach(t => {
result.push({
taskName: t.taskName ?? '-',
mainUserName: t.mainUserName ?? t.mainUserId ?? '-',
planStartDate: t.planStartDate ?? '',
planEndDate: t.planEndDate ?? '',
scales: t.scales ?? '',
status: t.status,
statusLabel: t.statusLabel ?? (typeof t.status === 'number' ? PROJECT_STATUS_MAP[t.status as 0|1|2] : t.status ?? '-')
})
if (Array.isArray(t.children) && t.children.length) {
result.push(...flatten(t.children))
}
})
return result
}
detailTasks.value = flatten(rawTasks)
} catch (e) {
console.error('获取项目详情失败:', e)
Message.error('获取项目详情失败')