Files
tjt_czjs_ui/src/views/tjt/project/index.vue
2026-04-25 18:10:45 +08:00

500 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<ContentWrap>
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="88px"
>
<el-form-item label="工程名称" prop="projectName">
<el-input
v-model="queryParams.projectName"
class="!w-240px"
clearable
placeholder="请输入工程名称"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="是否签约" prop="contractSignedFlag">
<el-select
v-model="queryParams.contractSignedFlag"
class="!w-180px"
clearable
placeholder="请选择"
>
<el-option
v-for="item in contractSignOptions"
:key="String(item.value)"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="开始年度" prop="projectStartYear">
<el-date-picker
v-model="queryProjectStartYearValue"
class="!w-180px"
clearable
placeholder="请选择年度"
type="year"
value-format="YYYY"
/>
</el-form-item>
<el-form-item label="项目状态" prop="projectStatus">
<el-select
v-model="queryParams.projectStatus"
class="!w-180px"
clearable
placeholder="请选择项目状态"
>
<el-option
v-for="item in projectStatusOptions"
:key="String(item.value)"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-260px"
end-placeholder="结束时间"
start-placeholder="开始时间"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button
v-hasPermi="['tjt:project:create']"
plain
type="primary"
@click="openProjectForm('create')"
>
<Icon class="mr-5px" icon="ep:plus" />
新增项目
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table
ref="projectTableRef"
v-loading="loading"
:data="list"
highlight-current-row
@current-change="handleCurrentProjectChange"
>
<el-table-column align="center" label="项目ID" prop="id" width="88" />
<el-table-column align="center" label="工程名称" min-width="220" prop="projectName" />
<el-table-column align="center" label="项目经理" min-width="140" prop="projectManagerName" />
<el-table-column
align="center"
label="项目负责人"
min-width="140"
prop="engineeringPrincipalName"
/>
<el-table-column align="center" label="开始年度" prop="projectStartYear" width="110" />
<el-table-column align="center" label="项目状态" width="110">
<template #default="scope">
<el-tag :type="projectStatusTagType(scope.row.projectStatus)">
{{ getProjectStatusText(scope.row.projectStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="是否封档" width="90">
<template #default="scope">
{{ scope.row.archiveFlag ? '是' : '否' }}
</template>
</el-table-column>
<el-table-column align="center" label="创建时间" prop="createTime" width="180">
<template #default="scope">
{{ formatDate(scope.row.createTime as any) }}
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="150">
<template #default="scope">
<el-button
v-hasPermi="['tjt:project:update']"
link
type="primary"
@click="openProjectForm('update', scope.row.id)"
>
编辑
</el-button>
<el-button
v-hasPermi="['tjt:project:delete']"
link
type="danger"
@click="handleDeleteProject(scope.row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/>
</ContentWrap>
<ContentWrap v-if="currentProject">
<div class="mb-16px flex items-center justify-between gap-16px">
<div>
<div class="text-16px font-600">{{ currentProject.projectName }}</div>
<div class="mt-4px text-13px text-[var(--el-text-color-secondary)]">
建设单位{{ currentProject.constructionUnitName || '-' }}项目经理{{
currentProject.projectManagerName || '-'
}}项目负责人{{ currentProject.engineeringPrincipalName || '-' }}
</div>
</div>
<el-button
v-hasPermi="['tjt:planning:create']"
plain
type="primary"
@click="openPlanningForm('create')"
>
<Icon class="mr-5px" icon="ep:plus" />
新增合约规划
</el-button>
</div>
<el-row :gutter="16">
<el-col :span="8">
<el-descriptions :column="1" border title="项目概况">
<el-descriptions-item label="工程名称">{{ currentProject.projectName }}</el-descriptions-item>
<el-descriptions-item label="工程类型">
{{ getProjectTypeText(currentProject.projectType) }}
</el-descriptions-item>
<el-descriptions-item label="工程类别">
{{ getProjectCategoryText(currentProject.projectCategory) }}
</el-descriptions-item>
<el-descriptions-item label="合同签订日期">
{{ currentProject.contractSigningDate || '-' }}
</el-descriptions-item>
<el-descriptions-item label="联系人">
{{ currentProject.contactName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="联系电话">
{{ currentProject.contactPhone || '-' }}
</el-descriptions-item>
<el-descriptions-item label="项目状态">
{{ getProjectStatusText(currentProject.projectStatus) }}
</el-descriptions-item>
<el-descriptions-item label="封档时间">
{{ currentProject.archiveTime ? formatDate(currentProject.archiveTime as any) : '-' }}
</el-descriptions-item>
<el-descriptions-item
v-if="normalizeProjectStatus(currentProject.projectStatus) === pausedStatusValue"
label="暂停原因"
>
{{ currentProject.pauseReason || '-' }}
</el-descriptions-item>
<el-descriptions-item
v-if="normalizeProjectStatus(currentProject.projectStatus) === terminatedStatusValue"
label="中止原因"
>
{{ currentProject.terminateReason || '-' }}
</el-descriptions-item>
<el-descriptions-item label="合同金额(元)">
{{ formatAmountText(currentProject.contractAmount) }}
</el-descriptions-item>
<el-descriptions-item label="工程总面积(m²)">
{{ formatAreaText(currentProject.totalConstructionArea) }}
</el-descriptions-item>
</el-descriptions>
</el-col>
<el-col :span="16">
<el-table v-loading="planningLoading" :data="planningList">
<el-table-column
align="center"
label="规划内容"
min-width="260"
prop="planningContent"
show-overflow-tooltip
/>
<el-table-column align="center" label="归属类型" width="100">
<template #default="scope">
{{ getOwnershipTypeText(scope.row.ownershipType) }}
</template>
</el-table-column>
<el-table-column align="center" label="规划金额(元)" width="130">
<template #default="scope">
{{ formatAmountText(scope.row.planningAmount) }}
</template>
</el-table-column>
<el-table-column align="center" label="管理费率" width="110">
<template #default="scope">
{{ formatPercentText(scope.row.managementFeeRate) }}
</template>
</el-table-column>
<el-table-column align="center" label="管理费(元)" width="120">
<template #default="scope">
{{ formatAmountText(scope.row.managementFee) }}
</template>
</el-table-column>
<el-table-column
align="center"
label="实施团队"
min-width="140"
prop="implementationTeam"
show-overflow-tooltip
/>
<el-table-column align="center" fixed="right" label="操作" width="140">
<template #default="scope">
<el-button
v-hasPermi="['tjt:planning:update']"
link
type="primary"
@click="openPlanningForm('update', scope.row.id)"
>
编辑
</el-button>
<el-button
v-hasPermi="['tjt:planning:delete']"
link
type="danger"
@click="handleDeletePlanning(scope.row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</ContentWrap>
<ContentWrap v-else>
<el-empty description="请选择项目后查看合约规划" />
</ContentWrap>
<ProjectForm ref="projectFormRef" @success="handleProjectFormSuccess" />
<PlanningForm ref="planningFormRef" @success="handlePlanningFormSuccess" />
</template>
<script lang="ts" setup>
import { formatDate } from '@/utils/formatTime'
import * as ProjectApi from '@/api/tjt/project'
import * as PlanningApi from '@/api/tjt/planning'
import ProjectForm from './ProjectForm.vue'
import PlanningForm from './PlanningForm.vue'
import {
CONTRACT_SIGN_OPTIONS,
OWNERSHIP_TYPE_OPTIONS,
PROJECT_CATEGORY_OPTIONS,
PROJECT_STATUS_OPTIONS,
PROJECT_TYPE_OPTIONS,
formatAmountText,
formatAreaText,
formatPercentText,
normalizeProjectStatus
} from '@/views/tjt/shared/planning'
defineOptions({ name: 'TjtProject' })
const message = useMessage()
const { t } = useI18n()
const cleanOptionLabels = <T,>(options: Array<{ label: string; value: T }>, labels: string[]) =>
options.map((item, index) => ({
label: labels[index] || item.label,
value: item.value
}))
const contractSignOptions = cleanOptionLabels(CONTRACT_SIGN_OPTIONS, ['已签约', '未签约'])
const ownershipTypeOptions = cleanOptionLabels(OWNERSHIP_TYPE_OPTIONS, ['专业所', '综合所', '专业分包'])
const projectTypeOptions = cleanOptionLabels(PROJECT_TYPE_OPTIONS, [
'建筑工程',
'精装工程',
'综合工程',
'专项设计',
'BIM设计',
'其他'
])
const projectCategoryOptions = cleanOptionLabels(PROJECT_CATEGORY_OPTIONS, [
'住宅',
'公建',
'工业',
'园林景观',
'其他'
])
const projectStatusOptions = cleanOptionLabels(PROJECT_STATUS_OPTIONS, [
'进行中',
'已完成',
'已暂停',
'已中止'
])
const completedStatusValue = projectStatusOptions[1]?.value
const pausedStatusValue = projectStatusOptions[2]?.value
const terminatedStatusValue = projectStatusOptions[3]?.value
const getOptionLabel = <T,>(options: Array<{ label: string; value: T }>, value?: T) =>
options.find((item) => item.value === value)?.label || '-'
const getOwnershipTypeText = (value?: string) => getOptionLabel(ownershipTypeOptions, value)
const getProjectTypeText = (value?: string) => getOptionLabel(projectTypeOptions, value)
const getProjectCategoryText = (value?: string) => getOptionLabel(projectCategoryOptions, value)
const getProjectStatusText = (value?: string) => getOptionLabel(projectStatusOptions, value)
const loading = ref(false)
const planningLoading = ref(false)
const total = ref(0)
const list = ref<ProjectApi.ProjectVO[]>([])
const planningList = ref<PlanningApi.ProjectPlanningVO[]>([])
const currentProject = ref<ProjectApi.ProjectVO>()
const queryFormRef = ref()
const projectTableRef = ref()
const queryParams = reactive<ProjectApi.ProjectPageReqVO>({
pageNo: 1,
pageSize: 10,
projectName: undefined,
contractSignedFlag: undefined,
projectStartYear: undefined,
projectStatus: undefined,
createTime: []
})
const queryProjectStartYearValue = computed({
get: () => (queryParams.projectStartYear ? String(queryParams.projectStartYear) : undefined),
set: (value?: string) => {
queryParams.projectStartYear = value ? Number(value) : undefined
}
})
const projectStatusTagType = (status?: string) => {
const normalizedStatus = normalizeProjectStatus(status)
if (normalizedStatus === completedStatusValue) {
return 'success'
}
if (normalizedStatus === pausedStatusValue) {
return 'warning'
}
if (normalizedStatus === terminatedStatusValue) {
return 'danger'
}
return 'primary'
}
const getList = async () => {
loading.value = true
try {
const data = await ProjectApi.getProjectPage(queryParams)
list.value = data.list
total.value = data.total
if (!list.value.length) {
currentProject.value = undefined
planningList.value = []
return
}
const targetProjectId = currentProject.value?.id || list.value[0].id
const targetProject = list.value.find((item) => item.id === targetProjectId) || list.value[0]
await nextTick()
projectTableRef.value?.setCurrentRow(targetProject)
} finally {
loading.value = false
}
}
const getPlanningList = async () => {
if (!currentProject.value?.id) {
planningList.value = []
return
}
planningLoading.value = true
try {
planningList.value = await PlanningApi.getProjectPlanningListByProjectId(currentProject.value.id)
} finally {
planningLoading.value = false
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value?.resetFields()
handleQuery()
}
const handleCurrentProjectChange = async (row?: ProjectApi.ProjectVO) => {
currentProject.value = row || undefined
await getPlanningList()
}
const projectFormRef = ref()
const openProjectForm = (type: 'create' | 'update', id?: number) => {
projectFormRef.value.open(type, id)
}
const planningFormRef = ref()
const openPlanningForm = (type: string, id?: number) => {
if (type === 'create') {
if (!currentProject.value?.id) {
message.warning('请先选择项目')
return
}
planningFormRef.value.open(type, {
projectId: currentProject.value.id,
defaultPlanningAmount: currentProject.value.contractAmount
})
return
}
planningFormRef.value.open(type, { id })
}
const handleDeleteProject = async (id: number) => {
try {
await message.delConfirm()
await ProjectApi.deleteProject(id)
message.success(t('common.delSuccess'))
await getList()
} catch {}
}
const handleDeletePlanning = async (id: number) => {
try {
await message.delConfirm()
await PlanningApi.deleteProjectPlanning(id)
message.success(t('common.delSuccess'))
await getPlanningList()
} catch {}
}
const handleProjectFormSuccess = async () => {
await getList()
}
const handlePlanningFormSuccess = async () => {
await getPlanningList()
}
onMounted(() => {
getList()
})
onActivated(() => {
getList()
})
</script>