Files
tjt_czjs_ui/src/views/tjt/output-split/index.vue

737 lines
24 KiB
Vue
Raw Normal View History

2026-04-17 18:17:42 +08:00
<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="projectStartYear">
<el-date-picker
v-model="queryProjectStartYearValue"
class="!w-180px"
clearable
placeholder="请选择年度"
type="year"
value-format="YYYY"
/>
</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-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table
ref="projectTableRef"
v-loading="loading"
:data="projectList"
highlight-current-row
@current-change="handleCurrentProjectChange"
>
2026-04-25 18:10:45 +08:00
<el-table-column align="center" label="项目ID" prop="id" width="88" />
2026-04-17 18:17:42 +08:00
<el-table-column align="center" label="工程名称" min-width="220" prop="projectName" />
2026-04-25 18:10:45 +08:00
<el-table-column align="center" label="项目负责人" min-width="180">
<template #default="scope">
{{ getProjectLeadText(scope.row.projectManagerName, scope.row.engineeringPrincipalName) }}
</template>
</el-table-column>
<el-table-column align="center" label="开始年度" prop="projectStartYear" width="120" />
2026-04-17 18:17:42 +08:00
</el-table>
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getProjectList"
/>
</ContentWrap>
<el-row :gutter="16">
<el-col :span="8">
<ContentWrap>
2026-04-25 18:10:45 +08:00
<div class="mb-12px text-14px font-600">
{{ currentProject?.projectName || '专业所规划列表' }}
2026-04-17 18:17:42 +08:00
</div>
<el-table
ref="planningTableRef"
v-loading="planningLoading"
:data="planningList"
highlight-current-row
@current-change="handleCurrentPlanningChange"
>
<el-table-column align="center" label="规划内容" min-width="180" prop="planningContent" />
<el-table-column align="center" label="开始年度" prop="planningStartYear" width="100" />
<el-table-column align="center" label="考核产值(元)" width="120">
<template #default="scope">
{{ formatAmountText(scope.row.assessmentOutputValue) }}
</template>
</el-table-column>
</el-table>
</ContentWrap>
</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) }}
</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>
2026-04-25 18:10:45 +08:00
<el-descriptions-item label="项目负责人">
{{ getProjectLeadText(formData.projectManagerName, formData.engineeringLeaderName) }}
</el-descriptions-item>
2026-04-17 18:17:42 +08:00
</el-descriptions>
<el-divider content-position="left">项目层结果</el-divider>
<el-table :data="projectResultRows" border>
2026-04-25 18:10:45 +08:00
<el-table-column align="center" label="类别" min-width="160" prop="label" />
2026-04-17 18:17:42 +08:00
<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>
2026-04-25 18:10:45 +08:00
<div class="mb-12px">
2026-04-17 18:17:42 +08:00
<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">
2026-04-25 18:10:45 +08:00
<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>
2026-04-17 18:17:42 +08:00
</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>
<ContentWrap v-else>
2026-04-25 18:10:45 +08:00
<el-empty description="请选择专业所规划后查看分配结果" />
2026-04-17 18:17:42 +08:00
</ContentWrap>
</el-col>
</el-row>
2026-04-25 18:10:45 +08:00
<Dialog v-model="dialogVisible" title="编辑专业及项目总分配比例" width="920">
2026-04-17 18:17:42 +08:00
<template v-if="editForm">
<el-divider content-position="left">项目层比例编辑</el-divider>
<el-row :gutter="16">
2026-04-25 18:10:45 +08:00
<el-col v-for="item in editProjectRatioItems" :key="item.key" :span="12">
<div class="ratio-card">
<div class="ratio-title">{{ item.label }}</div>
2026-04-17 18:17:42 +08:00
<el-input-number
:model-value="item.percent"
:disabled="item.key === 'officeRatio'"
:min="0"
:precision="2"
:step="0.1"
class="!w-1/1"
controls-position="right"
@update:model-value="(value) => updateProjectRatio(item.key, value)"
/>
2026-04-25 18:10:45 +08:00
<div class="ratio-amount">金额{{ formatAmountText(item.amount) }} </div>
2026-04-17 18:17:42 +08:00
</div>
</el-col>
</el-row>
<div class="mt-8px text-12px" :class="projectRatioTotalClass">
项目层比例合计{{ projectRatioTotalText }}
</div>
<el-divider content-position="left">专业层比例编辑</el-divider>
<el-row :gutter="16">
<el-col v-for="item in editSpecialtyRatioItems" :key="item.key" :span="8" class="mb-12px">
2026-04-25 18:10:45 +08:00
<div class="ratio-card">
<div class="ratio-title">{{ item.label }}</div>
2026-04-17 18:17:42 +08:00
<el-input-number
:model-value="item.percent"
:disabled="item.key === 'archRatio'"
:min="0"
:precision="2"
:step="0.1"
class="!w-1/1"
controls-position="right"
@update:model-value="(value) => updateSpecialtyRatio(item.key, value)"
/>
2026-04-25 18:10:45 +08:00
<div class="ratio-amount">金额{{ formatAmountText(item.amount) }} </div>
2026-04-17 18:17:42 +08:00
</div>
</el-col>
</el-row>
<div class="mt-8px text-12px" :class="specialtyRatioTotalClass">
专业层比例合计{{ specialtyRatioTotalText }}
</div>
</template>
2026-04-25 18:10:45 +08:00
2026-04-17 18:17:42 +08:00
<template #footer>
<el-button :loading="saveLoading" type="primary" @click="handleSave">保存</el-button>
<el-button @click="dialogVisible = false">取消</el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import * as ProjectApi from '@/api/tjt/project'
import * as PlanningApi from '@/api/tjt/planning'
import * as PlanningQuarterApi from '@/api/tjt/planningQuarter'
import * as OutputSplitApi from '@/api/tjt/outputSplit'
import {
formatAmountText,
fromPercentValue,
isMajorOwnership,
OUTPUT_SPLIT_SPECIALTY_OPTIONS,
QUARTER_OPTIONS,
toPercentValue
} from '@/views/tjt/shared/planning'
defineOptions({ name: 'TjtOutputSplit' })
type AnnualCategoryKey =
2026-04-25 18:10:45 +08:00
| 'project_lead'
2026-04-17 18:17:42 +08:00
| 'arch'
| 'decor'
| 'struct'
| 'water'
| 'elec'
| 'hvac'
| 'digital'
interface QuarterYearRow {
distributionYear: number
quarters: PlanningQuarterApi.ProjectPlanningQuarterVO[]
}
const annualCategoryOptions: { label: string; value: AnnualCategoryKey }[] = [
2026-04-25 18:10:45 +08:00
{ label: '项目经理/项目负责人', value: 'project_lead' },
2026-04-17 18:17:42 +08:00
{ label: '建筑专业', value: 'arch' },
{ label: '装修专业', value: 'decor' },
{ label: '结构专业', value: 'struct' },
{ label: '水专业', value: 'water' },
{ label: '电气专业', value: 'elec' },
{ label: '暖通专业', value: 'hvac' },
{ label: '数字化设计专业', value: 'digital' }
]
const message = useMessage()
const loading = ref(false)
const planningLoading = ref(false)
const quarterLoading = ref(false)
const saveLoading = ref(false)
const total = ref(0)
const projectList = ref<ProjectApi.ProjectVO[]>([])
const planningList = ref<PlanningApi.ProjectPlanningVO[]>([])
const currentProject = ref<ProjectApi.ProjectVO>()
const currentPlanning = ref<PlanningApi.ProjectPlanningVO>()
const formData = ref<OutputSplitApi.ProjectOutputSplitVO>()
const editForm = ref<OutputSplitApi.ProjectOutputSplitVO>()
const quarterRows = ref<QuarterYearRow[]>([])
2026-04-25 18:10:45 +08:00
const selectedAnnualCategory = ref<AnnualCategoryKey>('project_lead')
2026-04-17 18:17:42 +08:00
const dialogVisible = ref(false)
const queryFormRef = ref()
const projectTableRef = ref()
const planningTableRef = ref()
const queryParams = reactive<ProjectApi.ProjectPageReqVO>({
pageNo: 1,
pageSize: 10,
projectName: undefined,
projectStartYear: undefined
})
const queryProjectStartYearValue = computed({
get: () => (queryParams.projectStartYear ? String(queryParams.projectStartYear) : undefined),
set: (value?: string) => {
queryParams.projectStartYear = value ? Number(value) : undefined
}
})
const toNumeric = (value?: number | string | null) => {
const numericValue = Number(value ?? 0)
return Number.isNaN(numericValue) ? 0 : numericValue
}
2026-04-25 18:10:45 +08:00
const multiplyAmount = (...values: Array<number | string | null | undefined>) => {
let result = 1
values.forEach((value) => {
result *= toNumeric(value)
})
return Number(result.toFixed(2))
}
2026-04-17 18:17:42 +08:00
const formatRatioText = (value?: number | string | null) => `${(toPercentValue(value) ?? 0).toFixed(2)}%`
2026-04-25 18:10:45 +08:00
const getRatioValue = (model: OutputSplitApi.ProjectOutputSplitVO, key: string) =>
(model as unknown as Record<string, number | undefined>)[key]
const getProjectLeadText = (projectManagerName?: string, engineeringLeaderName?: string) =>
[projectManagerName, engineeringLeaderName].filter(Boolean).join(' / ') || '-'
2026-04-17 18:17:42 +08:00
const buildProjectRows = (model?: OutputSplitApi.ProjectOutputSplitVO) => {
if (!model) {
return []
}
return [
{
2026-04-25 18:10:45 +08:00
key: 'projectLeadRatio',
label: '项目经理/项目负责人',
percentText: formatRatioText(model.projectLeadRatio),
percent: toPercentValue(model.projectLeadRatio),
amount: model.projectLeadAmount
2026-04-17 18:17:42 +08:00
},
{
key: 'officeRatio',
label: '专业所',
percentText: formatRatioText(model.officeRatio),
percent: toPercentValue(model.officeRatio),
amount: model.officeAmount
}
]
}
const buildSpecialtyRows = (model?: OutputSplitApi.ProjectOutputSplitVO) => {
if (!model) {
return []
}
const amountMap: Record<string, number | undefined> = {
arch: model.archAmount,
decor: model.decorAmount,
struct: model.structAmount,
water: model.waterAmount,
elec: model.elecAmount,
hvac: model.hvacAmount,
digital: model.digitalAmount
}
return OUTPUT_SPLIT_SPECIALTY_OPTIONS.map((item) => ({
key: `${item.value}Ratio`,
label: item.label,
2026-04-25 18:10:45 +08:00
percentText: formatRatioText(getRatioValue(model, `${item.value}Ratio`)),
percent: toPercentValue(getRatioValue(model, `${item.value}Ratio`)),
2026-04-17 18:17:42 +08:00
amount: amountMap[item.value]
}))
}
const projectResultRows = computed(() => buildProjectRows(formData.value))
const specialtyResultRows = computed(() => buildSpecialtyRows(formData.value))
const editProjectRatioItems = computed(() => buildProjectRows(editForm.value))
const editSpecialtyRatioItems = computed(() => buildSpecialtyRows(editForm.value))
const annualCategoryMeta = computed(() => {
const model = formData.value
2026-04-25 18:10:45 +08:00
const option = annualCategoryOptions.find((item) => item.value === selectedAnnualCategory.value)
if (!model || !option) {
return { label: '-', ratio: 0, totalAmount: 0 }
}
if (selectedAnnualCategory.value === 'project_lead') {
const ratio = Number(toNumeric(model.projectLeadRatio).toFixed(4))
2026-04-17 18:17:42 +08:00
return {
2026-04-25 18:10:45 +08:00
label: option.label,
ratio,
totalAmount: multiplyAmount(model.assessmentOutputValue, ratio)
2026-04-17 18:17:42 +08:00
}
}
2026-04-25 18:10:45 +08:00
const specialtyRatio = toNumeric(getRatioValue(model, `${selectedAnnualCategory.value}Ratio`))
const ratio = Number((toNumeric(model.officeRatio) * specialtyRatio).toFixed(4))
return {
label: option.label,
ratio,
totalAmount: multiplyAmount(model.assessmentOutputValue, ratio)
2026-04-17 18:17:42 +08:00
}
})
const annualDistributionRows = computed(() =>
quarterRows.value.map((row) => {
const quarterAmounts: Record<number, number> = {}
let yearTotal = 0
QUARTER_OPTIONS.forEach((quarter) => {
const quarterNo = Number(quarter.value)
const quarterAmount = multiplyAmount(
row.quarters.find((item) => item.quarterNo === quarterNo)?.distributionAmount,
annualCategoryMeta.value.ratio
)
quarterAmounts[quarterNo] = quarterAmount
yearTotal += quarterAmount
})
return {
distributionYear: row.distributionYear,
quarterAmounts,
yearTotal: Number(yearTotal.toFixed(2))
}
})
)
const annualSummaryCards = computed(() => [
{
label: '总分配',
amount: multiplyAmount(
currentPlanning.value?.assessmentOutputValue,
currentPlanning.value?.totalDistributionAmount,
annualCategoryMeta.value.ratio
2026-04-25 18:10:45 +08:00
)
2026-04-17 18:17:42 +08:00
},
{
label: '已分配',
amount: multiplyAmount(
currentPlanning.value?.assessmentOutputValue,
currentPlanning.value?.allocatedAmount,
annualCategoryMeta.value.ratio
2026-04-25 18:10:45 +08:00
)
2026-04-17 18:17:42 +08:00
},
{
label: '待分配',
amount: multiplyAmount(
currentPlanning.value?.assessmentOutputValue,
currentPlanning.value?.pendingAmount,
annualCategoryMeta.value.ratio
2026-04-25 18:10:45 +08:00
)
2026-04-17 18:17:42 +08:00
}
])
const projectRatioTotal = computed(() =>
editProjectRatioItems.value.reduce((sum, item) => sum + Number(item.percent || 0), 0)
)
const specialtyRatioTotal = computed(() =>
editSpecialtyRatioItems.value.reduce((sum, item) => sum + Number(item.percent || 0), 0)
)
const projectRatioTotalText = computed(() => `${projectRatioTotal.value.toFixed(2)}%`)
const specialtyRatioTotalText = computed(() => `${specialtyRatioTotal.value.toFixed(2)}%`)
const projectRatioTotalClass = computed(() =>
Math.abs(projectRatioTotal.value - 100) < 0.001
? 'text-[var(--el-color-success)]'
: 'text-[var(--el-color-danger)]'
)
const specialtyRatioTotalClass = computed(() =>
Math.abs(specialtyRatioTotal.value - 100) < 0.001
? 'text-[var(--el-color-success)]'
: 'text-[var(--el-color-danger)]'
)
const recalculateOfficeRatio = () => {
if (!editForm.value) {
return
}
2026-04-25 18:10:45 +08:00
editForm.value.officeRatio = Number(Math.max(0, 1 - Number(editForm.value.projectLeadRatio || 0)).toFixed(4))
2026-04-17 18:17:42 +08:00
}
const recalculateArchRatio = () => {
if (!editForm.value) {
return
}
const otherSpecialtyTotal =
Number(editForm.value.decorRatio || 0) +
Number(editForm.value.structRatio || 0) +
Number(editForm.value.waterRatio || 0) +
Number(editForm.value.elecRatio || 0) +
Number(editForm.value.hvacRatio || 0) +
Number(editForm.value.digitalRatio || 0)
editForm.value.archRatio = Number(Math.max(0, 1 - otherSpecialtyTotal).toFixed(4))
}
const buildQuarterRows = (
planning: PlanningApi.ProjectPlanningVO,
quarters: PlanningQuarterApi.ProjectPlanningQuarterVO[]
) => {
const yearSet = new Set<number>()
if (planning.planningStartYear) {
yearSet.add(planning.planningStartYear)
}
quarters.forEach((item) => yearSet.add(item.distributionYear))
if (yearSet.size === 0) {
yearSet.add(new Date().getFullYear())
}
return Array.from(yearSet)
.sort((a, b) => a - b)
.map((distributionYear) => ({
distributionYear,
quarters: QUARTER_OPTIONS.map((option) => {
const quarterNo = Number(option.value)
const match = quarters.find(
(item) =>
item.distributionYear === distributionYear && Number(item.quarterNo) === quarterNo
)
return (
match || {
planningId: planning.id!,
distributionYear,
quarterNo,
distributionRatio: undefined,
distributionAmount: 0
}
)
})
}))
}
const getProjectList = async () => {
loading.value = true
try {
const data = await ProjectApi.getProjectPage(queryParams)
projectList.value = data.list
total.value = data.total
if (!projectList.value.length) {
currentProject.value = undefined
planningList.value = []
currentPlanning.value = undefined
formData.value = undefined
quarterRows.value = []
return
}
const targetProjectId = currentProject.value?.id || projectList.value[0].id
const targetProject =
projectList.value.find((item) => item.id === targetProjectId) || projectList.value[0]
await nextTick()
projectTableRef.value?.setCurrentRow(targetProject)
} finally {
loading.value = false
}
}
const getPlanningList = async () => {
if (!currentProject.value?.id) {
planningList.value = []
currentPlanning.value = undefined
formData.value = undefined
quarterRows.value = []
return
}
planningLoading.value = true
try {
const list = await PlanningApi.getProjectPlanningListByProjectId(currentProject.value.id)
planningList.value = list.filter((item) => isMajorOwnership(item.ownershipType))
if (!planningList.value.length) {
currentPlanning.value = undefined
formData.value = undefined
quarterRows.value = []
return
}
const targetPlanningId = currentPlanning.value?.id || planningList.value[0].id
const targetPlanning =
planningList.value.find((item) => item.id === targetPlanningId) || planningList.value[0]
await nextTick()
planningTableRef.value?.setCurrentRow(targetPlanning)
} finally {
planningLoading.value = false
}
}
const loadPlanningRelatedData = async (planning: PlanningApi.ProjectPlanningVO) => {
if (!planning.id) {
return
}
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)
} finally {
quarterLoading.value = false
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getProjectList()
}
const resetQuery = () => {
queryFormRef.value?.resetFields()
handleQuery()
}
const handleCurrentProjectChange = async (row?: ProjectApi.ProjectVO) => {
currentProject.value = row || undefined
await getPlanningList()
}
const handleCurrentPlanningChange = async (row?: PlanningApi.ProjectPlanningVO) => {
currentPlanning.value = row || undefined
if (!row?.id) {
formData.value = undefined
quarterRows.value = []
return
}
await loadPlanningRelatedData(row)
}
const refreshCurrentPlanning = async () => {
if (!currentPlanning.value?.id) {
return
}
await loadPlanningRelatedData(currentPlanning.value)
}
const openEditDialog = () => {
if (!formData.value) {
return
}
editForm.value = { ...formData.value }
dialogVisible.value = true
}
const updateProjectRatio = (key: string, value?: number) => {
if (!editForm.value || key === 'officeRatio') {
return
}
2026-04-25 18:10:45 +08:00
;(editForm.value as unknown as Record<string, number | undefined>)[key] =
fromPercentValue(value) ?? 0
2026-04-17 18:17:42 +08:00
recalculateOfficeRatio()
}
const updateSpecialtyRatio = (key: string, value?: number) => {
if (!editForm.value || key === 'archRatio') {
return
}
2026-04-25 18:10:45 +08:00
;(editForm.value as unknown as Record<string, number | undefined>)[key] =
fromPercentValue(value) ?? 0
2026-04-17 18:17:42 +08:00
recalculateArchRatio()
}
const handleSave = async () => {
if (!editForm.value) {
return
}
if (Math.abs(projectRatioTotal.value - 100) >= 0.001) {
2026-04-25 18:10:45 +08:00
message.warning('项目层比例合计必须等于 100%')
2026-04-17 18:17:42 +08:00
return
}
if (Math.abs(specialtyRatioTotal.value - 100) >= 0.001) {
2026-04-25 18:10:45 +08:00
message.warning('专业层比例合计必须等于 100%')
2026-04-17 18:17:42 +08:00
return
}
saveLoading.value = true
try {
await OutputSplitApi.saveProjectOutputSplit({
planningId: editForm.value.planningId,
2026-04-25 18:10:45 +08:00
projectLeadRatio: editForm.value.projectLeadRatio,
2026-04-17 18:17:42 +08:00
officeRatio: editForm.value.officeRatio,
archRatio: editForm.value.archRatio,
decorRatio: editForm.value.decorRatio,
structRatio: editForm.value.structRatio,
waterRatio: editForm.value.waterRatio,
elecRatio: editForm.value.elecRatio,
hvacRatio: editForm.value.hvacRatio,
digitalRatio: editForm.value.digitalRatio
})
message.success('保存成功')
dialogVisible.value = false
await refreshCurrentPlanning()
} finally {
saveLoading.value = false
}
}
onMounted(() => {
getProjectList()
})
onActivated(() => {
getProjectList()
})
</script>
2026-04-25 18:10:45 +08:00
<style lang="scss" scoped>
.ratio-card {
padding: 12px;
border: 1px solid var(--el-border-color);
border-radius: 8px;
}
.ratio-title {
margin-bottom: 8px;
font-size: 13px;
font-weight: 600;
}
.ratio-amount {
margin-top: 8px;
font-size: 12px;
color: var(--el-text-color-secondary);
}
</style>