明树Git Lab
Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
J
jt_front
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
jt_front
Commits
1e428006
Commit
1e428006
authored
Feb 09, 2026
by
zhanghan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
成本管理开发
parent
efe4b95d
Pipeline
#107145
passed with stage
in 18 seconds
Changes
5
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1229 additions
and
98 deletions
+1229
-98
FinancialTable.vue
src/components/FinancialTable.vue
+179
-76
routes.js
src/router/routes.js
+8
-2
cost.vue
src/views/elseManage/cost.vue
+178
-20
costAdd.vue
src/views/elseManage/costAdd.vue
+494
-0
costTab1.vue
src/views/elseManage/costTab1.vue
+370
-0
No files found.
src/components/FinancialTable.vue
View file @
1e428006
...
@@ -37,19 +37,67 @@
...
@@ -37,19 +37,67 @@
<span
v-else
>
</span>
<span
v-else
>
</span>
</
template
>
</
template
>
</el-table-column>
</el-table-column>
<!-- 动态时间列:改用【处理后的有效时间列表】,保留原条件渲染逻辑 -->
<!-- 🌟 新增:多级时间列表头渲染逻辑 -->
<!-- 有时间列分组 → 渲染多级表头 -->
<
template
v-if=
"hasTimeHeaderGroup"
>
<el-table-column
v-for=
"group in timeColumnGroups"
:key=
"`time-group-$
{group.key}`"
:label="group.label"
align="right"
>
<!-- 二级表头:时间列 -->
<el-table-column
<el-table-column
v-for=
"timeItem in group.children"
:key=
"`time-col-$
{timeItem.prop}`"
:label="timeItem.label"
min-width="140"
align="center"
>
<template
#
default=
"
{ row }">
<!-- 1. 文本行:多行文本输入框 -->
<el-input
v-if=
"row.isTextRow"
v-model=
"row[timeItem.prop]"
type=
"textarea"
:rows=
"2"
:disabled=
"isPreview"
placeholder=
"请输入说明"
style=
"width: 100%"
@
input=
"() => handleTextInput(row)"
/>
<!-- 2. 数字行:金额输入框 -->
<el-input-number
v-else
v-model=
"row[timeItem.prop]"
:min=
"0"
:precision=
"2"
controls-position=
"right"
:disabled=
"isPreview"
placeholder=
"请输入金额"
style=
"width: 100%"
@
change=
"() => handleFinancialChange(row)"
/>
</
template
>
</el-table-column>
</el-table-column>
</template>
<!-- 无时间列分组 → 保持原有单级表头逻辑 -->
<el-table-column
v-else
v-for=
"time in validDynamicTimeList"
v-for=
"time in validDynamicTimeList"
:key=
"`time-col-${time}`"
:key=
"`time-col-${time
.prop || time
}`"
:label=
"time"
:label=
"time
.label || time
"
min-width=
"140"
min-width=
"140"
align=
"center"
align=
"center"
>
>
<
template
#
default=
"{ row }"
>
<
template
#
default=
"{ row }"
>
<!-- 1. 文本行:多行文本输入框
,支持字符串 → 绑定内部数据
-->
<!-- 1. 文本行:多行文本输入框 -->
<el-input
<el-input
v-if=
"row.isTextRow"
v-if=
"row.isTextRow"
v-model=
"row[time]"
v-model=
"row[time
.prop || time
]"
type=
"textarea"
type=
"textarea"
:rows=
"2"
:rows=
"2"
:disabled=
"isPreview"
:disabled=
"isPreview"
...
@@ -57,10 +105,10 @@
...
@@ -57,10 +105,10 @@
style=
"width: 100%"
style=
"width: 100%"
@
input=
"() => handleTextInput(row)"
@
input=
"() => handleTextInput(row)"
/>
/>
<!-- 2. 数字行:金额输入框
(0+、2位小数)→ 绑定内部数据
-->
<!-- 2. 数字行:金额输入框 -->
<el-input-number
<el-input-number
v-else
v-else
v-model=
"row[time]"
v-model=
"row[time
.prop || time
]"
:min=
"0"
:min=
"0"
:precision=
"2"
:precision=
"2"
controls-position=
"right"
controls-position=
"right"
...
@@ -82,8 +130,7 @@
...
@@ -82,8 +130,7 @@
<
script
setup
>
<
script
setup
>
import
{
ref
,
computed
,
watch
,
nextTick
,
defineProps
,
defineEmits
}
from
"vue"
;
import
{
ref
,
computed
,
watch
,
nextTick
,
defineProps
,
defineEmits
}
from
"vue"
;
// 1. 重构Props/Emit:对齐参考组件,改用defineProps+defineEmits,隔离v-model循环
// 1. 重构Props/Emit:兼容多级表头配置
// 替代原defineModel,避免对象式v-model的循环更新问题,和参考组件统一
const
props
=
defineProps
({
const
props
=
defineProps
({
// 核心配置对象:包含indicatorList/dynamicTimeList/tableData
// 核心配置对象:包含indicatorList/dynamicTimeList/tableData
modelValue
:
{
modelValue
:
{
...
@@ -101,30 +148,95 @@ const props = defineProps({
...
@@ -101,30 +148,95 @@ const props = defineProps({
},
},
});
});
// 定义发射事件:双向绑定+业务事件
,和参考组件一致
// 定义发射事件:双向绑定+业务事件
const
emit
=
defineEmits
([
const
emit
=
defineEmits
([
"update:modelValue"
,
// v-model双向绑定更新
"update:modelValue"
,
// v-model双向绑定更新
"handleTableChange"
,
// 自定义业务变化事件
(可让父组件监听)
"handleTableChange"
,
// 自定义业务变化事件
]);
]);
// 2. 核心:创建组件内部响应式数据,完全隔离外部Props → 参考组件核心设计
// 2. 核心:创建组件内部响应式数据
// 所有数据操作先改内部,再统一发射,避免直接修改Props导致的循环更新
const
tableDataRef
=
ref
([]);
const
tableDataRef
=
ref
([]);
// 3. 处理动态时间列表:对齐参考组件,做【去重/去空/trim/兜底】→ 关键健壮性改造
// 3. 🌟 新增:处理动态时间列表(兼容单级/多级配置)
// 解决原组件直接用props中可能的无效时间列(空值/空格/重复)问题
// 支持两种格式:
const
validDynamicTimeList
=
computed
(()
=>
{
// - 单级:["一月", "二月"]
// - 多级:[{ label: "一月", prop: "一月", headerGroup: "一季度" }, ...]
const
validDynamicTimeConfig
=
computed
(()
=>
{
const
rawTimeList
=
props
.
modelValue
.
dynamicTimeList
||
[];
const
rawTimeList
=
props
.
modelValue
.
dynamicTimeList
||
[];
if
(
!
Array
.
isArray
(
rawTimeList
)
||
rawTimeList
.
length
===
0
)
{
if
(
!
Array
.
isArray
(
rawTimeList
)
||
rawTimeList
.
length
===
0
)
{
// 兜底默认值:保证表格至少有列渲染,不会空白
// 兜底默认值
return
[
"2025"
,
"2026"
,
"2027"
];
return
[
{
label
:
"2025"
,
prop
:
"2025"
,
headerGroup
:
""
},
{
label
:
"2026"
,
prop
:
"2026"
,
headerGroup
:
""
},
{
label
:
"2027"
,
prop
:
"2027"
,
headerGroup
:
""
},
];
}
// 标准化配置:统一转为对象格式(兼容字符串数组)
return
rawTimeList
.
map
((
item
)
=>
{
if
(
typeof
item
===
"string"
)
{
return
{
label
:
item
.
trim
(),
prop
:
item
.
trim
(),
headerGroup
:
""
,
// 无分组
};
}
return
{
label
:
item
.
label
?.
trim
()
||
""
,
prop
:
item
.
prop
?.
trim
()
||
item
.
label
?.
trim
()
||
""
,
headerGroup
:
item
.
headerGroup
?.
trim
()
||
""
,
};
})
.
filter
((
item
)
=>
!!
item
.
prop
);
// 过滤空值
});
// 🌟 新增:判断是否有时间列分组(多级表头开关)
const
hasTimeHeaderGroup
=
computed
(()
=>
{
return
validDynamicTimeConfig
.
value
.
some
((
item
)
=>
!!
item
.
headerGroup
);
});
// 🌟 新增:时间列分组计算(按headerGroup自动分组,保持原始顺序)
const
timeColumnGroups
=
computed
(()
=>
{
const
timeConfig
=
validDynamicTimeConfig
.
value
;
if
(
!
timeConfig
.
length
)
return
[];
// 构建分组映射
const
groupMap
=
{};
timeConfig
.
forEach
((
item
)
=>
{
// 无headerGroup用唯一标识,避免分组混乱
const
groupKey
=
item
.
headerGroup
||
`single_
${
item
.
prop
}
`
;
if
(
!
groupMap
[
groupKey
])
{
groupMap
[
groupKey
]
=
{
label
:
item
.
headerGroup
||
item
.
label
,
key
:
groupKey
,
children
:
[],
};
}
}
return
[...
new
Set
(
rawTimeList
)]
groupMap
[
groupKey
].
children
.
push
(
item
);
.
map
((
time
)
=>
(
typeof
time
===
"string"
?
time
.
trim
()
:
""
))
// 去除隐形空白
});
.
filter
((
time
)
=>
!!
time
);
// 过滤空值
// 保持原始顺序,避免分组后列乱序
const
result
=
[];
const
addedKeys
=
new
Set
();
timeConfig
.
forEach
((
item
)
=>
{
const
groupKey
=
item
.
headerGroup
||
`single_
${
item
.
prop
}
`
;
if
(
!
addedKeys
.
has
(
groupKey
))
{
result
.
push
(
groupMap
[
groupKey
]);
addedKeys
.
add
(
groupKey
);
}
});
return
result
;
});
});
// 4. 配置合法性校验:基于【处理后的有效时间列表】重构,更严谨
// 兼容原有逻辑:提取纯时间prop列表(用于数据初始化/计算合计)
const
validDynamicTimeList
=
computed
(()
=>
{
// 兼容多级/单级配置,只提取prop
return
validDynamicTimeConfig
.
value
.
map
((
item
)
=>
item
.
prop
);
});
// 4. 配置合法性校验:基于标准化后的时间配置
const
validConfig
=
computed
(()
=>
{
const
validConfig
=
computed
(()
=>
{
const
{
indicatorList
}
=
props
.
modelValue
;
const
{
indicatorList
}
=
props
.
modelValue
;
return
(
return
(
...
@@ -134,45 +246,44 @@ const validConfig = computed(() => {
...
@@ -134,45 +246,44 @@ const validConfig = computed(() => {
);
);
});
});
// 5. 工具方法:初始化行的时间字段 → 对齐参考组件initRowTimeField
// 5. 工具方法:初始化行的时间字段(适配新的时间配置)
// 针对【文本行/数字行】做差异化初始化,避免类型混乱
const
initRowTimeField
=
(
row
)
=>
{
const
initRowTimeField
=
(
row
)
=>
{
if
(
!
row
||
typeof
row
!==
"object"
)
return
;
if
(
!
row
||
typeof
row
!==
"object"
)
return
;
validDynamicTimeList
.
value
.
forEach
((
time
)
=>
{
validDynamicTimeList
.
value
.
forEach
((
time
Prop
)
=>
{
if
(
row
.
isTextRow
)
{
if
(
row
.
isTextRow
)
{
// 文本行:无值则置空字符串,保留原有字符串
// 文本行:无值则置空字符串
row
[
time
]
=
row
[
timeProp
]
=
row
[
time
]
===
undefined
||
row
[
time
]
===
null
?
""
:
String
(
row
[
time
]);
row
[
timeProp
]
===
undefined
||
row
[
timeProp
]
===
null
?
""
:
String
(
row
[
timeProp
]);
}
else
{
}
else
{
// 数字行:无值/非数字则置0
,确保数字类型
// 数字行:无值/非数字则置0
row
[
time
]
=
row
[
time
Prop
]
=
row
[
time
]
===
undefined
||
row
[
time
Prop
]
===
undefined
||
row
[
time
]
===
null
||
row
[
time
Prop
]
===
null
||
isNaN
(
Number
(
row
[
time
]))
isNaN
(
Number
(
row
[
time
Prop
]))
?
0
?
0
:
Number
(
row
[
time
]);
:
Number
(
row
[
time
Prop
]);
}
}
});
});
};
};
// 6. 工具方法:计算单行合计 → 对齐参考组件calcRowTotal
// 6. 工具方法:计算单行合计(逻辑不变,适配prop列表)
// 仅数字行生效,自动过滤文本行/非数字,解决浮点精度问题
const
calculateRowTotal
=
(
row
)
=>
{
const
calculateRowTotal
=
(
row
)
=>
{
if
(
!
row
||
typeof
row
!==
"object"
||
row
.
noTotal
||
row
.
isTextRow
)
return
0
;
if
(
!
row
||
typeof
row
!==
"object"
||
row
.
noTotal
||
row
.
isTextRow
)
return
0
;
const
total
=
validDynamicTimeList
.
value
.
reduce
((
sum
,
time
)
=>
{
const
total
=
validDynamicTimeList
.
value
.
reduce
((
sum
,
time
Prop
)
=>
{
return
sum
+
(
Number
(
row
[
time
])
||
0
);
return
sum
+
(
Number
(
row
[
time
Prop
])
||
0
);
},
0
);
},
0
);
return
Number
(
total
.
toFixed
(
2
));
//
保留2位小数,避免0.1+0.2=0.30000000000000004
return
Number
(
total
.
toFixed
(
2
));
//
解决浮点精度问题
};
};
// 7. 核心工具方法:深拷贝+数据处理 → 对齐参考组件handleTableData
// 7. 核心工具方法:深拷贝+数据处理(逻辑不变)
// 强制保留所有原有字段,仅【初始化时间字段/计算合计】,避免字段丢失
const
handleTableData
=
(
sourceIndicatorList
,
sourceTableData
)
=>
{
const
handleTableData
=
(
sourceIndicatorList
,
sourceTableData
)
=>
{
if
(
!
validConfig
.
value
)
return
[];
if
(
!
validConfig
.
value
)
return
[];
const
newData
=
[];
const
newData
=
[];
sourceIndicatorList
.
forEach
((
item
,
index
)
=>
{
sourceIndicatorList
.
forEach
((
item
,
index
)
=>
{
const
{
name
,
isTextRow
=
false
,
noTotal
=
false
}
=
item
;
const
{
name
,
isTextRow
=
false
,
noTotal
=
false
}
=
item
;
// 深拷贝源数据行
:保留所有原有字段(serialNumber/level/indicatorName等)
// 深拷贝源数据行
const
originRow
=
const
originRow
=
sourceTableData
.
find
((
row
)
=>
row
.
indicatorName
===
name
)
||
{};
sourceTableData
.
find
((
row
)
=>
row
.
indicatorName
===
name
)
||
{};
const
rowData
=
JSON
.
parse
(
const
rowData
=
JSON
.
parse
(
...
@@ -183,56 +294,50 @@ const handleTableData = (sourceIndicatorList, sourceTableData) => {
...
@@ -183,56 +294,50 @@ const handleTableData = (sourceIndicatorList, sourceTableData) => {
isTextRow
,
isTextRow
,
noTotal
,
noTotal
,
total
:
0
,
total
:
0
,
...
originRow
,
// 源数据行覆盖默认值,保留原有配置
...
originRow
,
}),
}),
);
);
// 初始化时间字段
:差异化处理文本/数字行
// 初始化时间字段
initRowTimeField
(
rowData
);
initRowTimeField
(
rowData
);
// 计算合计
:仅数字行/需要合计的行生效
// 计算合计
rowData
.
total
=
calculateRowTotal
(
rowData
);
rowData
.
total
=
calculateRowTotal
(
rowData
);
newData
.
push
(
rowData
);
newData
.
push
(
rowData
);
});
});
return
newData
;
return
newData
;
};
};
// 8. 工具方法:数据变化后统一发射 → 对齐参考组件emitDataChange
// 8. 工具方法:数据变化后统一发射(逻辑不变)
// 深拷贝后发射,保留所有字段,避免父组件修改影响内部数据,统一发射逻辑
const
emitDataChange
=
(
newInnerData
)
=>
{
const
emitDataChange
=
(
newInnerData
)
=>
{
if
(
props
.
isPreview
)
return
;
// 预览模式不发射
if
(
props
.
isPreview
)
return
;
// 深拷贝内部数据,避免引用传递
const
emitData
=
JSON
.
parse
(
JSON
.
stringify
(
newInnerData
));
const
emitData
=
JSON
.
parse
(
JSON
.
stringify
(
newInnerData
));
// 构造父组件需要的完整配置对象,保持原数据结构
const
newModelValue
=
{
const
newModelValue
=
{
...
props
.
modelValue
,
...
props
.
modelValue
,
tableData
:
emitData
,
tableData
:
emitData
,
};
};
// 统一发射:双向绑定更新 + 业务事件
emit
(
"update:modelValue"
,
newModelValue
);
emit
(
"update:modelValue"
,
newModelValue
);
emit
(
"handleTableChange"
,
newModelValue
);
emit
(
"handleTableChange"
,
newModelValue
);
};
};
// 9. 监听Props变化:同步到内部数据 → 对齐参考组件的watch逻辑
// 9. 监听Props变化:同步到内部数据(逻辑不变)
// 仅同步,不发射;nextTick确保有效时间列表先处理完成
watch
(
watch
(
()
=>
props
.
modelValue
,
()
=>
props
.
modelValue
,
async
(
newVal
)
=>
{
async
(
newVal
)
=>
{
if
(
!
validConfig
.
value
)
return
;
if
(
!
validConfig
.
value
)
return
;
await
nextTick
();
// 等待有效时间列表计算完成
await
nextTick
();
const
newTableData
=
handleTableData
(
const
newTableData
=
handleTableData
(
newVal
.
indicatorList
,
newVal
.
indicatorList
,
newVal
.
tableData
,
newVal
.
tableData
,
);
);
tableDataRef
.
value
=
newTableData
;
// 只更新内部数据,不直接修改Props
tableDataRef
.
value
=
newTableData
;
},
},
{
deep
:
true
,
immediate
:
true
},
// 深度监听+立即执行,组件挂载即初始化
{
deep
:
true
,
immediate
:
true
},
);
);
// 监听
有效时间列表变化:更新内部数据后统一发射 → 对齐参考组件
// 监听
时间配置变化:更新内部数据
watch
(
watch
(
()
=>
validDynamicTimeList
.
value
,
()
=>
validDynamicTimeList
.
value
,
()
=>
{
()
=>
{
if
(
!
validConfig
.
value
||
tableDataRef
.
value
.
length
===
0
)
return
;
if
(
!
validConfig
.
value
||
tableDataRef
.
value
.
length
===
0
)
return
;
// 重新初始化时间字段+计算合计
tableDataRef
.
value
.
forEach
((
row
)
=>
{
tableDataRef
.
value
.
forEach
((
row
)
=>
{
initRowTimeField
(
row
);
initRowTimeField
(
row
);
row
.
total
=
calculateRowTotal
(
row
);
row
.
total
=
calculateRowTotal
(
row
);
...
@@ -242,39 +347,37 @@ watch(
...
@@ -242,39 +347,37 @@ watch(
{
deep
:
true
,
immediate
:
true
},
{
deep
:
true
,
immediate
:
true
},
);
);
// 10. 数字行输入回调
→ 重构,仅更新内部数据,再统一发射
// 10. 数字行输入回调
(逻辑不变)
const
handleFinancialChange
=
(
currentRow
)
=>
{
const
handleFinancialChange
=
(
currentRow
)
=>
{
if
(
props
.
isPreview
||
!
currentRow
||
currentRow
.
noTotal
)
return
;
if
(
props
.
isPreview
||
!
currentRow
||
currentRow
.
noTotal
)
return
;
currentRow
.
total
=
calculateRowTotal
(
currentRow
);
// 仅更新内部数据的合计
currentRow
.
total
=
calculateRowTotal
(
currentRow
);
emitDataChange
(
tableDataRef
.
value
);
// 统一发射所有数据
emitDataChange
(
tableDataRef
.
value
);
};
};
// 11. 文本行输入回调
→ 重构,同步内部数据后统一发射(可扩展
)
// 11. 文本行输入回调
(逻辑不变
)
const
handleTextInput
=
(
currentRow
)
=>
{
const
handleTextInput
=
(
currentRow
)
=>
{
if
(
props
.
isPreview
||
!
currentRow
)
return
;
if
(
props
.
isPreview
||
!
currentRow
)
return
;
emitDataChange
(
tableDataRef
.
value
);
// 文本输入仅同步,统一发射
emitDataChange
(
tableDataRef
.
value
);
};
};
// 12. 表格单元格样式
→ 保留原逻辑,微调健壮性
// 12. 表格单元格样式
(保留原逻辑)
const
tableCellStyle
=
({
row
})
=>
{
const
tableCellStyle
=
({
row
})
=>
{
// 合计行高亮
if
(
row
?.
isTotal
)
{
if
(
row
?.
isTotal
)
{
return
{
background
:
"#f5f7fa"
,
fontWeight
:
"bold"
,
textAlign
:
"right"
};
return
{
background
:
"#f5f7fa"
,
fontWeight
:
"bold"
,
textAlign
:
"right"
};
}
}
// 所有行右对齐,文本行输入框单独左对齐(样式层处理)
return
{
textAlign
:
"right"
};
return
{
textAlign
:
"right"
};
};
};
</
script
>
</
script
>
<
style
scoped
lang=
"scss"
>
<
style
scoped
lang=
"scss"
>
// 外层容器:适配滚动
,和参考组件样式统一
// 外层容器:适配滚动
.mixed-table-wrap
{
.mixed-table-wrap
{
width
:
100%
;
width
:
100%
;
box-sizing
:
border-box
;
box-sizing
:
border-box
;
overflow-x
:
auto
;
overflow-x
:
auto
;
}
}
// 数字输入框样式
:对齐参考组件,填满单元格
// 数字输入框样式
:deep
(
.el-input-number
)
{
:deep
(
.el-input-number
)
{
width
:
100%
;
width
:
100%
;
}
}
...
@@ -284,14 +387,14 @@ const tableCellStyle = ({ row }) => {
...
@@ -284,14 +387,14 @@ const tableCellStyle = ({ row }) => {
width
:
100%
!
important
;
width
:
100%
!
important
;
}
}
// 文本输入框样式
:适配表格,和数字框高度对齐,保留原逻辑
// 文本输入框样式
:deep
(
.el-input__textarea
)
{
:deep
(
.el-input__textarea
)
{
resize
:
none
;
// 禁止手动拉伸
resize
:
none
;
text-align
:
left
!
important
;
// 文本左对齐,符合阅读习惯
text-align
:
left
!
important
;
min-height
:
40px
!
important
;
// 固定最小高度,和数字框对齐
min-height
:
40px
!
important
;
}
}
// 表格列头样式
:统一对齐,保留原逻辑
// 表格列头样式
:deep
(
.el-table__header-cell
)
{
:deep
(
.el-table__header-cell
)
{
text-align
:
right
!
important
;
text-align
:
right
!
important
;
&
.el-table__header-cell--align-center
{
&
.el-table__header-cell--align-center
{
...
@@ -299,14 +402,14 @@ const tableCellStyle = ({ row }) => {
...
@@ -299,14 +402,14 @@ const tableCellStyle = ({ row }) => {
}
}
}
}
// 表格单元格
:优化内边距,避免输入框溢出,保留原逻辑
// 表格单元格
:deep
(
.el-table__cell
)
{
:deep
(
.el-table__cell
)
{
padding
:
4px
8px
!
important
;
padding
:
4px
8px
!
important
;
height
:
48px
!
important
;
// 统一行高,和参考组件一致
height
:
48px
!
important
;
vertical-align
:
middle
!
important
;
vertical-align
:
middle
!
important
;
}
}
// 表格行高
:统一设置,避免行高混乱
// 表格行高
:deep
(
.el-table__row
)
{
:deep
(
.el-table__row
)
{
height
:
48px
!
important
;
height
:
48px
!
important
;
}
}
...
...
src/router/routes.js
View file @
1e428006
...
@@ -353,18 +353,24 @@ const routes = [
...
@@ -353,18 +353,24 @@ const routes = [
title
:
"成本管理"
,
title
:
"成本管理"
,
component
:
()
=>
import
(
"@/views/elseManage/cost.vue"
),
component
:
()
=>
import
(
"@/views/elseManage/cost.vue"
),
},
},
{
path
:
"/costAdd"
,
name
:
"costAdd"
,
title
:
"成本管理"
,
component
:
()
=>
import
(
"@/views/elseManage/costAdd.vue"
),
},
{
{
path
:
"/property"
,
path
:
"/property"
,
name
:
"property"
,
name
:
"property"
,
title
:
"
成本管理
"
,
title
:
"
资产管理情况
"
,
component
:
()
=>
import
(
"@/views/elseManage/property.vue"
),
component
:
()
=>
import
(
"@/views/elseManage/property.vue"
),
},
},
{
{
path
:
"/link"
,
path
:
"/link"
,
name
:
"link"
,
name
:
"link"
,
title
:
"
成本
管理"
,
title
:
"
链接
管理"
,
component
:
()
=>
import
(
"@/views/elseManage/link.vue"
),
component
:
()
=>
import
(
"@/views/elseManage/link.vue"
),
},
},
],
],
...
...
src/views/elseManage/cost.vue
View file @
1e428006
<
template
>
<
template
>
<div
class=
"building-container"
>
<div
class=
"manage-container"
>
<img
src=
"@/assets/images/building.png"
alt=
""
/>
<div
class=
"manage-wrap"
>
<div
class=
"title"
>
努力开发中……
</div>
<div
class=
"manage-header"
>
<div
class=
"header-left"
></div>
<div
class=
"header-right"
>
<el-button
type=
"primary"
@
click=
"costAdd"
>
新增
</el-button>
</div>
</div>
<div
class=
"manage-content"
v-loading=
"loading"
>
<common-table
:autoHeight=
"true"
:maxRows=
"10"
:data=
"tableData"
:columns=
"tableColumns"
:total=
"total"
:current-page=
"currentPage"
:page-size=
"pageSize"
:index=
"true"
:indexLabel=
"'序号'"
title=
""
:border=
"true"
@
size-change=
"handleSizeChange"
@
current-page-change=
"handleCurrentPageChange"
>
<template
#
operations=
"
{ row, index }">
<el-button
link
type=
"primary"
size=
"small"
@
click=
"previewStatement(row)"
>
查看
</el-button
>
<el-button
link
type=
"primary"
size=
"small"
@
click=
"editStatement(row)"
>
编辑
</el-button
>
<el-button
link
type=
"danger"
size=
"small"
@
click=
"deleteStatement(row)"
>
删除
</el-button
>
</
template
>
</common-table>
</div>
</div>
</div>
</div>
</template>
</template>
<
script
setup
></
script
>
<
script
setup
>
import
{
ref
,
onMounted
,
getCurrentInstance
}
from
"vue"
;
import
{
useRouter
}
from
"vue-router"
;
import
{
ElMessage
,
ElMessageBox
}
from
"element-plus"
;
import
CommonTable
from
"@/components/common/commonTable.vue"
;
<
style
lang=
"less"
>
const
router
=
useRouter
();
.building-container {
const
{
proxy
}
=
getCurrentInstance
();
width: 100%;
height: 100%;
let
tableData
=
ref
([]);
display: flex;
let
tableColumns
=
ref
([
flex-direction: column;
{
align-items: center;
prop
:
"projectName"
,
justify-content: center;
label
:
"项目信息"
,
img {
showOverflowTooltip
:
true
,
max-width: 300px;
},
}
{
.title {
prop
:
"yjzbLen"
,
font-size: 18px;
label
:
"一级指标"
,
color: #999;
showOverflowTooltip
:
true
,
},
{
prop
:
"ejzbLen"
,
label
:
"二级指标"
,
showOverflowTooltip
:
true
,
},
{
prop
:
"operations"
,
label
:
"操作"
,
width
:
170
,
slot
:
"operations"
,
fixed
:
"right"
,
align
:
"center"
,
},
]);
let
loading
=
ref
(
false
);
let
total
=
ref
(
0
);
let
currentPage
=
ref
(
1
);
let
pageSize
=
ref
(
10
);
// 获取列表数据
const
getStatementData
=
()
=>
{
loading
.
value
=
true
;
proxy
.
$post
({
url
:
"/api/project/getCbglList"
,
data
:
{
page
:
currentPage
.
value
,
pagesize
:
pageSize
.
value
,
},
callback
:
(
data
)
=>
{
const
countValidRows
=
(
arr
)
=>
{
if
(
!
Array
.
isArray
(
arr
))
return
0
;
return
arr
.
reduce
((
count
,
item
)
=>
{
if
(
Array
.
isArray
(
item
?.
fj
)
&&
item
.
fj
.
length
>
0
)
{
count
++
;
}
}
}
return
count
;
},
0
);
};
tableData
.
value
=
data
.
rows
.
map
((
it
)
=>
{
return
{
...
it
,
yjzbLen
:
`
${
it
.
yjzb
.
length
}
行`
,
ejzbLen
:
`
${
it
.
ejzb
.
length
}
行`
,
};
});
total
.
value
=
data
.
count
;
loading
.
value
=
false
;
},
});
};
// 分页
const
handleSizeChange
=
(
size
)
=>
{
pageSize
.
value
=
size
;
currentPage
.
value
=
1
;
getStatementData
();
};
const
handleCurrentPageChange
=
(
page
)
=>
{
currentPage
.
value
=
page
;
getStatementData
();
};
const
costAdd
=
()
=>
{
router
.
push
(
"/costAdd"
);
};
const
editStatement
=
(
item
)
=>
{
router
.
push
({
name
:
"costAdd"
,
query
:
{
id
:
item
.
id
,
},
});
};
const
previewStatement
=
(
item
)
=>
{
router
.
push
({
name
:
"costAdd"
,
query
:
{
isPreview
:
true
,
id
:
item
.
id
,
},
});
};
const
deleteStatement
=
(
item
)
=>
{
ElMessageBox
.
confirm
(
"确认删除该项?"
,
"提示"
,
{
confirmButtonText
:
"确认"
,
cancelButtonText
:
"取消"
,
type
:
"warning"
,
})
.
then
(()
=>
{
proxy
.
$post
({
url
:
"/api/project/deleteCbgl"
,
data
:
{
id
:
item
.
id
,
},
callback
:
(
data
)
=>
{
ElMessage
.
success
(
"删除成功"
);
getStatementData
();
},
});
})
.
catch
(()
=>
{});
};
onMounted
(()
=>
{
getStatementData
();
});
</
script
>
<
style
scoped
lang=
"less"
>
@import "@/styles/manage.less";
</
style
>
</
style
>
src/views/elseManage/costAdd.vue
0 → 100644
View file @
1e428006
<
template
>
<div
class=
"add-project-container"
>
<div
class=
"add-project-content"
v-loading=
"loading"
>
<div
class=
"add-project-header"
>
<div
class=
"header-left"
></div>
<div
class=
"header-right"
>
<el-button
type=
"default"
@
click=
"backClick"
>
返回
</el-button>
<template
v-if=
"!loading && !isPreview"
>
<el-button
type=
"primary"
@
click=
"saveClick"
>
保存
</el-button>
</
template
>
</div>
</div>
<div
class=
"tabs-content"
>
<div
class=
"project-tab-content"
>
<div
class=
"tab-content"
>
<el-form
:model=
"formData"
:label-width=
"200"
:disabled=
"isPreview"
>
<el-collapse
v-model=
"activeCollapse"
>
<!-- 项目信息 -->
<el-collapse-item
title=
"项目信息"
name=
"项目信息"
>
<el-row
:gutter=
"20"
>
<el-col
:span=
"12"
>
<el-form-item
label=
"项目信息"
required
>
<el-select
v-model=
"formData.projectId"
placeholder=
"请选择项目信息"
no-data-text=
"暂无数据"
@
change=
"changeProject"
>
<el-option
v-for=
"item in projectList"
:key=
"item.key"
:label=
"item.projectName"
:value=
"item.id"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-collapse-item>
<!-- 6类主体成本指标体系一览表:给costTab1传初始化数据 -->
<el-collapse-item
title=
"6类主体成本指标体系一览表"
name=
"6类主体成本指标体系一览表"
>
<costTab
ref=
"costTab1Ref"
:isPreview=
"isPreview"
:init-table-data=
"formData.yjzb"
v-if=
"formData.yjzb && formData.yjzb.length > 0"
/>
<costTab
ref=
"costTab1Ref2"
:init-table-data=
"formData.ejzb"
v-if=
"formData.ejzb && formData.ejzb.length > 0"
/>
</el-collapse-item>
</el-collapse>
</el-form>
</div>
</div>
</div>
</div>
</div>
</template>
<
script
setup
>
import
{
reactive
,
ref
,
onMounted
,
getCurrentInstance
}
from
"vue"
;
import
{
useRouter
,
useRoute
}
from
"vue-router"
;
import
{
ElMessage
}
from
"element-plus"
;
import
costTab
from
"./costTab1.vue"
;
// 路由与全局实例
const
router
=
useRouter
();
const
route
=
useRoute
();
const
{
proxy
}
=
getCurrentInstance
();
// 折叠面板默认展开项
const
activeCollapse
=
ref
([
"项目信息"
,
"6类主体成本指标体系一览表"
,
"期中履约终止移交"
,
"期满移交"
,
]);
// 🌟 新增:costTab1的ref引用,用于调用子组件方法
const
costTab1Ref
=
ref
(
null
);
// 🌟 修改1:新增ejzb对应的子组件ref引用
const
costTab1Ref2
=
ref
(
null
);
// 表单核心数据
const
formData
=
reactive
({
projectName
:
""
,
projectId
:
""
,
del
:
0
,
createdAt
:
""
,
updatedAt
:
""
,
// 🌟 新增:costTab1的初始化数据(编辑时从接口获取,新增时用默认值)
yjzb
:
[],
// 🌟 修改2:将ejzb从对象改为数组(和yjzb保持一致)
ejzb
:
[],
});
// 状态管理
const
loading
=
ref
(
false
);
const
isPreview
=
ref
(
!!
route
.
query
.
isPreview
);
const
projectList
=
ref
([]);
const
rcCgqyglId
=
ref
(
route
.
query
.
id
||
""
);
let
options
=
ref
();
// 获取项目列表数据
const
getProjectData
=
()
=>
{
proxy
.
$post
({
url
:
"/api/project/listProject"
,
data
:
{
page
:
1
,
pagesize
:
1000
,
attributes
:
[],
menuType
:
"xmjc"
},
callback
:
(
data
)
=>
{
projectList
.
value
=
data
.
rows
||
[];
},
});
};
// 选择项目同步名称
const
changeProject
=
(
val
)
=>
{
const
selectItem
=
projectList
.
value
.
find
((
item
)
=>
item
.
id
===
val
);
if
(
selectItem
)
formData
.
projectName
=
selectItem
.
projectName
;
};
// 获取单条记录详情(编辑/预览)
const
getJsqtzjcDetail
=
()
=>
{
if
(
!
rcCgqyglId
.
value
)
return
;
loading
.
value
=
true
;
proxy
.
$post
({
url
:
"/api/project/getCbgl"
,
data
:
{
id
:
rcCgqyglId
.
value
},
callback
:
(
data
)
=>
{
loading
.
value
=
false
;
Object
.
assign
(
formData
,
data
);
},
});
};
// 返回上一页
const
backClick
=
()
=>
{
router
.
back
(
-
1
);
};
// 保存/提交表单
const
saveClick
=
async
()
=>
{
if
(
!
formData
.
projectId
)
{
ElMessage
.
warning
(
"请选择项目信息"
);
return
;
}
loading
.
value
=
true
;
try
{
const
costTab1EditedData
=
costTab1Ref
.
value
?.
getEditedTableData
()
||
[];
const
costTab1EditedData2
=
costTab1Ref2
.
value
?.
getEditedTableData
()
||
[];
console
.
log
(
costTab1EditedData
,
"costTab1EditedData"
);
console
.
log
(
costTab1EditedData2
,
"costTab1EditedData2"
);
const
url
=
rcCgqyglId
.
value
?
"/api/project/updateCbgl"
:
"/api/project/createCbgl"
;
const
submitData
=
{
...
formData
,
projectId
:
formData
.
projectId
+
""
,
yjzb
:
costTab1EditedData
,
// 🌟 修改4:将ejzb的编辑后数据加入提交参数
ejzb
:
costTab1EditedData2
,
};
await
proxy
.
$post
({
url
:
url
,
data
:
submitData
,
callback
:
()
=>
{
ElMessage
.
success
(
rcCgqyglId
.
value
?
"编辑成功"
:
"新增成功"
);
router
.
back
(
-
1
);
},
});
}
catch
(
error
)
{
ElMessage
.
error
(
"保存失败,请重试"
);
console
.
error
(
"保存失败:"
,
error
);
}
finally
{
loading
.
value
=
false
;
}
};
// 页面初始化
onMounted
(()
=>
{
getProjectData
();
options
.
value
=
JSON
.
parse
(
sessionStorage
.
getItem
(
"resourceData"
));
if
(
rcCgqyglId
.
value
)
{
getJsqtzjcDetail
();
}
else
{
// yjzb 初始化数据(保持原有逻辑)
formData
.
yjzb
=
[
{
serialNumber
:
{
value
:
""
,
placeholder
:
"1"
},
entityType
:
{
value
:
""
,
placeholder
:
"交投本部"
},
rows
:
[
{
level1Code
:
{
value
:
""
,
placeholder
:
" (1) "
},
level1Name
:
{
value
:
""
,
placeholder
:
"ROA,辅以总资产周转率"
},
level1Content
:
{
value
:
""
,
placeholder
:
"ROA=净利率×总资产周转率;重资产企业要不断提高净利水平,要不加快资产变现。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"中期ROA提升2%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"①"
},
level2Name
:
{
value
:
""
,
placeholder
:
"3类费用压降比例"
},
level2Content
:
{
value
:
""
,
placeholder
:
"考核非生产性支出控制成果"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"近期降1%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(2)"
},
level1Name
:
{
value
:
""
,
placeholder
:
"成本营收比"
},
level1Content
:
{
value
:
""
,
placeholder
:
"衡量企业成本控制效率和资产盈利能力(重资产企业)。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≥能建考核值的90%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"②"
},
level2Name
:
{
value
:
""
,
placeholder
:
"每亿元“投资+营收”管理费"
},
level2Content
:
{
value
:
""
,
placeholder
:
"成本费用支出效率"
},
level2Target
:
{
value
:
""
,
placeholder
:
"近期升5%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(3)"
},
level1Name
:
{
value
:
""
,
placeholder
:
"投资控制偏差率*"
},
level1Content
:
{
value
:
""
,
placeholder
:
"考核建设项目投资控制能力。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≤0"
},
level2Code
:
{
value
:
""
,
placeholder
:
"③"
},
level2Name
:
{
value
:
""
,
placeholder
:
"概预差*"
},
level2Content
:
{
value
:
""
,
placeholder
:
"设计阶段限额设计和设计优化的工作成果"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"≥3%"
},
},
],
},
{
serialNumber
:
{
value
:
""
,
placeholder
:
"2"
},
entityType
:
{
value
:
""
,
placeholder
:
"建设项目"
},
rows
:
[
{
level1Code
:
{
value
:
""
,
placeholder
:
"(4)"
},
level1Name
:
{
value
:
""
,
placeholder
:
"投资价差收取达成率"
},
level1Content
:
{
value
:
""
,
placeholder
:
"考核投资项目设计优化、合同策划、承包穿透管理和投资价差催收4方面综合能力。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≥100%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"④"
},
level2Name
:
{
value
:
""
,
placeholder
:
"施工合同降造比例*"
},
level2Content
:
{
value
:
""
,
placeholder
:
"协同施工单位承包合同价(单价水平)较批复预算(除激励基金和风险费外)下降比例。"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"能建要求≥15%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(5)"
},
level1Name
:
{
value
:
""
,
placeholder
:
"综合融资成本率**"
},
level1Content
:
{
value
:
""
,
placeholder
:
"建设期融资成本控制水平。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≤银行业自律利率"
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑤"
},
level2Name
:
{
value
:
""
,
placeholder
:
"投资价差同步收取比例"
},
level2Content
:
{
value
:
""
,
placeholder
:
"投资价差收取同步性情况。"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"≥100%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"/"
},
level1Name
:
{
value
:
""
,
placeholder
:
""
},
level1Content
:
{
value
:
""
,
placeholder
:
""
},
level1Target
:
{
value
:
""
,
placeholder
:
""
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑥"
},
level2Name
:
{
value
:
""
,
placeholder
:
"变更索赔费用占预备费比例"
},
level2Content
:
{
value
:
""
,
placeholder
:
"衡量履约阶段投资控制质量。"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"<70%"
},
},
],
},
{
serialNumber
:
{
value
:
""
,
placeholder
:
"3"
},
entityType
:
{
value
:
""
,
placeholder
:
"运营项目"
},
rows
:
[
{
level1Code
:
{
value
:
""
,
placeholder
:
"(6)"
},
level1Name
:
{
value
:
""
,
placeholder
:
"百元营收“管理费+运行费”"
},
level1Content
:
{
value
:
""
,
placeholder
:
"管理、运行成本控制水平"
},
level1Target
:
{
value
:
""
,
placeholder
:
"同行业均值之下"
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑦"
},
level2Name
:
{
value
:
""
,
placeholder
:
"集约化、智能化措施完成率"
},
level2Content
:
{
value
:
""
,
placeholder
:
"考核公司“三定方案”等降本措施落实情况"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"100%"
},
},
],
},
];
// 🌟 修改5:ejzb 初始化数据(和yjzb结构完全一致)
formData
.
ejzb
=
[
{
serialNumber
:
{
value
:
""
,
placeholder
:
"4"
},
entityType
:
{
value
:
""
,
placeholder
:
" 工程建设类 "
},
rows
:
[
{
level1Code
:
{
value
:
""
,
placeholder
:
"(7)"
},
level1Name
:
{
value
:
""
,
placeholder
:
" 累计养护成本总成本比例 "
},
level1Content
:
{
value
:
""
,
placeholder
:
" 综合养护能力和养护成本控制水平。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
" 三块算养护成本 "
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑧"
},
level2Name
:
{
value
:
""
,
placeholder
:
" 百公里养护成本 "
},
level2Content
:
{
value
:
""
,
placeholder
:
" 在 POI 等养护质量指标达标前提下,养护成本控制水平 "
,
},
level2Target
:
{
value
:
""
,
placeholder
:
" 降低 1%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(8)"
},
level1Name
:
{
value
:
""
,
placeholder
:
" 综合融资成本率 "
},
level1Content
:
{
value
:
""
,
placeholder
:
" 运营期融资成本控制水平。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
" 三目制利率 + 10BP"
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑨"
},
level2Name
:
{
value
:
""
,
placeholder
:
" 车车通收费人数 "
},
level2Content
:
{
value
:
""
,
placeholder
:
" 考核智慧化改造、少人化收费工作落实情况 "
,
},
level2Target
:
{
value
:
""
,
placeholder
:
" 降低 15%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(9)"
},
level1Name
:
{
value
:
""
,
placeholder
:
" 成本营收比 "
},
level1Content
:
{
value
:
""
,
placeholder
:
" 公司成本管控水平和业务盈利水平(有利润的营收)。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≤85%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑩"
},
level2Name
:
{
value
:
""
,
placeholder
:
" 项目平均毛利率 "
},
level2Content
:
{
value
:
""
,
placeholder
:
" 项目成本控制水平 "
},
level2Target
:
{
value
:
""
,
placeholder
:
"≥15%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(10)"
},
level1Name
:
{
value
:
""
,
placeholder
:
" 总部支出占营收比例 "
},
level1Content
:
{
value
:
""
,
placeholder
:
" 总部成本控制水平及业务发展规模。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≤10%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑪"
},
level2Name
:
{
value
:
""
,
placeholder
:
" 项目成本责任制覆盖率 "
},
level2Content
:
{
value
:
""
,
placeholder
:
" 集团标后预算等成本管理制度落实情况。"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"100%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(11)"
},
level1Name
:
{
value
:
""
,
placeholder
:
" 项目直接费占总成本比例 "
},
level1Content
:
{
value
:
""
,
placeholder
:
" 项目直接成本水平不增的前提下降低间接成本 "
,
},
level1Target
:
{
value
:
""
,
placeholder
:
" 近期升 5%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑫"
},
level2Name
:
{
value
:
""
,
placeholder
:
" 项目直接费占总成本比例 "
},
level2Content
:
{
value
:
""
,
placeholder
:
" 项目直接成本水平不增的前提下降低间接成本 "
,
},
level2Target
:
{
value
:
""
,
placeholder
:
" 近期升 5%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(12)"
},
level1Name
:
{
value
:
""
,
placeholder
:
" 营收收现比例 "
},
level1Content
:
{
value
:
""
,
placeholder
:
" 有现金流的利润。"
},
level1Target
:
{
value
:
""
,
placeholder
:
"≥能建考核指标 "
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑬"
},
level2Name
:
{
value
:
""
,
placeholder
:
" 专业分包比例 *"
},
level2Content
:
{
value
:
""
,
placeholder
:
" 自营施工水平。"
},
level2Target
:
{
value
:
""
,
placeholder
:
"≤50%"
},
},
],
},
{
serialNumber
:
{
value
:
""
,
placeholder
:
"5"
},
entityType
:
{
value
:
""
,
placeholder
:
" 路衍经济类 "
},
rows
:
[
{
level1Code
:
{
value
:
""
,
placeholder
:
"(13)"
},
level1Name
:
{
value
:
""
,
placeholder
:
" 成本营收比 "
},
level1Content
:
{
value
:
""
,
placeholder
:
" 公司成本管控水平和业务盈利水平(有利润的营收)。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≤85%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑭"
},
level2Name
:
{
value
:
""
,
placeholder
:
" 商业策划覆盖比例 "
},
level2Content
:
{
value
:
""
,
placeholder
:
" 考核商业分析、经营策划、成本核算等经营机制建立情况 "
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"100%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(14)"
},
level1Name
:
{
value
:
""
,
placeholder
:
" 总部支出占营收比例 "
},
level1Content
:
{
value
:
""
,
placeholder
:
" 总部成本控制水平及业务发展规模。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≤10%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑮"
},
level2Name
:
{
value
:
""
,
placeholder
:
" 各类业务净利率 "
},
level2Content
:
{
value
:
""
,
placeholder
:
" 考核业务筛选、成本优化、经营能力提升等情况 "
,
},
level2Target
:
{
value
:
""
,
placeholder
:
" 均≥0%"
},
},
],
},
{
serialNumber
:
{
value
:
""
,
placeholder
:
"6"
},
entityType
:
{
value
:
""
,
placeholder
:
" 功能性单位 "
},
rows
:
[
{
level1Code
:
{
value
:
""
,
placeholder
:
"(15)"
},
level1Name
:
{
value
:
""
,
placeholder
:
" 经费预算纳入考核比例 "
},
level1Content
:
{
value
:
""
,
placeholder
:
" 考核成本体系建立情况 "
},
level1Target
:
{
value
:
""
,
placeholder
:
"100%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"/"
},
level2Name
:
{
value
:
""
,
placeholder
:
""
},
level2Content
:
{
value
:
""
,
placeholder
:
""
},
level2Target
:
{
value
:
""
,
placeholder
:
""
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(16)"
},
level1Name
:
{
value
:
""
,
placeholder
:
" 委外成果与付费挂钩比例 "
},
level1Content
:
{
value
:
""
,
placeholder
:
" 考核引入中介和委外咨询服务效率评价体系建立情况 "
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"100%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"/"
},
level2Name
:
{
value
:
""
,
placeholder
:
""
},
level2Content
:
{
value
:
""
,
placeholder
:
""
},
level2Target
:
{
value
:
""
,
placeholder
:
""
},
},
],
},
];
}
});
</
script
>
<
style
scoped
lang=
"less"
>
@import "@/styles/verticalManages.less";
</
style
>
src/views/elseManage/costTab1.vue
0 → 100644
View file @
1e428006
<
template
>
<div
class=
"table-container"
>
<table
class=
"cost-table"
>
<thead>
<!-- 一级表头 -->
<tr
class=
"header-row level1"
>
<th
rowspan=
"2"
style=
"width: 70px"
>
序号
</th>
<th
rowspan=
"2"
style=
"width: 110px"
>
主体类型
</th>
<th
colspan=
"4"
>
1级指标
</th>
<th
colspan=
"4"
>
2级指标
</th>
</tr>
<!-- 二级表头 -->
<tr
class=
"header-row level2"
>
<th
style=
"width: 70px"
>
编号
</th>
<th>
指标名称
</th>
<th>
指标考核内容
</th>
<th>
建议值
</th>
<th
style=
"width: 70px"
>
编号
</th>
<th>
指标名称
</th>
<th>
指标考核内容
</th>
<th>
建议值
</th>
</tr>
</thead>
<tbody>
<!-- 按主体分组渲染 -->
<template
v-for=
"(group, groupIndex) in groupedTableData"
:key=
"groupIndex"
>
<tr
v-for=
"(item, rowIndex) in group.rows"
:key=
"`$
{groupIndex}-${rowIndex}`"
class="body-row"
>
<!-- 序号:仅每组第一行显示 -->
<td
v-if=
"rowIndex === 0"
:rowspan=
"group.rows.length"
class=
"cell-item"
>
{{
group
.
serialNumber
.
placeholder
}}
</td>
<!-- 主体类型:仅每组第一行显示 -->
<td
v-if=
"rowIndex === 0"
:rowspan=
"group.rows.length"
class=
"cell-item entity-cell"
>
{{
group
.
entityType
.
placeholder
}}
</td>
<!-- 1级指标编号 -->
<td
class=
"cell-item"
>
{{
item
.
level1Code
.
placeholder
}}
</td>
<!-- 1级指标名称 -->
<td
class=
"cell-item"
>
{{
item
.
level1Name
.
placeholder
}}
</td>
<!-- 1级指标考核内容(textarea) -->
<td
class=
"cell-item"
>
<el-input
type=
"textarea"
rows=
"3"
v-model=
"item.level1Content.value"
:placeholder=
"item.level1Content.placeholder"
size=
"small"
class=
"table-input textarea-input"
:disabled=
"isPreview"
/>
</td>
<!-- 1级指标建议值 -->
<td
class=
"cell-item"
>
<el-input
type=
"textarea"
rows=
"3"
v-model=
"item.level1Target.value"
:placeholder=
"item.level1Target.placeholder"
size=
"small"
class=
"table-input"
:disabled=
"isPreview"
/>
</td>
<!-- 2级指标编号 -->
<td
class=
"cell-item"
>
{{
item
.
level2Code
.
placeholder
}}
</td>
<!-- 2级指标名称 -->
<td
class=
"cell-item"
>
{{
item
.
level2Name
.
placeholder
}}
</td>
<!-- 2级指标考核内容(textarea) -->
<td
class=
"cell-item"
>
<el-input
type=
"textarea"
rows=
"3"
v-model=
"item.level2Content.value"
:placeholder=
"item.level2Content.placeholder"
size=
"small"
class=
"table-input textarea-input"
:disabled=
"isPreview"
/>
</td>
<!-- 2级指标建议值 -->
<td
class=
"cell-item"
>
<el-input
type=
"textarea"
rows=
"3"
v-model=
"item.level2Target.value"
:placeholder=
"item.level2Target.placeholder"
size=
"small"
class=
"table-input"
:disabled=
"isPreview"
/>
</td>
</tr>
</
template
>
</tbody>
</table>
</div>
</template>
<
script
setup
>
import
{
ref
,
watch
,
computed
,
defineProps
,
defineExpose
}
from
"vue"
;
import
{
ElInput
}
from
"element-plus"
;
// ===================== 1. 接收父组件传入的初始化数据 =====================
const
props
=
defineProps
({
// 新增:预览模式控制属性
isPreview
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
// 父组件传入的初始化表格数据,默认值为原有结构
initTableData
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[
{
serialNumber
:
{
value
:
""
,
placeholder
:
"1"
},
entityType
:
{
value
:
""
,
placeholder
:
"交投本部"
},
rows
:
[
{
level1Code
:
{
value
:
""
,
placeholder
:
" (1) "
},
level1Name
:
{
value
:
""
,
placeholder
:
"ROA,辅以总资产周转率"
},
level1Content
:
{
value
:
""
,
placeholder
:
"ROA=净利率×总资产周转率;重资产企业要不断提高净利水平,要不加快资产变现。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"中期ROA提升2%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"①"
},
level2Name
:
{
value
:
""
,
placeholder
:
"3类费用压降比例"
},
level2Content
:
{
value
:
""
,
placeholder
:
"考核非生产性支出控制成果"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"近期降1%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(2)"
},
level1Name
:
{
value
:
""
,
placeholder
:
"成本营收比"
},
level1Content
:
{
value
:
""
,
placeholder
:
"衡量企业成本控制效率和资产盈利能力(重资产企业)。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≥能建考核值的90%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"②"
},
level2Name
:
{
value
:
""
,
placeholder
:
"每亿元“投资+营收”管理费"
},
level2Content
:
{
value
:
""
,
placeholder
:
"成本费用支出效率"
},
level2Target
:
{
value
:
""
,
placeholder
:
"近期升5%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(3)"
},
level1Name
:
{
value
:
""
,
placeholder
:
"投资控制偏差率*"
},
level1Content
:
{
value
:
""
,
placeholder
:
"考核建设项目投资控制能力。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≤0"
},
level2Code
:
{
value
:
""
,
placeholder
:
"③"
},
level2Name
:
{
value
:
""
,
placeholder
:
"概预差*"
},
level2Content
:
{
value
:
""
,
placeholder
:
"设计阶段限额设计和设计优化的工作成果"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"≥3%"
},
},
],
},
{
serialNumber
:
{
value
:
""
,
placeholder
:
"2"
},
entityType
:
{
value
:
""
,
placeholder
:
"建设项目"
},
rows
:
[
{
level1Code
:
{
value
:
""
,
placeholder
:
"(4)"
},
level1Name
:
{
value
:
""
,
placeholder
:
"投资价差收取达成率"
},
level1Content
:
{
value
:
""
,
placeholder
:
"考核投资项目设计优化、合同策划、承包穿透管理和投资价差催收4方面综合能力。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≥100%"
},
level2Code
:
{
value
:
""
,
placeholder
:
"④"
},
level2Name
:
{
value
:
""
,
placeholder
:
"施工合同降造比例*"
},
level2Content
:
{
value
:
""
,
placeholder
:
"协同施工单位承包合同价(单价水平)较批复预算(除激励基金和风险费外)下降比例。"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"能建要求≥15%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"(5)"
},
level1Name
:
{
value
:
""
,
placeholder
:
"综合融资成本率**"
},
level1Content
:
{
value
:
""
,
placeholder
:
"建设期融资成本控制水平。"
,
},
level1Target
:
{
value
:
""
,
placeholder
:
"≤银行业自律利率"
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑤"
},
level2Name
:
{
value
:
""
,
placeholder
:
"投资价差同步收取比例"
},
level2Content
:
{
value
:
""
,
placeholder
:
"投资价差收取同步性情况。"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"≥100%"
},
},
{
level1Code
:
{
value
:
""
,
placeholder
:
"/"
},
level1Name
:
{
value
:
""
,
placeholder
:
""
},
level1Content
:
{
value
:
""
,
placeholder
:
""
},
level1Target
:
{
value
:
""
,
placeholder
:
""
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑥"
},
level2Name
:
{
value
:
""
,
placeholder
:
"变更索赔费用占预备费比例"
},
level2Content
:
{
value
:
""
,
placeholder
:
"衡量履约阶段投资控制质量。"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"<70%"
},
},
],
},
{
serialNumber
:
{
value
:
""
,
placeholder
:
"3"
},
entityType
:
{
value
:
""
,
placeholder
:
"运营项目"
},
rows
:
[
{
level1Code
:
{
value
:
""
,
placeholder
:
"(6)"
},
level1Name
:
{
value
:
""
,
placeholder
:
"百元营收“管理费+运行费”"
},
level1Content
:
{
value
:
""
,
placeholder
:
"管理、运行成本控制水平"
},
level1Target
:
{
value
:
""
,
placeholder
:
"同行业均值之下"
},
level2Code
:
{
value
:
""
,
placeholder
:
"⑦"
},
level2Name
:
{
value
:
""
,
placeholder
:
"集约化、智能化措施完成率"
},
level2Content
:
{
value
:
""
,
placeholder
:
"考核公司“三定方案”等降本措施落实情况"
,
},
level2Target
:
{
value
:
""
,
placeholder
:
"100%"
},
},
],
},
],
},
});
// ===================== 2. 初始化可编辑数据(深拷贝,避免修改父组件传入的原始数据) =====================
const
rawTableData
=
ref
(
JSON
.
parse
(
JSON
.
stringify
(
props
.
initTableData
)));
// 计算属性:保持分组渲染逻辑
const
groupedTableData
=
computed
(()
=>
rawTableData
.
value
);
// ===================== 3. 暴露方法给父组件:获取编辑后的数据 =====================
const
getEditedTableData
=
()
=>
{
console
.
log
(
rawTableData
.
value
,
"rawTableData.value"
);
return
rawTableData
.
value
;
};
// 暴露方法给父组件调用
defineExpose
({
getEditedTableData
,
// 可选:暴露原始编辑数据(含placeholder),按需使用
getRawTableData
:
()
=>
JSON
.
parse
(
JSON
.
stringify
(
rawTableData
.
value
)),
});
</
script
>
<
style
scoped
>
/* 容器样式 */
.table-container
{
width
:
98%
;
margin
:
20px
auto
;
padding
:
10px
;
font-family
:
"Microsoft YaHei"
,
Arial
,
sans-serif
;
overflow-x
:
auto
;
/* 小屏幕横向滚动 */
}
/* 表格核心样式 */
.cost-table
{
width
:
100%
;
border-collapse
:
collapse
;
border
:
1px
solid
#ddd
;
font-size
:
14px
;
color
:
#333
;
min-width
:
1200px
;
}
/* 一级表头 */
.header-row.level1
th
{
background-color
:
#2c5aa0
;
color
:
#fff
;
padding
:
12px
8px
;
border
:
1px
solid
#2c5aa0
;
text-align
:
center
;
vertical-align
:
middle
;
font-weight
:
600
;
font-size
:
15px
;
}
/* 二级表头 */
.header-row.level2
th
{
background-color
:
#4073b8
;
color
:
#fff
;
padding
:
10px
8px
;
border
:
1px
solid
#2c5aa0
;
text-align
:
center
;
vertical-align
:
middle
;
font-weight
:
500
;
font-size
:
14px
;
}
/* 表体单元格 */
.cell-item
{
padding
:
8px
4px
;
border
:
1px
solid
#ddd
;
text-align
:
center
;
vertical-align
:
middle
;
height
:
80px
;
/* 适配textarea高度 */
}
/* 主体类型单元格特殊样式 */
.entity-cell
{
font-weight
:
500
;
}
/* el-input通用样式 */
.table-input
{
width
:
95%
;
--el-input-placeholder-color
:
#666
;
/* 占位符文字颜色 */
}
/* 多行文本域适配 */
.textarea-input
{
resize
:
none
;
height
:
70px
!important
;
line-height
:
1.5
;
}
/* 新增:优化禁用状态的输入框样式 */
:deep
(
.el-input.is-disabled
)
{
--el-input-text-color
:
#333
;
/* 禁用时文字仍显示黑色 */
--el-input-bg-color
:
#f8f9fa
;
/* 禁用背景色更柔和 */
cursor
:
default
;
}
:deep
(
.el-input__inner
:disabled
)
{
background-color
:
#f8f9fa
;
color
:
#333
;
border-color
:
#e9ecef
;
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment