0513新功能优化
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import request from '@/config/axios'
|
||||
import type { ProjectPlanningVO } from '@/api/tjt/planning'
|
||||
import type { ProjectPlanningQuarterVO } from '@/api/tjt/planningQuarter'
|
||||
|
||||
export interface ProjectOutputSplitVO {
|
||||
id?: number
|
||||
@@ -45,10 +47,20 @@ export type ProjectOutputSplitSaveVO = Pick<
|
||||
| 'digitalRatio'
|
||||
>
|
||||
|
||||
export interface ProjectOutputSplitPlanningDetailVO {
|
||||
planning: ProjectPlanningVO
|
||||
outputSplit: ProjectOutputSplitVO
|
||||
quarters: ProjectPlanningQuarterVO[]
|
||||
}
|
||||
|
||||
export const getProjectOutputSplitByPlanningId = (planningId: number) => {
|
||||
return request.get({ url: '/tjt/output-split/get-by-planning', params: { planningId } })
|
||||
}
|
||||
|
||||
export const getProjectOutputSplitPlanningDetail = (planningId: number) => {
|
||||
return request.get({ url: '/tjt/output-split/planning-detail', params: { planningId } })
|
||||
}
|
||||
|
||||
export const saveProjectOutputSplit = (data: ProjectOutputSplitSaveVO) => {
|
||||
return request.put({ url: '/tjt/output-split/save', data })
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import request from '@/config/axios'
|
||||
import type { ProjectPlanningGuideDetailVO } from '@/api/tjt/planningGuideDetail'
|
||||
|
||||
export interface ProjectPlanningVO {
|
||||
id?: number
|
||||
@@ -71,6 +72,11 @@ export interface ProjectPlanningPageReqVO extends PageParam {
|
||||
createTime?: string[]
|
||||
}
|
||||
|
||||
export interface ProjectPlanningOutputEditDetailVO {
|
||||
planning: ProjectPlanningVO
|
||||
guideDetails: ProjectPlanningGuideDetailVO[]
|
||||
}
|
||||
|
||||
export const getProjectPlanningPage = (params: ProjectPlanningPageReqVO) => {
|
||||
return request.get({ url: '/tjt/planning/page', params })
|
||||
}
|
||||
@@ -79,6 +85,10 @@ export const getProjectPlanning = (id: number) => {
|
||||
return request.get({ url: '/tjt/planning/get', params: { id } })
|
||||
}
|
||||
|
||||
export const getProjectPlanningOutputEditDetail = (id: number) => {
|
||||
return request.get({ url: '/tjt/planning/output-edit-detail', params: { id } })
|
||||
}
|
||||
|
||||
export const getProjectPlanningListByProjectId = (projectId: number) => {
|
||||
return request.get({ url: '/tjt/planning/list-by-project', params: { projectId } })
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import request from '@/config/axios'
|
||||
import type { ProjectPlanningVO } from '@/api/tjt/planning'
|
||||
|
||||
export interface ProjectPlanningQuarterVO {
|
||||
id?: number
|
||||
@@ -15,6 +16,11 @@ export type ProjectPlanningQuarterSaveVO = Omit<
|
||||
'distributionAmount' | 'createTime'
|
||||
>
|
||||
|
||||
export interface ProjectPlanningQuarterPlanningDetailVO {
|
||||
planning: ProjectPlanningVO
|
||||
quarters: ProjectPlanningQuarterVO[]
|
||||
}
|
||||
|
||||
export const getProjectPlanningQuarter = (id: number) => {
|
||||
return request.get({ url: '/tjt/planning-quarter/get', params: { id } })
|
||||
}
|
||||
@@ -23,6 +29,13 @@ export const getProjectPlanningQuarterListByPlanningId = (planningId: number) =>
|
||||
return request.get({ url: '/tjt/planning-quarter/list-by-planning', params: { planningId } })
|
||||
}
|
||||
|
||||
export const getProjectPlanningQuarterPlanningDetail = (planningId: number) => {
|
||||
return request.get({
|
||||
url: '/tjt/planning-quarter/planning-detail',
|
||||
params: { planningId }
|
||||
})
|
||||
}
|
||||
|
||||
export const createProjectPlanningQuarter = (data: ProjectPlanningQuarterSaveVO) => {
|
||||
return request.post({ url: '/tjt/planning-quarter/create', data })
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export interface SpecialtyRoleSplitSaveItemVO {
|
||||
export interface SpecialtyRoleSplitBatchSaveVO {
|
||||
planningId: number
|
||||
items: SpecialtyRoleSplitSaveItemVO[]
|
||||
temporarySave?: boolean
|
||||
}
|
||||
|
||||
export const getSpecialtyRoleSplitListByPlanningId = (planningId: number) => {
|
||||
|
||||
@@ -450,11 +450,18 @@ const handleDelete = async (id: number) => {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -451,11 +451,18 @@ const handleDelete = async (id: number) => {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -504,12 +504,19 @@ const handleDelete = async (id: number) => {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(async () => {
|
||||
await loadOfficeOptions()
|
||||
await getList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -236,11 +236,18 @@ const handleDelete = async (id: number) => {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -103,108 +103,126 @@
|
||||
</el-col>
|
||||
|
||||
<el-col :span="16">
|
||||
<ContentWrap v-if="currentPlanning && formData">
|
||||
<div class="mb-16px flex items-center justify-between gap-16px">
|
||||
<div>
|
||||
<div class="text-16px font-600">{{ currentPlanning.planningContent }}</div>
|
||||
<div class="mt-4px text-13px text-[var(--el-text-color-secondary)]">
|
||||
年度:{{ formData.year || '-' }},考核产值:{{ formatAmountText(formData.assessmentOutputValue) }} 元
|
||||
<ContentWrap>
|
||||
<div v-loading="quarterLoading" class="min-h-320px">
|
||||
<template v-if="currentPlanning && formData">
|
||||
<div class="mb-16px flex items-center justify-between gap-16px">
|
||||
<div>
|
||||
<div class="text-16px font-600">{{ currentPlanning.planningContent }}</div>
|
||||
<div class="mt-4px text-13px text-[var(--el-text-color-secondary)]">
|
||||
年度:{{ formData.year || '-' }},考核产值:{{
|
||||
formatAmountText(formData.assessmentOutputValue)
|
||||
}}
|
||||
元
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-12px">
|
||||
<el-button
|
||||
v-hasPermi="['tjt:output-split:update']"
|
||||
plain
|
||||
type="primary"
|
||||
@click="openEditDialog"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:edit" />
|
||||
编辑比例
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-12px">
|
||||
<el-button
|
||||
v-hasPermi="['tjt:output-split:update']"
|
||||
plain
|
||||
type="primary"
|
||||
@click="openEditDialog"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:edit" />
|
||||
编辑比例
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-descriptions :column="2" border title="基础信息">
|
||||
<el-descriptions-item label="项目名称">{{ formData.projectName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="项目任务包">{{ formData.planningContent || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="工程负责人">
|
||||
{{ getProjectLeadText(formData.projectManagerName, formData.engineeringLeaderName) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions :column="2" border title="基础信息">
|
||||
<el-descriptions-item label="项目名称">{{
|
||||
formData.projectName || '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="项目任务包">{{
|
||||
formData.planningContent || '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="工程负责人">
|
||||
{{
|
||||
getProjectLeadText(formData.projectManagerName, formData.engineeringLeaderName)
|
||||
}}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-divider content-position="left">项目层结果</el-divider>
|
||||
<el-table :data="projectResultRows" border>
|
||||
<el-table-column align="center" label="类别" min-width="160" prop="label" />
|
||||
<el-table-column align="center" label="比例" min-width="120">
|
||||
<template #default="scope">
|
||||
{{ scope.row.percentText }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="金额(元)" min-width="140">
|
||||
<template #default="scope">
|
||||
{{ formatAmountText(scope.row.amount) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-divider content-position="left">项目层结果</el-divider>
|
||||
<el-table :data="projectResultRows" border>
|
||||
<el-table-column align="center" label="类别" min-width="160" prop="label" />
|
||||
<el-table-column align="center" label="比例" min-width="120">
|
||||
<template #default="scope">
|
||||
{{ scope.row.percentText }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="金额(元)" min-width="140">
|
||||
<template #default="scope">
|
||||
{{ formatAmountText(scope.row.amount) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-divider content-position="left">专业层结果</el-divider>
|
||||
<el-table :data="specialtyResultRows" border>
|
||||
<el-table-column align="center" label="专业" min-width="140" prop="label" />
|
||||
<el-table-column align="center" label="比例" min-width="120">
|
||||
<template #default="scope">
|
||||
{{ scope.row.percentText }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="金额(元)" min-width="140">
|
||||
<template #default="scope">
|
||||
{{ formatAmountText(scope.row.amount) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-divider content-position="left">专业层结果</el-divider>
|
||||
<el-table :data="specialtyResultRows" border>
|
||||
<el-table-column align="center" label="专业" min-width="140" prop="label" />
|
||||
<el-table-column align="center" label="比例" min-width="120">
|
||||
<template #default="scope">
|
||||
{{ scope.row.percentText }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="金额(元)" min-width="140">
|
||||
<template #default="scope">
|
||||
{{ formatAmountText(scope.row.amount) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-divider content-position="left">年度分配信息</el-divider>
|
||||
<div class="mb-12px">
|
||||
<el-radio-group v-model="selectedAnnualCategory" size="small">
|
||||
<el-radio-button
|
||||
v-for="item in annualCategoryOptions"
|
||||
:key="item.value"
|
||||
:label="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-row :gutter="16" class="mb-16px">
|
||||
<el-col v-for="item in annualSummaryCards" :key="item.label" :span="8">
|
||||
<div class="rounded-8px bg-[var(--el-fill-color-light)] px-16px py-12px">
|
||||
<div class="text-12px text-[var(--el-text-color-secondary)]">{{ item.label }}</div>
|
||||
<div class="mt-6px text-18px font-600">{{ formatAmountText(item.amount) }}</div>
|
||||
<el-divider content-position="left">年度分配信息</el-divider>
|
||||
<div class="mb-12px">
|
||||
<el-radio-group v-model="selectedAnnualCategory" size="small">
|
||||
<el-radio-button
|
||||
v-for="item in annualCategoryOptions"
|
||||
:key="item.value"
|
||||
:label="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-table v-loading="quarterLoading" :data="annualDistributionRows" border>
|
||||
<el-table-column align="center" label="分配年度" min-width="120" prop="distributionYear" />
|
||||
<el-table-column
|
||||
v-for="quarter in QUARTER_OPTIONS"
|
||||
:key="String(quarter.value)"
|
||||
align="center"
|
||||
:label="quarter.label"
|
||||
min-width="150"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ formatAmountText(scope.row.quarterAmounts[Number(quarter.value)]) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="年度合计(元)" min-width="160">
|
||||
<template #default="scope">
|
||||
{{ formatAmountText(scope.row.yearTotal) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
<el-row :gutter="16" class="mb-16px">
|
||||
<el-col v-for="item in annualSummaryCards" :key="item.label" :span="8">
|
||||
<div class="rounded-8px bg-[var(--el-fill-color-light)] px-16px py-12px">
|
||||
<div class="text-12px text-[var(--el-text-color-secondary)]">{{
|
||||
item.label
|
||||
}}</div>
|
||||
<div class="mt-6px text-18px font-600">{{ formatAmountText(item.amount) }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-table :data="annualDistributionRows" border>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="分配年度"
|
||||
min-width="120"
|
||||
prop="distributionYear"
|
||||
/>
|
||||
<el-table-column
|
||||
v-for="quarter in QUARTER_OPTIONS"
|
||||
:key="String(quarter.value)"
|
||||
align="center"
|
||||
:label="quarter.label"
|
||||
min-width="150"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ formatAmountText(scope.row.quarterAmounts[Number(quarter.value)]) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="年度合计(元)" min-width="160">
|
||||
<template #default="scope">
|
||||
{{ formatAmountText(scope.row.yearTotal) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<ContentWrap v-else>
|
||||
<el-empty description="请选择合约规划后查看分配结果" />
|
||||
<el-empty v-else-if="!quarterLoading" description="请选择合约规划后查看分配结果" />
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -618,14 +636,12 @@ const loadPlanningRelatedData = async (planning: PlanningApi.ProjectPlanningVO)
|
||||
}
|
||||
quarterLoading.value = true
|
||||
try {
|
||||
const [planningDetail, outputSplit, quarterList] = await Promise.all([
|
||||
PlanningApi.getProjectPlanning(planning.id),
|
||||
OutputSplitApi.getProjectOutputSplitByPlanningId(planning.id),
|
||||
PlanningQuarterApi.getProjectPlanningQuarterListByPlanningId(planning.id)
|
||||
])
|
||||
currentPlanning.value = planningDetail
|
||||
formData.value = outputSplit
|
||||
quarterRows.value = buildQuarterRows(planningDetail, quarterList)
|
||||
const detail = await OutputSplitApi.getProjectOutputSplitPlanningDetail(planning.id)
|
||||
currentPlanning.value = detail?.planning
|
||||
formData.value = detail?.outputSplit
|
||||
quarterRows.value = detail?.planning
|
||||
? buildQuarterRows(detail.planning, detail.quarters || [])
|
||||
: []
|
||||
} finally {
|
||||
quarterLoading.value = false
|
||||
}
|
||||
@@ -723,11 +739,18 @@ const handleSave = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getProjectList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求项目与合约规划列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getProjectList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1192,12 +1192,17 @@ const open = async (id: number) => {
|
||||
dialogVisible.value = true
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = await PlanningApi.getProjectPlanning(id)
|
||||
formData.value = normalizeFormData(data)
|
||||
const detail = await PlanningApi.getProjectPlanningOutputEditDetail(id)
|
||||
if (!detail?.planning) {
|
||||
formData.value = createFormData()
|
||||
guideDetails.value = []
|
||||
dialogVisible.value = false
|
||||
message.warning('合约规划不存在或已被删除')
|
||||
return
|
||||
}
|
||||
formData.value = normalizeFormData(detail.planning)
|
||||
guideDetails.value = showGuideDetailSection.value
|
||||
? normalizeGuideDetailList(
|
||||
await PlanningGuideDetailApi.getProjectPlanningGuideDetailListByPlanningId(id)
|
||||
)
|
||||
? normalizeGuideDetailList(detail.guideDetails)
|
||||
: []
|
||||
applyCalculationRatioDefault()
|
||||
if (formData.value.reviewOutsourceRatio === undefined || formData.value.reviewOutsourceRatio === null) {
|
||||
|
||||
@@ -205,7 +205,24 @@ const open = async (id: number) => {
|
||||
loading.value = true
|
||||
deletedQuarterIds.value = []
|
||||
try {
|
||||
const planning = await PlanningApi.getProjectPlanning(id)
|
||||
const detail = await PlanningQuarterApi.getProjectPlanningQuarterPlanningDetail(id)
|
||||
if (!detail?.planning) {
|
||||
planningId.value = undefined
|
||||
formData.value = {
|
||||
projectId: 0,
|
||||
ownershipType: '',
|
||||
calculationMethod: '',
|
||||
planningContent: '',
|
||||
totalDistributionAmount: 1,
|
||||
progressRemark: ''
|
||||
}
|
||||
quarterRows.value = []
|
||||
deletedQuarterIds.value = []
|
||||
dialogVisible.value = false
|
||||
message.warning('合约规划不存在或已被删除')
|
||||
return
|
||||
}
|
||||
const planning = detail.planning
|
||||
formData.value = {
|
||||
...planning,
|
||||
contractValueQuantity: planning.contractValueQuantity ?? 1,
|
||||
@@ -214,8 +231,7 @@ const open = async (id: number) => {
|
||||
totalDistributionAmount: planning.totalDistributionAmount ?? 1,
|
||||
progressRemark: planning.progressRemark ?? ''
|
||||
}
|
||||
const quarterList = await PlanningQuarterApi.getProjectPlanningQuarterListByPlanningId(id)
|
||||
quarterRows.value = buildQuarterRows(formData.value, quarterList)
|
||||
quarterRows.value = buildQuarterRows(formData.value, detail.quarters || [])
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -166,7 +166,9 @@
|
||||
</el-col>
|
||||
|
||||
<el-col :span="15">
|
||||
<ContentWrap v-if="currentPlanning">
|
||||
<div v-loading="quarterLoading" class="min-h-320px">
|
||||
<template v-if="currentPlanning">
|
||||
<ContentWrap>
|
||||
<div class="mb-16px flex items-center justify-between gap-16px">
|
||||
<div>
|
||||
<div class="text-16px font-600">{{ currentPlanning.planningContent }}</div>
|
||||
@@ -298,7 +300,7 @@
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap v-if="currentPlanning">
|
||||
<ContentWrap>
|
||||
<div class="mb-16px flex items-center justify-between gap-16px">
|
||||
<div>
|
||||
<div class="text-14px font-600">季度分配</div>
|
||||
@@ -348,7 +350,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="quarterLoading" :data="quarterRows" border>
|
||||
<el-table :data="quarterRows" border>
|
||||
<el-table-column align="center" label="分配年度" width="150" prop="distributionYear" />
|
||||
<el-table-column
|
||||
v-for="quarter in QUARTER_OPTIONS"
|
||||
@@ -366,10 +368,12 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<ContentWrap v-else>
|
||||
<ContentWrap v-else-if="!quarterLoading">
|
||||
<el-empty description="请选择合约规划后查看测算结果和季度分配" />
|
||||
</ContentWrap>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
@@ -581,12 +585,11 @@ const getPlanningList = async () => {
|
||||
}
|
||||
|
||||
const loadPlanningDetail = async (planningId: number) => {
|
||||
const planning = await PlanningApi.getProjectPlanning(planningId)
|
||||
currentPlanning.value = planning
|
||||
quarterLoading.value = true
|
||||
try {
|
||||
const quarterList = await PlanningQuarterApi.getProjectPlanningQuarterListByPlanningId(planningId)
|
||||
quarterRows.value = buildQuarterRows(planning, quarterList)
|
||||
const detail = await PlanningQuarterApi.getProjectPlanningQuarterPlanningDetail(planningId)
|
||||
currentPlanning.value = detail?.planning
|
||||
quarterRows.value = detail?.planning ? buildQuarterRows(detail.planning, detail.quarters || []) : []
|
||||
} finally {
|
||||
quarterLoading.value = false
|
||||
}
|
||||
@@ -643,11 +646,18 @@ const handlePlanningOutputFormSuccess = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getProjectList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求项目与合约规划列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getProjectList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -159,71 +159,73 @@
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap v-if="currentProfit">
|
||||
<div class="mb-16px flex items-center justify-between gap-16px">
|
||||
<div class="text-16px font-600">{{ currentProfit.projectName }}</div>
|
||||
<el-button plain type="primary" @click="openProfitEditDialog">
|
||||
<Icon class="mr-5px" icon="ep:edit" />
|
||||
编辑盈亏参数
|
||||
</el-button>
|
||||
<ContentWrap>
|
||||
<div v-loading="detailLoading" class="min-h-220px">
|
||||
<template v-if="currentProfit">
|
||||
<div class="mb-16px flex items-center justify-between gap-16px">
|
||||
<div class="text-16px font-600">{{ currentProfit.projectName }}</div>
|
||||
<el-button plain type="primary" @click="openProfitEditDialog">
|
||||
<Icon class="mr-5px" icon="ep:edit" />
|
||||
编辑盈亏参数
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="合同产值(元)">
|
||||
{{ formatAmountText(currentProfit.contractAmount) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="最终结算金额(元)">
|
||||
{{ formatAmountText(currentProfit.finalSettlementAmount) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="测算采用金额(元)">
|
||||
<el-tooltip
|
||||
v-if="isUsingContractAmount(currentProfit)"
|
||||
content="最终结算金额未填写,当前暂按合同产值测算"
|
||||
placement="top"
|
||||
>
|
||||
<span>{{ formatAmountText(currentProfit.effectiveSettlementAmount) }}</span>
|
||||
</el-tooltip>
|
||||
<span v-else>{{ formatAmountText(currentProfit.effectiveSettlementAmount) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="项目开始年度">
|
||||
{{ currentProfit.projectStartYear || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="综合所协作金额(元)">
|
||||
{{ formatAmountText(currentProfit.comprehensivePlanningAmount) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="专业分包金额(元)">
|
||||
{{ formatAmountText(currentProfit.subcontractPlanningAmount) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="专业所产值(元)">
|
||||
{{ formatAmountText(currentProfit.majorOutputValue) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="专业所预计绩效(元)">
|
||||
{{ formatAmountText(currentProfit.majorExpectedPerformance) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="科创产值比例">
|
||||
{{ formatPercentText(currentProfit.innovationOutputRate) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="科创产值(元)">
|
||||
{{ formatAmountText(currentProfit.innovationOutputValue) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="其他成本(元)">
|
||||
{{ formatAmountText(currentProfit.otherCost) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="盈亏值(元)">
|
||||
<span :class="profitLossClass(currentProfit.profitLossValue)">
|
||||
{{ formatAmountText(currentProfit.profitLossValue) }}
|
||||
</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="盈亏百分比">
|
||||
<span :class="profitLossClass(currentProfit.profitLossValue)">
|
||||
{{ formatPercentText(currentProfit.profitLossRate) }}
|
||||
</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
|
||||
<el-empty v-else-if="!detailLoading" description="请选择项目后查看盈亏详情" />
|
||||
</div>
|
||||
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="合同产值(元)">
|
||||
{{ formatAmountText(currentProfit.contractAmount) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="最终结算金额(元)">
|
||||
{{ formatAmountText(currentProfit.finalSettlementAmount) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="测算采用金额(元)">
|
||||
<el-tooltip
|
||||
v-if="isUsingContractAmount(currentProfit)"
|
||||
content="最终结算金额未填写,当前暂按合同产值测算"
|
||||
placement="top"
|
||||
>
|
||||
<span>{{ formatAmountText(currentProfit.effectiveSettlementAmount) }}</span>
|
||||
</el-tooltip>
|
||||
<span v-else>{{ formatAmountText(currentProfit.effectiveSettlementAmount) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="项目开始年度">
|
||||
{{ currentProfit.projectStartYear || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="综合所协作金额(元)">
|
||||
{{ formatAmountText(currentProfit.comprehensivePlanningAmount) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="专业分包金额(元)">
|
||||
{{ formatAmountText(currentProfit.subcontractPlanningAmount) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="专业所产值(元)">
|
||||
{{ formatAmountText(currentProfit.majorOutputValue) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="专业所预计绩效(元)">
|
||||
{{ formatAmountText(currentProfit.majorExpectedPerformance) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="科创产值比例">
|
||||
{{ formatPercentText(currentProfit.innovationOutputRate) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="科创产值(元)">
|
||||
{{ formatAmountText(currentProfit.innovationOutputValue) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="其他成本(元)">
|
||||
{{ formatAmountText(currentProfit.otherCost) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="盈亏值(元)">
|
||||
<span :class="profitLossClass(currentProfit.profitLossValue)">
|
||||
{{ formatAmountText(currentProfit.profitLossValue) }}
|
||||
</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="盈亏百分比">
|
||||
<span :class="profitLossClass(currentProfit.profitLossValue)">
|
||||
{{ formatPercentText(currentProfit.profitLossRate) }}
|
||||
</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap v-else>
|
||||
<el-empty description="请选择项目后查看盈亏详情" />
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" title="编辑盈亏参数" width="520">
|
||||
@@ -286,6 +288,7 @@ const message = useMessage()
|
||||
const { t } = useI18n()
|
||||
|
||||
const loading = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
const total = ref(0)
|
||||
const list = ref<ProfitApi.ProjectProfitVO[]>([])
|
||||
const currentProfit = ref<ProfitApi.ProjectProfitVO>()
|
||||
@@ -337,21 +340,24 @@ const queryProjectStartYearValue = computed({
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
let targetProfit: ProfitApi.ProjectProfitVO | undefined
|
||||
try {
|
||||
const data = await ProfitApi.getProjectProfitPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
if (!list.value.length) {
|
||||
currentProfit.value = undefined
|
||||
return
|
||||
} else {
|
||||
const targetProjectId = currentProfit.value?.projectId || list.value[0].projectId
|
||||
targetProfit = list.value.find((item) => item.projectId === targetProjectId) || list.value[0]
|
||||
}
|
||||
const targetProjectId = currentProfit.value?.projectId || list.value[0].projectId
|
||||
const targetProfit = list.value.find((item) => item.projectId === targetProjectId) || list.value[0]
|
||||
await nextTick()
|
||||
profitTableRef.value?.setCurrentRow(targetProfit)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
if (targetProfit) {
|
||||
await nextTick()
|
||||
profitTableRef.value?.setCurrentRow(targetProfit)
|
||||
}
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
@@ -369,14 +375,24 @@ const handleCurrentProfitChange = async (row?: ProfitApi.ProjectProfitVO) => {
|
||||
currentProfit.value = undefined
|
||||
return
|
||||
}
|
||||
currentProfit.value = await ProfitApi.getProjectProfit(row.projectId)
|
||||
detailLoading.value = true
|
||||
try {
|
||||
currentProfit.value = await ProfitApi.getProjectProfit(row.projectId)
|
||||
} finally {
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const refreshCurrentProfit = async () => {
|
||||
if (!currentProfit.value?.projectId) {
|
||||
return
|
||||
}
|
||||
currentProfit.value = await ProfitApi.getProjectProfit(currentProfit.value.projectId)
|
||||
detailLoading.value = true
|
||||
try {
|
||||
currentProfit.value = await ProfitApi.getProjectProfit(currentProfit.value.projectId)
|
||||
} finally {
|
||||
detailLoading.value = false
|
||||
}
|
||||
await getList()
|
||||
}
|
||||
|
||||
@@ -440,11 +456,18 @@ const profitLossClass = (value?: number) => {
|
||||
const isUsingContractAmount = (row?: ProfitApi.ProjectProfitVO) =>
|
||||
!!row && Number(row.finalSettlementAmount || 0) <= 0 && Number(row.contractAmount || 0) > 0
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -526,11 +526,18 @@ const handlePlanningFormSuccess = async () => {
|
||||
await getPlanningList()
|
||||
}
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -384,11 +384,18 @@ const submitProjectBudgetExport = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getProjectList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求项目与合约规划列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getProjectList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -103,7 +103,9 @@
|
||||
</el-col>
|
||||
|
||||
<el-col :span="16">
|
||||
<ContentWrap v-if="currentPlanning && formData">
|
||||
<ContentWrap>
|
||||
<div v-loading="quarterLoading" class="min-h-320px">
|
||||
<template v-if="currentPlanning && formData">
|
||||
<div class="mb-16px flex items-center justify-between gap-16px">
|
||||
<div>
|
||||
<div class="text-16px font-600">{{ currentPlanning.planningContent }}</div>
|
||||
@@ -193,7 +195,7 @@
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-table v-loading="quarterLoading" :data="annualDistributionRows" border>
|
||||
<el-table :data="annualDistributionRows" border>
|
||||
<el-table-column align="center" label="分配年度" min-width="120" prop="distributionYear" />
|
||||
<el-table-column
|
||||
v-for="quarter in QUARTER_OPTIONS"
|
||||
@@ -212,10 +214,10 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<ContentWrap v-else>
|
||||
<el-empty description="请选择合约规划后查看导出预览" />
|
||||
<el-empty v-else-if="!quarterLoading" description="请选择合约规划后查看导出预览" />
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -566,14 +568,12 @@ const loadPlanningRelatedData = async (planning: PlanningApi.ProjectPlanningVO)
|
||||
}
|
||||
quarterLoading.value = true
|
||||
try {
|
||||
const [planningDetail, outputSplit, quarterList] = await Promise.all([
|
||||
PlanningApi.getProjectPlanning(planning.id),
|
||||
OutputSplitApi.getProjectOutputSplitByPlanningId(planning.id),
|
||||
PlanningQuarterApi.getProjectPlanningQuarterListByPlanningId(planning.id)
|
||||
])
|
||||
currentPlanning.value = planningDetail
|
||||
formData.value = outputSplit
|
||||
quarterRows.value = buildQuarterRows(planningDetail, quarterList)
|
||||
const detail = await OutputSplitApi.getProjectOutputSplitPlanningDetail(planning.id)
|
||||
currentPlanning.value = detail?.planning
|
||||
formData.value = detail?.outputSplit
|
||||
quarterRows.value = detail?.planning
|
||||
? buildQuarterRows(detail.planning, detail.quarters || [])
|
||||
: []
|
||||
} finally {
|
||||
quarterLoading.value = false
|
||||
}
|
||||
@@ -684,11 +684,18 @@ const submitProjectLeadQuarterExport = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getProjectList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求项目与合约规划列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getProjectList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -105,7 +105,9 @@
|
||||
</el-col>
|
||||
|
||||
<el-col :span="16">
|
||||
<ContentWrap v-if="currentPlanning && currentGroup">
|
||||
<ContentWrap>
|
||||
<div v-loading="previewLoading" class="min-h-320px">
|
||||
<template v-if="currentPlanning && currentGroup">
|
||||
<div class="mb-16px flex items-center justify-between gap-12px">
|
||||
<div>
|
||||
<div class="text-16px font-600">{{ currentPlanning.planningContent }}</div>
|
||||
@@ -252,10 +254,10 @@
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<ContentWrap v-else>
|
||||
<el-empty description="请选择合约规划后查看导出预览" />
|
||||
<el-empty v-else-if="!previewLoading" description="请选择合约规划后查看导出预览" />
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -545,11 +547,18 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getProjectList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求项目与合约规划列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getProjectList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -565,7 +565,14 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onActivated(() => {
|
||||
// immediate watch 已经负责首次预览加载,跳过 KeepAlive 首轮激活避免重复请求。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
refreshCurrentPreview()
|
||||
})
|
||||
|
||||
|
||||
@@ -105,7 +105,9 @@
|
||||
</el-col>
|
||||
|
||||
<el-col :span="16">
|
||||
<ContentWrap v-if="currentPlanning && currentGroup">
|
||||
<ContentWrap>
|
||||
<div v-loading="roleLoading" class="min-h-320px">
|
||||
<template v-if="currentPlanning && currentGroup">
|
||||
<div class="mb-16px flex items-center justify-between gap-12px">
|
||||
<div class="text-16px font-600">{{ currentPlanning.planningContent }}</div>
|
||||
<div class="flex items-center gap-12px">
|
||||
@@ -172,10 +174,10 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<ContentWrap v-else>
|
||||
<el-empty description="请选择合约规划后查看人员分配" />
|
||||
<el-empty v-else-if="!roleLoading" description="请选择合约规划后查看人员分配" />
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -306,7 +308,8 @@
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<el-button :loading="saveLoading" type="primary" @click="handleSave">保存</el-button>
|
||||
<el-button :loading="saveLoading" @click="handleTemporarySave">临时保存</el-button>
|
||||
<el-button :loading="saveLoading" type="primary" @click="handleSave">校验保存</el-button>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
@@ -349,6 +352,7 @@ const message = useMessage()
|
||||
|
||||
const loading = ref(false)
|
||||
const planningLoading = ref(false)
|
||||
const roleLoading = ref(false)
|
||||
const saveLoading = ref(false)
|
||||
const total = ref(0)
|
||||
const projectList = ref<ProjectApi.ProjectVO[]>([])
|
||||
@@ -374,7 +378,7 @@ const queryParams = reactive<ProjectApi.ProjectPageReqVO>({
|
||||
})
|
||||
|
||||
const getProjectRowIndex = (index: number) =>
|
||||
(queryParams.pageNo - 1) * queryParams.pageSize + index + 1
|
||||
(Number(queryParams.pageNo || 1) - 1) * Number(queryParams.pageSize || 10) + index + 1
|
||||
|
||||
const queryProjectStartYearValue = computed({
|
||||
get: () => (queryParams.projectStartYear ? String(queryParams.projectStartYear) : undefined),
|
||||
@@ -681,7 +685,8 @@ const updateRoleRatio = (row: SpecialtyRoleSplitVO, value?: number) => {
|
||||
}
|
||||
|
||||
const buildSavePersons = (
|
||||
row: SpecialtyRoleSplitVO
|
||||
row: SpecialtyRoleSplitVO,
|
||||
validate: boolean
|
||||
):
|
||||
| { persons: SpecialtyRolePersonVO[]; error?: never }
|
||||
| { persons?: never; error: string } => {
|
||||
@@ -693,35 +698,39 @@ const buildSavePersons = (
|
||||
if (!hasEmployeeId && !hasRatio) {
|
||||
continue
|
||||
}
|
||||
if (!hasEmployeeId || !hasRatio) {
|
||||
if (!validate && !hasEmployeeId) {
|
||||
continue
|
||||
}
|
||||
if (validate && (!hasEmployeeId || !hasRatio)) {
|
||||
return { error: `${row.roleName} 的员工和比例必须同时填写` }
|
||||
}
|
||||
persons.push({
|
||||
employeeId: person.employeeId,
|
||||
employeeName: employeeName || undefined,
|
||||
personRatio: roundRatio(person.personRatio)
|
||||
personRatio: roundRatio(hasRatio ? person.personRatio : 0)
|
||||
})
|
||||
}
|
||||
return { persons }
|
||||
}
|
||||
|
||||
const validateAndBuildSaveItems = () => {
|
||||
const buildSaveItems = (validate: boolean) => {
|
||||
const items: SpecialtyRoleSplitApi.SpecialtyRoleSplitSaveItemVO[] = []
|
||||
for (const group of editGroups.value) {
|
||||
let roleTotal = 0
|
||||
for (const row of group.rows) {
|
||||
const result = buildSavePersons(row)
|
||||
const result = buildSavePersons(row, validate)
|
||||
if (result.error) {
|
||||
message.warning(result.error)
|
||||
return undefined
|
||||
}
|
||||
const persons = result.persons || []
|
||||
const personTotalRatio = sumPersonRatios(persons)
|
||||
if (personTotalRatio > 1 + EPSILON) {
|
||||
if (validate && personTotalRatio > 1 + EPSILON) {
|
||||
message.warning(`${group.specialtyName}-${row.roleName} 的人员比例合计不能大于 100%`)
|
||||
return undefined
|
||||
}
|
||||
if (
|
||||
validate &&
|
||||
row.roleCode === DESIGN_ROLE_CODE &&
|
||||
Number(row.roleAmount || 0) > EPSILON &&
|
||||
persons.length === 0
|
||||
@@ -737,7 +746,7 @@ const validateAndBuildSaveItems = () => {
|
||||
persons: isProjectLeadRow(row) ? [] : persons
|
||||
})
|
||||
}
|
||||
if (Math.abs(roleTotal - 1) > EPSILON) {
|
||||
if (validate && Math.abs(roleTotal - 1) > EPSILON) {
|
||||
message.warning(`${group.specialtyName} 的角色比例合计必须等于 100%`)
|
||||
return undefined
|
||||
}
|
||||
@@ -798,10 +807,15 @@ const getPlanningList = async () => {
|
||||
}
|
||||
|
||||
const loadRoleList = async (planningId: number) => {
|
||||
const data = await SpecialtyRoleSplitApi.getSpecialtyRoleSplitListByPlanningId(planningId)
|
||||
roleList.value = cloneRoleRows(data)
|
||||
roleList.value.forEach((row) => syncRoleRatiosForGroup(roleList.value, row.specialtyCode))
|
||||
syncAllDerivedValues(roleList.value)
|
||||
roleLoading.value = true
|
||||
try {
|
||||
const data = await SpecialtyRoleSplitApi.getSpecialtyRoleSplitListByPlanningId(planningId)
|
||||
roleList.value = cloneRoleRows(data)
|
||||
roleList.value.forEach((row) => syncRoleRatiosForGroup(roleList.value, row.specialtyCode))
|
||||
syncAllDerivedValues(roleList.value)
|
||||
} finally {
|
||||
roleLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
@@ -841,11 +855,11 @@ const openEditDialog = () => {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
const saveRoleSplit = async (temporarySave: boolean) => {
|
||||
if (!currentPlanning.value?.id) {
|
||||
return
|
||||
}
|
||||
const items = validateAndBuildSaveItems()
|
||||
const items = buildSaveItems(!temporarySave)
|
||||
if (!items) {
|
||||
return
|
||||
}
|
||||
@@ -853,21 +867,35 @@ const handleSave = async () => {
|
||||
try {
|
||||
await SpecialtyRoleSplitApi.saveSpecialtyRoleSplitBatch({
|
||||
planningId: currentPlanning.value.id,
|
||||
items
|
||||
items,
|
||||
temporarySave
|
||||
})
|
||||
message.success('保存成功')
|
||||
dialogVisible.value = false
|
||||
message.success(temporarySave ? '临时保存成功' : '保存成功')
|
||||
if (!temporarySave) {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
await loadRoleList(currentPlanning.value.id)
|
||||
} finally {
|
||||
saveLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleTemporarySave = () => saveRoleSplit(true)
|
||||
|
||||
const handleSave = () => saveRoleSplit(false)
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getProjectList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求项目与合约规划列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getProjectList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -299,11 +299,18 @@ const handleDelete = async (id: number) => {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
let activatedOnce = false
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
// KeepAlive 首次挂载也会触发 onActivated,跳过首轮避免重复请求列表。
|
||||
if (!activatedOnce) {
|
||||
activatedOnce = true
|
||||
return
|
||||
}
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user