This commit is contained in:
zlwu committed 2025-08-04 16:51:07 +08:00
commit 93e1fd0338
73 files changed
+19101 -1608

No files matched your search

+92 -8
View File
@@ -82,20 +82,68 @@ export const getApprovalRecord = (id: string | number): AxiosPromise<CwApprovalR
// 审批申报(单个)
export const approveApplication = (data: CwApprovalForm) => {
// 🔧 修复:使用部门审批接口,将JSON数据转换为URL参数
const params = new URLSearchParams();
params.append('applicationId', String(data.applicationId || ''));
// 🔧 修复:正确处理审批结果映射
let approveAction = 'return'; // 默认为退回
if (data.approvalResult === 'approve') {
approveAction = 'approve';
} else if (data.approvalResult === 'return') {
approveAction = 'return';
}
params.append('approveAction', approveAction);
params.append('approveOpinion', data.approvalComment || '');
return request({
url: '/cw/approval/process',
url: '/cw/approval/dept', // 🔧 修复:使用部门审批接口
method: 'post',
data: data
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: params.toString()
});
};
// 批量审批申报
export const batchApproveApplication = (data: CwApprovalForm) => {
return request({
url: '/cw/approval/batchApprove',
method: 'post',
data: data
});
export const batchApproveApplication = async (data: CwApprovalForm) => {
// 🔧 修复:后端没有批量接口,改为逐个调用
if (!data.applicationIds || data.applicationIds.length === 0) {
throw new Error('未选择要审批的申报项目');
}
console.log('🔧 批量审批开始,项目数量:', data.applicationIds.length);
const results = [];
for (const applicationId of data.applicationIds) {
try {
const singleData = {
applicationId: applicationId,
approvalResult: data.approvalResult,
approvalComment: data.approvalComment,
nextApprover: data.nextApprover
};
console.log('🔧 处理项目ID:', applicationId);
const result = await approveApplication(singleData);
results.push({ applicationId, success: true, result });
} catch (error) {
console.error('🔧 项目审批失败,ID:', applicationId, 'Error:', error);
results.push({ applicationId, success: false, error });
}
}
// 检查是否有失败的项目
const failures = results.filter(r => !r.success);
if (failures.length > 0) {
console.error('🔧 批量审批部分失败:', failures);
throw new Error(`批量审批完成,${failures.length}个项目审批失败`);
}
console.log('🔧 批量审批全部成功');
return { code: 200, msg: '批量审批成功', data: results };
};
// 获取审批历史记录
@@ -133,6 +181,42 @@ export const getDepartmentApprovals = (query: CwApprovalRecordQuery): AxiosPromi
});
};
// 获取财务审批待审批列表
export const getFinancePendingApprovals = (query: CwApprovalRecordQuery): AxiosPromise<CwApprovalRecordVO[]> => {
return request({
url: '/cw/approval/finance/pending',
method: 'get',
params: query
});
};
// 获取财务审批已审批列表
export const getFinanceApprovedRecords = (query: CwApprovalRecordQuery): AxiosPromise<CwApprovalRecordVO[]> => {
return request({
url: '/cw/approval/finance/approved',
method: 'get',
params: query
});
};
// 获取学院领导审批待审批列表
export const getLeaderPendingApprovals = (query: CwApprovalRecordQuery): AxiosPromise<CwApprovalRecordVO[]> => {
return request({
url: '/cw/approval/leader/pending',
method: 'get',
params: query
});
};
// 获取学院领导已审批列表
export const getLeaderApprovedApprovals = (query: CwApprovalRecordQuery): AxiosPromise<CwApprovalRecordVO[]> => {
return request({
url: '/cw/approval/leader/approved',
method: 'get',
params: query
});
};
// 获取可选审批人列表
export const getAvailableApprovers = (applicationId: string | number): AxiosPromise<any[]> => {
return request({
+46
View File
@@ -0,0 +1,46 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
export interface FinanceApprovalForm {
applicationId: number;
approvalResult: string; // 'approve' | 'reject'
approvalComment: string;
selectedApproverId?: number; // 选择的院领导ID(审批通过时必填)
}
// 财务审批操作
export const financeApproveApplication = (data: FinanceApprovalForm): AxiosPromise<any> => {
const params = new URLSearchParams({
applicationId: data.applicationId.toString(),
approveAction: data.approvalResult,
approveOpinion: data.approvalComment
});
// 如果选择了院领导,添加参数
if (data.selectedApproverId) {
params.append('selectedApproverId', data.selectedApproverId.toString());
}
return request({
url: '/cw/approval/finance',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: params
});
};
// 财务批量审批操作
export const batchFinanceApproveApplication = (dataList: FinanceApprovalForm[]): Promise<any> => {
// 前端模拟批量操作,逐个调用单个审批接口
const promises = dataList.map(item => financeApproveApplication(item));
return Promise.all(promises).then(results => {
return {
data: results,
code: 200,
message: '批量操作成功'
};
});
};
+38
View File
@@ -0,0 +1,38 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
export interface LeaderApprovalForm {
applicationId: number;
approvalResult: string; // 'approve' | 'reject'
approvalComment: string;
}
// 分管领导审批操作
export const leaderApproveApplication = (data: LeaderApprovalForm): AxiosPromise<any> => {
return request({
url: '/cw/approval/leader',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: new URLSearchParams({
applicationId: data.applicationId.toString(),
approveAction: data.approvalResult,
approveOpinion: data.approvalComment
})
});
};
// 分管领导批量审批操作
export const batchLeaderApproveApplication = (dataList: LeaderApprovalForm[]): AxiosPromise<any> => {
// 前端模拟批量操作,逐个调用单个审批接口
const promises = dataList.map(item => leaderApproveApplication(item));
return Promise.all(promises).then(results => {
return {
data: results,
code: 200,
message: '批量操作成功'
};
});
};
+41
View File
@@ -0,0 +1,41 @@
import request from '@/utils/request'
import { AxiosPromise } from 'axios'
export interface StatisticsQuery {
budgetYear?: string
}
// 获取项目清单(用于统计页面)
export function getStatisticsList(query: StatisticsQuery): AxiosPromise<any> {
return request({
url: '/cw/application/statistics/list',
method: 'get',
params: query
})
}
// 获取状态统计
export function getStatusStatistics(budgetYear?: string): AxiosPromise<any> {
return request({
url: '/cw/application/statistics/status',
method: 'get',
params: { budgetYear }
})
}
// 获取部门预算汇总统计
export function getDepartmentStatistics(budgetYear?: string): AxiosPromise<any> {
return request({
url: '/cw/application/statistics/department',
method: 'get',
params: { budgetYear }
})
}
// 获取年度汇总统计
export function getYearlyStatistics(): AxiosPromise<any> {
return request({
url: '/cw/application/statistics/yearly',
method: 'get'
})
}
+5 -2
View File
@@ -1,6 +1,6 @@
/**
* 财务项目预算审批 - TypeScript类型定义
*
*
* @author ruoyi
* @date 2025-01-26
*/
@@ -39,6 +39,7 @@ export interface CwProjectApplicationVO {
totalMunicipalFiscal?: number;
totalSelfFunding?: number;
totalOtherFunding?: number;
// 分年度预算明细
currentYearTotal?: number;
currentYearMunicipal?: number;
currentYearSelf?: number;
@@ -64,6 +65,7 @@ export interface CwProjectApplicationVO {
updateBy?: number;
updateTime?: string;
remark?: string;
performanceIndicators?: any[]; // 绩效指标数据
}
// 项目预算申报表单对象
@@ -86,6 +88,7 @@ export interface CwProjectApplicationForm {
totalMunicipalFiscal?: number;
totalSelfFunding?: number;
totalOtherFunding?: number;
// 分年度预算明细
currentYearTotal?: number;
currentYearMunicipal?: number;
currentYearSelf?: number;
@@ -199,4 +202,4 @@ export interface StatusDistribution {
statusName: string;
count: number;
percentage: number;
}
}
+416 -70
View File
@@ -315,54 +315,136 @@
<el-col :span="6">
<el-form-item label="年度执行合计" prop="yearlyTotal">
<el-input-number
v-model="form.yearlyTotal"
:model-value="yearlyTotals.total"
:precision="2"
:min="0"
placeholder="万元"
size="large"
style="width: 100%"
:disabled="true"
readonly
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="年度财政资金" prop="yearlyMunicipalFiscal">
<el-input-number
v-model="form.yearlyMunicipalFiscal"
:model-value="yearlyTotals.municipal"
:precision="2"
:min="0"
placeholder="万元"
size="large"
style="width: 100%"
:disabled="true"
readonly
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="年度自筹资金" prop="yearlySelfFunding">
<el-input-number
v-model="form.yearlySelfFunding"
:model-value="yearlyTotals.self"
:precision="2"
:min="0"
placeholder="万元"
size="large"
style="width: 100%"
:disabled="true"
readonly
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="年度其他资金" prop="yearlyOtherFunding">
<el-input-number
v-model="form.yearlyOtherFunding"
:model-value="yearlyTotals.other"
:precision="2"
:min="0"
placeholder="万元"
size="large"
style="width: 100%"
:disabled="true"
readonly
/>
</el-form-item>
</el-col>
</el-row>
</div>
<!-- 分年度预算明细 -->
<div class="budget-breakdown-section">
<h4 class="section-title">
<el-icon><Calendar /></el-icon>
分年度预算明细
</h4>
<el-table :data="budgetBreakdownData" border style="width: 100%">
<el-table-column prop="yearLabel" label="年度" width="120" align="center"/>
<el-table-column label="总投资(万元)" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.total"
:precision="2"
:min="0"
placeholder="0.00"
size="small"
style="width: 100%"
@change="updateBudgetBreakdown(scope.$index)"
/>
</template>
</el-table-column>
<el-table-column label="市财政资金(万元)" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.municipal"
:precision="2"
:min="0"
placeholder="0.00"
size="small"
style="width: 100%"
@change="updateBudgetBreakdown(scope.$index)"
/>
</template>
</el-table-column>
<el-table-column label="单位自筹(万元)" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.self"
:precision="2"
:min="0"
placeholder="0.00"
size="small"
style="width: 100%"
@change="updateBudgetBreakdown(scope.$index)"
/>
</template>
</el-table-column>
<el-table-column label="其他资金(万元)" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.other"
:precision="2"
:min="0"
placeholder="0.00"
size="small"
style="width: 100%"
@change="updateBudgetBreakdown(scope.$index)"
/>
</template>
</el-table-column>
</el-table>
<div class="budget-breakdown-note">
<el-alert
title="说明:请根据项目实际情况填写各年度的预算分配,系统会自动汇总计算总投资。"
type="info"
:closable="false"
show-icon
/>
</div>
</div>
<!-- 资金分配图表 -->
<div class="budget-chart" v-if="hasBudgetData">
<h4 class="section-title">资金分配可视化</h4>
@@ -516,17 +598,17 @@ const form = reactive({
totalMunicipalFiscal: 0,
totalSelfFunding: 0,
totalOtherFunding: 0,
// 年度预计执行数
// 分年度预算明细
currentYearTotal: 0,
currentYearMunicipal: 0,
currentYearSelf: 0,
currentYearOther: 0,
// 下年度申请资金
// 下年度预计执行数
nextYearTotal: 0,
nextYearMunicipal: 0,
nextYearSelf: 0,
nextYearOther: 0,
// 以后年度安排
// 后续年度预计执行数
futureYearTotal: 0,
futureYearMunicipal: 0,
futureYearSelf: 0,
@@ -536,32 +618,7 @@ const form = reactive({
approvalStatus: 'draft'
})
// 监听父组件传递的数据变化
watch(() => props.formData, (newData) => {
if (newData && typeof newData === 'object' && Object.keys(newData).length > 0) {
console.log('接收到父组件传递的数据:', newData)
// 安全地更新form数据
Object.keys(form).forEach(key => {
if (newData.hasOwnProperty(key) && newData[key] !== null && newData[key] !== undefined) {
// 特殊处理不同类型的字段
if (key === 'year' && newData[key]) {
form[key] = newData[key].toString()
} else if (key === 'startDate' || key === 'endDate') {
form[key] = newData[key] ? formatDateString(newData[key]) : ''
} else if (key.includes('Investment') || key.includes('Fiscal') || key.includes('Funding') ||
key.includes('Total') || key.includes('Municipal') || key.includes('Self') || key.includes('Other')) {
// 金额字段
form[key] = Number(newData[key]) || 0
} else {
form[key] = newData[key]
}
}
})
console.log('form数据更新完成:', form)
}
}, { deep: true, immediate: true })
// 日期格式化函数
// 工具函数定义 - 提前定义避免提升问题
const formatDateString = (dateValue: any): string => {
if (!dateValue) return ''
let date: Date
@@ -581,6 +638,61 @@ const formatDateString = (dateValue: any): string => {
return `${year}-${month}-${day}`
}
const updateFormWithData = (newData: any) => {
if (!newData || typeof newData !== 'object' || Object.keys(newData).length === 0) {
return
}
try {
// 安全地更新form数据
Object.keys(form).forEach(key => {
if (newData.hasOwnProperty(key) && newData[key] !== null && newData[key] !== undefined) {
// 特殊处理不同类型的字段
if (key === 'year' && newData[key]) {
form[key] = newData[key].toString()
} else if (key === 'startDate' || key === 'endDate') {
form[key] = newData[key] ? formatDateString(newData[key]) : ''
} else if (key.includes('Investment') || key.includes('Fiscal') || key.includes('Funding') ||
key.includes('Total') || key.includes('Municipal') || key.includes('Self') || key.includes('Other') ||
key.includes('yearly') || key.includes('Year')) {
// 金额字段 - 扩展匹配模式
form[key] = Number(newData[key]) || 0
} else if (key === 'performanceIndicators') {
// 绩效指标数据特殊处理 - 需要转换数字字段
if (Array.isArray(newData[key])) {
form[key] = newData[key].map(indicator => {
if (indicator && typeof indicator === 'object') {
return {
...indicator,
weightPercentage: indicator.weightPercentage ? Number(indicator.weightPercentage) : 0,
sortOrder: indicator.sortOrder ? Number(indicator.sortOrder) : 0
}
}
return indicator
})
} else {
form[key] = []
}
} else {
form[key] = newData[key]
}
}
})
// 数据加载完成后,重新计算年度总数
// calculateYearlyTotals() // 移除此函数调用
} catch (error) {
console.error('更新form数据时出错:', error)
// 即使出错也要确保基本数据结构
if (newData.id) form.id = newData.id
if (newData.projectName) form.projectName = newData.projectName
if (newData.principal) form.principal = newData.principal
}
}
// 监听父组件传递的数据变化
watch(() => props.formData, updateFormWithData, { deep: true, immediate: true })
// 表单验证规则
const rules = {
projectName: [
@@ -605,7 +717,7 @@ const rules = {
],
totalInvestment: [
{ required: true, message: '请填写项目总投资', trigger: 'blur' },
{ type: 'number', min: 0.01, message: '投资金额必须大于0', trigger: 'blur' }
{ type: 'number' as const, min: 0.01, message: '投资金额必须大于0', trigger: 'blur' }
]
}
@@ -618,6 +730,103 @@ const hasBudgetData = computed(() => {
return form.totalMunicipalFiscal > 0 || form.totalSelfFunding > 0 || form.totalOtherFunding > 0
})
// 年度执行合计(计算属性)
const yearlyTotals = computed(() => {
return {
total: (form.currentYearTotal || 0) + (form.nextYearTotal || 0) + (form.futureYearTotal || 0),
municipal: (form.currentYearMunicipal || 0) + (form.nextYearMunicipal || 0) + (form.futureYearMunicipal || 0),
self: (form.currentYearSelf || 0) + (form.nextYearSelf || 0) + (form.futureYearSelf || 0),
other: (form.currentYearOther || 0) + (form.nextYearOther || 0) + (form.futureYearOther || 0)
}
})
// 分年度预算明细数据
const budgetBreakdownData = computed(() => {
const currentYear = parseInt(form.year) || new Date().getFullYear()
return [
{
yearLabel: `${currentYear}年`,
total: form.currentYearTotal || 0,
municipal: form.currentYearMunicipal || 0,
self: form.currentYearSelf || 0,
other: form.currentYearOther || 0,
field: 'currentYear'
},
{
yearLabel: `${currentYear + 1}年`,
total: form.nextYearTotal || 0,
municipal: form.nextYearMunicipal || 0,
self: form.nextYearSelf || 0,
other: form.nextYearOther || 0,
field: 'nextYear'
},
{
yearLabel: '以后年度',
total: form.futureYearTotal || 0,
municipal: form.futureYearMunicipal || 0,
self: form.futureYearSelf || 0,
other: form.futureYearOther || 0,
field: 'futureYear'
}
]
})
// 更新分年度预算明细
const updateBudgetBreakdown = (index: number) => {
const breakdown = budgetBreakdownData.value[index]
const fieldPrefix = breakdown.field
// 自动计算该年度的总投资(市财政资金 + 单位自筹 + 其他资金)
const calculatedTotal = (breakdown.municipal || 0) + (breakdown.self || 0) + (breakdown.other || 0)
breakdown.total = calculatedTotal
// 更新form中对应的字段
form[`${fieldPrefix}Total`] = breakdown.total
form[`${fieldPrefix}Municipal`] = breakdown.municipal || 0
form[`${fieldPrefix}Self`] = breakdown.self || 0
form[`${fieldPrefix}Other`] = breakdown.other || 0
// 重新计算总投资
recalculateTotalFromBreakdown()
}
// 从分年度明细重新计算总投资
const recalculateTotalFromBreakdown = () => {
const totalMunicipal = (form.currentYearMunicipal || 0) + (form.nextYearMunicipal || 0) + (form.futureYearMunicipal || 0)
const totalSelf = (form.currentYearSelf || 0) + (form.nextYearSelf || 0) + (form.futureYearSelf || 0)
const totalOther = (form.currentYearOther || 0) + (form.nextYearOther || 0) + (form.futureYearOther || 0)
form.totalMunicipalFiscal = totalMunicipal
form.totalSelfFunding = totalSelf
form.totalOtherFunding = totalOther
form.totalInvestment = totalMunicipal + totalSelf + totalOther
}
// 计算预算汇总
const calculateBudgetSummary = () => {
// 计算各类资金的总和
const totalMunicipal = (form.currentYearMunicipal || 0) +
(form.nextYearMunicipal || 0) +
(form.futureYearMunicipal || 0)
const totalSelf = (form.currentYearSelf || 0) +
(form.nextYearSelf || 0) +
(form.futureYearSelf || 0)
const totalOther = (form.currentYearOther || 0) +
(form.nextYearOther || 0) +
(form.futureYearOther || 0)
// 更新总投资字段
form.totalMunicipalFiscal = totalMunicipal
form.totalSelfFunding = totalSelf
form.totalOtherFunding = totalOther
form.totalInvestment = totalMunicipal + totalSelf + totalOther
// 更新图表
if (budgetChartInstance && hasBudgetData.value) {
renderBudgetChart()
}
}
// 格式化金额
const formatAmount = (amount: number): string => {
return amount ? amount.toFixed(2) : '0.00'
@@ -697,12 +906,9 @@ const initBudgetChart = async () => {
// 验证绩效指标分值占比总和
const validatePerformanceIndicators = () => {
console.log('=== 校验绩效指标 ===')
// 直接使用绩效指标组件的校验方法
if (performanceIndicatorsRef?.value && typeof performanceIndicatorsRef.value.validate === 'function') {
const validationResult = performanceIndicatorsRef.value.validate()
console.log('绩效指标校验结果:', validationResult)
return validationResult
}
@@ -777,9 +983,6 @@ const handlePrevStep = () => {
// 处理绩效指标变化
const handlePerformanceIndicatorsChange = (indicators) => {
console.log('=== 绩效指标数据变化 ===')
console.log('接收到的indicators:', indicators)
// 为每个指标添加必要的字段
const processedIndicators = indicators.map((item, index) => ({
...item,
@@ -803,18 +1006,6 @@ const handlePerformanceIndicatorsChange = (indicators) => {
}))
form.performanceIndicators = processedIndicators
console.log('处理后的绩效指标数据:', form.performanceIndicators)
// 自动生成总绩效目标和当年目标(如果有目标值)
const totalGoals = processedIndicators
.filter(item => item.targetValue && !isNaN(Number(item.targetValue)))
.reduce((sum, item) => sum + Number(item.targetValue), 0)
if (totalGoals > 0) {
form.totalPerformanceGoal = totalGoals
form.currentYearGoal = totalGoals
console.log('自动生成总绩效目标:', form.totalPerformanceGoal)
}
}
// 保存草稿
@@ -831,13 +1022,6 @@ const handleSaveDraft = async () => {
}
}
// 详细调试信息
console.log('=== 前端保存草稿调试信息 ===')
console.log('form.performanceIndicators 是否存在:', !!form.performanceIndicators)
console.log('form.performanceIndicators 长度:', form.performanceIndicators ? form.performanceIndicators.length : 'undefined')
console.log('form.performanceIndicators 数据:', form.performanceIndicators)
console.log('完整的form数据:', form)
const result = form.id
? await updateProjectApplication(form as any)
: await saveDraft(form as any)
@@ -847,12 +1031,8 @@ const handleSaveDraft = async () => {
if (!form.id && result.data?.id) {
form.id = result.data.id
form.applicationNo = result.data.applicationNo
console.log('草稿保存成功,获得ID:', form.id, '申报编号:', form.applicationNo)
}
// 绩效指标数据已经作为form的一部分传递给后端,无需单独处理
console.log('绩效指标数据已随草稿数据一起保存,数量:', form.performanceIndicators ? form.performanceIndicators.length : 0)
ElMessage.success('草稿保存成功')
}
} catch (error) {
@@ -927,10 +1107,16 @@ const handleSubmit = async () => {
// 解析错误信息
let errorMessage = '提交申报失败'
if (error?.response?.data?.msg) {
errorMessage += ': ' + error.response.data.msg
} else if (error?.message) {
errorMessage += ': ' + error.message
if (error && typeof error === 'object' && 'response' in error) {
const axiosError = error as any
if (axiosError.response?.data?.msg) {
errorMessage += ': ' + axiosError.response.data.msg
}
} else if (error && typeof error === 'object' && 'message' in error) {
const generalError = error as any
if (generalError.message) {
errorMessage += ': ' + generalError.message
}
} else if (typeof error === 'string') {
errorMessage += ': ' + error
}
@@ -955,15 +1141,64 @@ const handleCancel = () => {
// 加载数据
const loadData = async () => {
console.log('=== loadData 函数被调用 ===')
const id = route.params.id as string
console.log('解析的ID:', id, '类型:', typeof id)
if (id && id !== 'add') {
console.log('符合加载条件,开始请求数据...')
try {
console.log('开始加载申报数据,ID:', id)
const response = await getProjectApplication(id)
console.log('API请求完成,响应:', response)
if (response.code === 200) {
console.log('申报基础数据加载成功:', response.data)
// 显示原始后端数据的调试信息
console.log('=== 后端返回的原始数据 ===')
console.log('完整数据:', JSON.stringify(response.data, null, 2))
console.log('分年度预算字段值:')
const budgetFields = [
'currentYearTotal', 'currentYearMunicipal', 'currentYearSelf', 'currentYearOther',
'nextYearTotal', 'nextYearMunicipal', 'nextYearSelf', 'nextYearOther',
'futureYearTotal', 'futureYearMunicipal', 'futureYearSelf', 'futureYearOther'
]
budgetFields.forEach(field => {
console.log(`${field}: ${response.data[field]} (类型: ${typeof response.data[field]})`)
})
// 更新基础数据
Object.assign(form, response.data)
// 特殊处理分年度预算数据
if (response.data) {
// 确保年度预算字段存在
const fields = [
'currentYearTotal', 'currentYearMunicipal', 'currentYearSelf', 'currentYearOther',
'nextYearTotal', 'nextYearMunicipal', 'nextYearSelf', 'nextYearOther',
'futureYearTotal', 'futureYearMunicipal', 'futureYearSelf', 'futureYearOther'
]
console.log('=== 开始处理分年度预算字段 ===')
fields.forEach(field => {
const originalValue = response.data[field]
const processedValue = Number(originalValue) || 0
console.log(`${field}: ${originalValue} -> ${processedValue}`)
if (response.data[field] !== undefined && response.data[field] !== null) {
form[field] = processedValue
}
})
// calculateYearlyTotals() // 移除此函数调用
console.log('=== 处理完成后的form数据 ===')
budgetFields.forEach(field => {
console.log(`form.${field}: ${form[field]}`)
})
}
// 检查是否有绩效指标数据
if (response.data.performanceIndicators && response.data.performanceIndicators.length > 0) {
console.log('发现绩效指标数据,数量:', response.data.performanceIndicators.length)
@@ -973,19 +1208,61 @@ const loadData = async () => {
console.log('没有绩效指标数据')
form.performanceIndicators = []
}
console.log('分年度预算数据加载完成:', {
currentYear: {
total: form.currentYearTotal,
municipal: form.currentYearMunicipal,
self: form.currentYearSelf,
other: form.currentYearOther
},
nextYear: {
total: form.nextYearTotal,
municipal: form.nextYearMunicipal,
self: form.nextYearSelf,
other: form.nextYearOther
},
futureYear: {
total: form.futureYearTotal,
municipal: form.futureYearMunicipal,
self: form.futureYearSelf,
other: form.futureYearOther
}
})
// 检查计算属性的值
console.log('=== budgetBreakdownData 计算属性结果 ===')
console.log('budgetBreakdownData:', budgetBreakdownData.value)
} else {
console.log('API响应错误:', response)
}
} catch (error) {
console.error('加载数据失败:', error)
ElMessage.error('加载数据失败')
}
} else {
console.log('不符合加载条件,ID:', id)
}
}
// 暴露给父组件的方法
const validate = (callback: (valid: boolean) => void) => {
// 简单的验证逻辑,检查必填字段
const validate = (callback: (valid: boolean, formData?: any) => void) => {
// 验证逻辑 + 数据收集
const isValid = !!(form.projectName && form.principal && form.totalInvestment > 0)
callback(isValid)
if (isValid) {
// 收集绩效指标数据
if (performanceIndicatorsRef.value && typeof performanceIndicatorsRef.value.getAllIndicators === 'function') {
const indicators = performanceIndicatorsRef.value.getAllIndicators()
form.performanceIndicators = indicators
console.log('收集到绩效指标数据:', indicators)
}
// 返回验证结果和完整的表单数据
callback(true, form)
} else {
callback(false)
}
}
// 暴露方法给父组件
@@ -995,7 +1272,15 @@ defineExpose({
// 组件挂载
onMounted(() => {
console.log('=== CW Application Form 组件开始挂载 ===')
console.log('当前路由参数:', route.params)
console.log('route.params.id:', route.params.id)
console.log('是否为详情页面:', route.params.id && route.params.id !== 'add')
loadData()
console.log('=== CW Application Form 组件挂载完成 ===')
console.log('当前form初始状态:', JSON.stringify(form, null, 2))
})
</script>
@@ -1124,6 +1409,67 @@ onMounted(() => {
}
}
// 分年度预算明细
.budget-breakdown-section {
margin-bottom: 32px;
padding: 20px;
background: #f8f9fa;
border-radius: 6px;
border: 1px solid #e9ecef;
.section-title {
margin: 0 0 20px 0;
font-size: 16px;
font-weight: 600;
color: #2c3e50;
display: flex;
align-items: center;
gap: 8px;
}
:deep(.el-table) {
.el-table__header {
background-color: #f5f7fa;
.el-table__cell {
background-color: #f5f7fa;
color: #606266;
font-weight: 600;
}
}
.el-table__body {
.el-table__row {
&:hover {
background-color: #f5f7fa;
}
}
}
.el-input-number {
width: 100%;
:deep(.el-input__inner) {
text-align: right;
}
}
}
.budget-breakdown-note {
margin-top: 16px;
:deep(.el-alert) {
border-radius: 4px;
.el-alert__title {
font-size: 13px;
line-height: 1.6;
}
}
}
}
// 资金分配图表
.budget-chart {
margin-top: 24px;
padding: 20px;
+119 -42
View File
@@ -170,7 +170,7 @@
@click="handleExport"
v-hasPermi="['cw:application:export']"
>导出</el-button>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar :showSearch="showSearch" @update:showSearch="showSearch = $event" @queryTable="getList"></right-toolbar>
</div>
</div>
</template>
@@ -224,7 +224,7 @@
v-hasPermi="['cw:application:query']"
/>
</el-tooltip>
<el-tooltip content="编辑" placement="top" v-if="scope.row.approvalStatus === 'draft'">
<el-tooltip content="编辑" placement="top" v-if="['draft', 'returned'].includes(scope.row.approvalStatus)">
<el-button
type="success"
icon="Edit"
@@ -234,7 +234,7 @@
v-hasPermi="['cw:application:edit']"
/>
</el-tooltip>
<el-tooltip content="提交审批" placement="top" v-if="scope.row.approvalStatus === 'draft'">
<el-tooltip content="提交审批" placement="top" v-if="['draft', 'returned'].includes(scope.row.approvalStatus)">
<el-button
type="warning"
icon="Upload"
@@ -254,7 +254,7 @@
v-hasPermi="['cw:application:withdraw']"
/>
</el-tooltip>
<el-tooltip content="删除" placement="top" v-if="['draft', 'cancelled'].includes(scope.row.approvalStatus)">
<el-tooltip content="删除" placement="top" v-if="['draft', 'cancelled', 'returned'].includes(scope.row.approvalStatus)">
<el-button
type="danger"
icon="Delete"
@@ -272,13 +272,15 @@
<!-- 分页 -->
<div class="pagination-container">
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<pagination
v-show="total>0"
:total="total"
:page="queryParams.pageNum"
:limit="queryParams.pageSize"
@update:page="queryParams.pageNum = $event"
@update:limit="queryParams.pageSize = $event"
@pagination="getList"
/>
</div>
</el-card>
@@ -287,8 +289,7 @@
<ApplicationForm ref="applicationFormRef" :form-data="form" @submit="submitForm" />
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取 消</el-button>
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">关 闭</el-button>
</div>
</template>
</el-dialog>
@@ -309,12 +310,14 @@
import { listProjectApplication, getProjectApplication, delProjectApplication, addProjectApplication, updateProjectApplication, submitApplication, getApplicationStatistics } from "@/api/cw/projectApplication"
import ApplicationForm from "./form.vue"
import ApplicationSummary from "./summary.vue"
import Pagination from "@/components/Pagination/index.vue"
export default {
name: "ProjectApplication",
components: {
ApplicationForm,
ApplicationSummary
ApplicationSummary,
Pagination
},
data() {
return {
@@ -373,6 +376,13 @@ export default {
this.applicationList = response.rows
this.total = response.total
this.loading = false
}).catch(error => {
console.error('获取项目申报列表失败:', error)
this.loading = false
this.applicationList = []
this.total = 0
// 可以添加用户友好的错误提示
this.$message.error('获取数据失败,请检查网络连接或联系管理员')
})
},
/** 获取统计数据 */
@@ -388,10 +398,10 @@ export default {
}).catch(error => {
console.error('获取统计数据失败:', error)
// 如果API失败,使用本地计算作为后备方案
this.totalCount = this.applicationList.length
this.approvedCount = this.applicationList.filter(item => item.approvalStatus === 'approved').length
this.pendingCount = this.applicationList.filter(item => item.approvalStatus === 'approving').length
this.draftCount = this.applicationList.filter(item => item.approvalStatus === 'draft').length
this.totalCount = this.applicationList.length
this.approvedCount = this.applicationList.filter(item => item.approvalStatus === 'approved').length
this.pendingCount = this.applicationList.filter(item => item.approvalStatus === 'approving').length
this.draftCount = this.applicationList.filter(item => item.approvalStatus === 'draft').length
})
},
/** 取消按钮 */
@@ -480,8 +490,12 @@ export default {
},
/** 提交按钮 */
submitForm() {
this.$refs["applicationFormRef"].validate((valid) => {
if (valid) {
this.$refs["applicationFormRef"].validate((valid, formData) => {
if (valid && formData) {
// 使用子组件回传的完整数据(包含绩效指标)
this.form = formData
console.log('提交表单数据:', this.form)
if (this.form.id != null) {
updateProjectApplication(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
@@ -497,6 +511,8 @@ export default {
this.getStatistics() // 新增后更新统计
})
}
} else {
this.$modal.msgError("请检查表单数据是否完整")
}
})
},
@@ -579,7 +595,7 @@ export default {
<style scoped lang="scss">
/* 继承后勤管理模块的样式风格 */
.page-container {
padding: 20px;
padding: 8px;
/* 统计卡片样式 */
.stats-row {
@@ -649,22 +665,18 @@ export default {
}
}
/* 卡片样式 */
.button-card, .table-card {
margin-bottom: 20px;
border-radius: 12px;
border: 1px solid #e4e7ed;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
/* 表格卡片布局 - 参考后勤管理功能 */
.table-card {
margin-top: 0;
}
:deep(.el-card__header) {
border-bottom: 1px solid rgba(93, 135, 255, 0.1);
background: linear-gradient(135deg, #fafbff 0%, #f8fafc 100%);
padding: 16px 20px;
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
:deep(.el-card__body) {
padding: 20px;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
.card-header {
@@ -696,12 +708,13 @@ export default {
}
}
/* 表格样式 */
/* 表格主体 */
.table-container {
width: 100%;
overflow-x: auto;
margin-bottom: 0;
}
/* 表格样式 - 参考后勤管理功能 */
.scroll-table {
width: 100%;
@@ -724,6 +737,69 @@ export default {
}
}
/* 表格横向滚动条样式优化 - 参考后勤管理功能 */
.scroll-table :deep(.el-table__body-wrapper) {
overflow-x: auto !important;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #409eff #f0f0f0;
}
.scroll-table :deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
/* Webkit 浏览器的横向滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 12px !important;
width: 12px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f0f0f0 !important;
border-radius: 6px !important;
border: 1px solid #e4e7ed !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #409eff !important;
border-radius: 6px !important;
border: 2px solid #f0f0f0 !important;
min-height: 20px !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #337ecc !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:active {
background: #2b6cb0 !important;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
/* 操作按钮样式 */
.action-buttons {
display: flex;
@@ -733,8 +809,8 @@ export default {
white-space: nowrap;
.el-button {
width: 32px !important;
height: 32px !important;
width: 27px !important;
height: 27px !important;
padding: 0 !important;
display: flex;
align-items: center;
@@ -748,12 +824,13 @@ export default {
color: #e74c3c;
}
/* 分页容器样式 */
/* 分页容器样式 - 参考后勤管理功能 */
.pagination-container {
display: flex;
justify-content: flex-end;
padding: 16px 0 0 0;
margin-top: 16px;
padding: 16px 20px 0 0 !important;
background-color: #fff;
margin-top: 0px !important;
}
/* 对话框样式 */
+59 -31
View File
@@ -155,7 +155,7 @@
<el-tag type="warning" size="small">分值占比总和:{{ performanceIndicatorTotal }}%</el-tag>
</div>
</template>
<el-table :data="validPerformanceIndicators" border style="width: 100%">
<el-table-column label="指标类型" width="120" align="center">
<template #default="scope">
@@ -229,7 +229,7 @@ const currentApplication = computed(() => {
console.log('application数据:', props.application);
return props.application;
}
// 否则使用内部获取的数据
console.log('=== 使用内部获取的application数据 ===');
console.log('internalApplication数据:', internalApplication.value);
@@ -239,10 +239,10 @@ const currentApplication = computed(() => {
// 获取申报详情数据
const fetchApplicationData = async (id) => {
if (!id) return;
console.log('=== ApplicationSummary 开始获取数据 ===');
console.log('applicationId:', id);
loading.value = true;
try {
const response = await getProjectApplication(id);
@@ -250,25 +250,25 @@ const fetchApplicationData = async (id) => {
console.log('API响应:', response);
console.log('响应数据:', response.data);
console.log('响应数据类型:', typeof response.data);
// 特别检查绩效指标字段
if (response.data) {
console.log('=== API响应中的绩效指标检查 ===');
console.log('response.data.performanceIndicators:', response.data.performanceIndicators);
console.log('performanceIndicators类型:', typeof response.data.performanceIndicators);
console.log('是否为数组:', Array.isArray(response.data.performanceIndicators));
// 检查所有可能的绩效指标字段名
console.log('可能的字段名检查:');
console.log('- performanceIndicators:', response.data.performanceIndicators);
console.log('- performance_indicators:', response.data.performance_indicators);
console.log('- indicators:', response.data.indicators);
console.log('- performanceIndicatorList:', response.data.performanceIndicatorList);
// 列出所有字段
console.log('响应数据的所有字段:', Object.keys(response.data));
}
internalApplication.value = response.data || {};
} catch (error) {
console.error('=== ApplicationSummary API调用失败 ===');
@@ -291,7 +291,7 @@ onMounted(() => {
console.log('=== ApplicationSummary 组件挂载 ===');
console.log('props.application:', props.application);
console.log('props.applicationId:', props.applicationId);
// 如果没有传入application但有applicationId,则获取数据
if (props.applicationId && (!props.application || Object.keys(props.application).length === 0)) {
fetchApplicationData(props.applicationId);
@@ -315,14 +315,14 @@ const hasPerformanceIndicators = computed(() => {
console.log('app.performanceIndicators类型:', typeof app.performanceIndicators);
console.log('app.performanceIndicators值:', app.performanceIndicators);
console.log('是否为数组:', Array.isArray(app.performanceIndicators));
if (app.performanceIndicators) {
console.log('绩效指标数组长度:', app.performanceIndicators.length);
console.log('绩效指标数组内容:', JSON.stringify(app.performanceIndicators, null, 2));
} else {
console.log('绩效指标数据为空或未定义');
}
const hasIndicators = app.performanceIndicators && app.performanceIndicators.length > 0;
console.log('最终判断 - 是否有绩效指标:', hasIndicators);
return hasIndicators;
@@ -346,18 +346,18 @@ const validPerformanceIndicators = computed(() => {
const app = currentApplication.value;
console.log('=== validPerformanceIndicators 计算 ===');
console.log('app.performanceIndicators:', app.performanceIndicators);
if (!app.performanceIndicators) {
console.log('没有绩效指标数据,返回空数组');
return [];
}
const validIndicators = app.performanceIndicators.filter(indicator => {
const weight = parseFloat(indicator.weightPercentage) || 0;
console.log(`指标 ${indicator.goalName}: 权重=${indicator.weightPercentage}, 解析后权重=${weight}, 是否有效=${weight > 0}`);
return weight > 0;
});
console.log('有效绩效指标数量:', validIndicators.length);
console.log('有效绩效指标详情:', validIndicators);
return validIndicators;
@@ -367,37 +367,65 @@ const validPerformanceIndicators = computed(() => {
const budgetBreakdown = computed(() => {
const app = currentApplication.value;
const currentYear = app.year || new Date().getFullYear();
console.log('=== 预算数据检查 ===');
console.log('application数据:', app);
console.log('当年预算字段:', {
console.log('=== 🔍 分年度预算数据详细检查 ===');
console.log('完整application数据:', app);
console.log('当前年度:', currentYear);
console.log('📊 分年度预算字段值详情:');
console.log('📅 当年预算:', {
currentYearTotal: app.currentYearTotal,
currentYearMunicipal: app.currentYearMunicipal,
currentYearSelf: app.currentYearSelf,
currentYearOther: app.currentYearOther
});
return [
console.log('📅 下年预算:', {
nextYearTotal: app.nextYearTotal,
nextYearMunicipal: app.nextYearMunicipal,
nextYearSelf: app.nextYearSelf,
nextYearOther: app.nextYearOther
});
console.log('📅 以后年度预算:', {
futureYearTotal: app.futureYearTotal,
futureYearMunicipal: app.futureYearMunicipal,
futureYearSelf: app.futureYearSelf,
futureYearOther: app.futureYearOther
});
const breakdown = [
{
year: `${currentYear}年`,
total: app.currentYearTotal || 0,
municipal: app.currentYearMunicipal || 0,
self: app.currentYearSelf || 0,
other: app.currentYearOther || 0
total: Number(app.currentYearTotal) || 0,
municipal: Number(app.currentYearMunicipal) || 0,
self: Number(app.currentYearSelf) || 0,
other: Number(app.currentYearOther) || 0
},
{
year: `${parseInt(currentYear) + 1}年`,
total: app.nextYearTotal || 0,
municipal: app.nextYearMunicipal || 0,
self: app.nextYearSelf || 0,
other: app.nextYearOther || 0
total: Number(app.nextYearTotal) || 0,
municipal: Number(app.nextYearMunicipal) || 0,
self: Number(app.nextYearSelf) || 0,
other: Number(app.nextYearOther) || 0
},
{
year: '以后年度',
total: app.futureYearTotal || 0,
municipal: app.futureYearMunicipal || 0,
self: app.futureYearSelf || 0,
other: app.futureYearOther || 0
total: Number(app.futureYearTotal) || 0,
municipal: Number(app.futureYearMunicipal) || 0,
self: Number(app.futureYearSelf) || 0,
other: Number(app.futureYearOther) || 0
}
].filter(item => item.total > 0); // 只显示有预算的年度
];
console.log('📋 计算后的budgetBreakdown数据:', breakdown);
console.log('📊 每年度预算汇总:');
breakdown.forEach((item, index) => {
const sum = item.municipal + item.self + item.other;
console.log(`${item.year}: 总投资=${item.total}, 细项合计=${sum}, 财政=${item.municipal}, 自筹=${item.self}, 其他=${item.other}`);
});
// 🔥 移除错误的过滤条件,始终显示所有年度
console.log('✅ 返回完整的分年度预算数据,不进行过滤');
return breakdown;
});
// 格式化金额
File diff suppressed because it is too large. Load diff
+685
View File
@@ -0,0 +1,685 @@
<template>
<div class="p-2 page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="animate.searchAnimate.enter" :leave-active-class="animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="申报编号" prop="applicationNo">
<el-input v-model="queryParams.applicationNo" placeholder="请输入申报编号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="申报人" prop="applicantName">
<el-input v-model="queryParams.applicantName" placeholder="请输入申报人姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</transition>
<!-- 快捷操作按钮 -->
<el-row :gutter="10" class="mb-[10px]">
<el-col :span="12">
<el-card shadow="hover" class="quick-action-card">
<div class="quick-action" @click="showPendingApprovals">
<el-icon class="quick-icon"><Clock /></el-icon>
<span>待我审批</span>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="hover" class="quick-action-card">
<div class="quick-action" @click="showMyApprovals">
<el-icon class="quick-icon"><User /></el-icon>
<span>我已审批</span>
</div>
</el-card>
</el-col>
</el-row>
<!-- 表格区域 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row :gutter="10" class="mb8">
<!-- 批量操作按钮暂时隐藏 -->
<!-- <el-col :span="1.5">
<el-button
type="success"
plain
icon="Check"
:disabled="multiple"
@click="handleBatchApprove"
v-hasPermi="['cw:approval:finance']"
>批量通过</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Close"
:disabled="multiple"
@click="handleBatchReject"
v-hasPermi="['cw:approval:finance']"
>批量退回</el-button>
</el-col> -->
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['cw:approval:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="approvalList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="申报编号" align="center" prop="applicationNo" v-if="columns[0].visible" width="180">
<template #default="scope">
<el-link type="primary" @click="openDetailDialog(scope.row)">{{ scope.row.applicationNo }}</el-link>
</template>
</el-table-column>
<el-table-column label="项目名称" align="center" prop="projectName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
<el-table-column label="申报人" align="center" prop="applicantName" v-if="columns[2].visible" width="120" />
<el-table-column label="申报部门" align="center" prop="departmentName" v-if="columns[3].visible" width="150" />
<el-table-column label="总投资(万元)" align="center" prop="totalInvestment" v-if="columns[4].visible" width="120">
<template #default="scope">
<span class="amount-text">{{ formatAmount(scope.row.totalInvestment) }}</span>
</template>
</el-table-column>
<el-table-column label="审批状态" align="center" prop="nodeStatus" v-if="columns[5].visible" width="120">
<template #default="scope">
<dict-tag :options="cw_approval_status" :value="scope.row.nodeStatus" />
</template>
</el-table-column>
<el-table-column label="提交时间" align="center" prop="startTime" v-if="columns[6].visible" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.startTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看详情" placement="top">
<el-button link type="info" icon="View" @click="openDetailDialog(scope.row)" v-hasPermi="['cw:approval:query']"></el-button>
</el-tooltip>
<el-tooltip content="财务审批" placement="top" v-if="scope.row.nodeStatus === 'waiting'">
<el-button link type="success" icon="Check" @click="openApprovalDialog(scope.row)" v-hasPermi="['cw:approval:finance']"></el-button>
</el-tooltip>
<el-tooltip content="审批历史" placement="top">
<el-button link type="primary" icon="Clock" @click="handleApprovalHistory(scope.row)" v-hasPermi="['cw:approval:query']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-card>
<!-- 项目详情对话框 -->
<el-dialog :title="detailDialog.title" v-model="detailDialog.visible" width="1000px" append-to-body>
<ApplicationSummary v-if="currentApplication && currentApplication.applicationId" :application-id="currentApplication.applicationId" />
<template #footer>
<div class="dialog-footer">
<el-button @click="detailDialog.visible = false">关 闭</el-button>
</div>
</template>
</el-dialog>
<!-- 财务审批对话框 -->
<el-dialog :title="approvalDialog.title" v-model="approvalDialog.visible" width="600px" append-to-body>
<el-form ref="approvalFormRef" :model="approvalForm" :rules="approvalRules" label-width="100px">
<el-form-item label="申报编号">
<span>{{ currentApplication.applicationNo }}</span>
</el-form-item>
<el-form-item label="项目名称">
<span>{{ currentApplication.projectName }}</span>
</el-form-item>
<el-form-item label="申报人">
<span>{{ currentApplication.applicantName }}</span>
</el-form-item>
<el-form-item label="总投资">
<span>{{ formatAmount(currentApplication.totalInvestment) }} 万元</span>
</el-form-item>
<el-form-item label="审批决定" prop="approvalResult" required>
<el-radio-group v-model="approvalForm.approvalResult" @change="onApprovalResultChange">
<el-radio value="approve">通过(提交院领导审批)</el-radio>
<el-radio value="reject">退回申请人</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="选择院领导" prop="selectedApproverId" v-if="approvalForm.approvalResult === 'approve'">
<el-select
v-model="approvalForm.selectedApproverId"
placeholder="请选择院领导"
clearable
filterable
:loading="loadingApprovers"
style="width: 100%">
<el-option
v-for="approver in availableApprovers"
:key="approver.userId"
:label="approver.nickName + '(' + approver.userName + ')'"
:value="approver.userId">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="审批意见" prop="approvalComment">
<el-input
v-model="approvalForm.approvalComment"
type="textarea"
:rows="4"
placeholder="请输入财务审批意见"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="approvalDialog.visible = false">取 消</el-button>
<el-button type="primary" @click="submitApproval" :loading="approvalDialog.loading">确 定</el-button>
</div>
</template>
</el-dialog>
<!-- 审批历史对话框 -->
<el-dialog title="审批历史" v-model="historyDialog.visible" width="800px" append-to-body>
<el-timeline>
<el-timeline-item
v-for="(record, index) in approvalHistory"
:key="index"
:timestamp="parseTime(record.endTime || record.startTime)"
:type="getTimelineType(record.approveAction)"
>
<el-card>
<h4>{{ record.nodeName }}</h4>
<p><strong>审批人:</strong>{{ record.approverName }}</p>
<p><strong>审批结果:</strong>
<el-tag :type="getApprovalResultType(record.approveAction)">
{{ getApprovalResultText(record.approveAction) }}
</el-tag>
</p>
<p v-if="record.approveOpinion"><strong>审批意见:</strong>{{ record.approveOpinion }}</p>
</el-card>
</el-timeline-item>
</el-timeline>
<template #footer>
<div class="dialog-footer">
<el-button @click="historyDialog.visible = false">关 闭</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script>
import {
listApprovalRecord,
exportApprovalRecord,
getMyApprovals,
getFinancePendingApprovals,
getApprovalHistory,
getAvailableApprovers,
getFinanceApprovedRecords
} from "@/api/cw/approvalRecord";
import { financeApproveApplication, batchFinanceApproveApplication } from "@/api/cw/financeApproval";
import { useDict } from "@/utils/dict";
import ApplicationSummary from "../application/summary.vue";
export default {
name: 'CwFinanceApproval',
components: {
ApplicationSummary
},
dicts: ['cw_approval_status'],
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 审批列表
approvalList: [],
// 动画配置
animate: {
searchAnimate: {
enter: 'animate__animated animate__fadeIn',
leave: 'animate__animated animate__fadeOut'
}
},
// 字典数据
cw_approval_status: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
applicationNo: '',
projectName: '',
applicantName: ''
},
// 列信息
columns: [
{ key: 0, label: `申报编号`, visible: true },
{ key: 1, label: `项目名称`, visible: true },
{ key: 2, label: `申报人`, visible: true },
{ key: 3, label: `申报部门`, visible: true },
{ key: 4, label: `总投资`, visible: true },
{ key: 5, label: `审批状态`, visible: true },
{ key: 6, label: `提交时间`, visible: true }
],
// 审批对话框
approvalDialog: {
visible: false,
title: '',
loading: false
},
// 详情对话框
detailDialog: {
visible: false,
title: ''
},
// 历史记录对话框
historyDialog: {
visible: false
},
// 当前申报信息
currentApplication: {},
// 当前申报ID
currentApplicationId: null,
// 审批表单
approvalForm: {
applicationId: null,
approvalResult: '',
approvalComment: '',
selectedApproverId: null
},
// 审批表单验证
approvalRules: {
approvalResult: [
{ required: true, message: "请选择审批结果", trigger: "change" }
],
selectedApproverId: [
{
validator: (rule, value, callback) => {
if (this.approvalForm.approvalResult === 'approve' && !value) {
callback(new Error('审批通过时必须选择院领导'));
} else {
callback();
}
},
trigger: 'change'
}
],
approvalComment: [
{ required: true, message: "请输入审批意见", trigger: "blur" },
{ min: 5, max: 500, message: "审批意见长度在 5 到 500 个字符", trigger: "blur" }
]
},
// 可选院领导列表
availableApprovers: [],
// 院领导列表加载状态
loadingApprovers: false,
// 审批历史数据
approvalHistory: []
};
},
created() {
this.getList();
},
methods: {
/** 查询审批列表 */
getList() {
this.loading = true;
// 默认显示当前用户的待审批记录
getFinancePendingApprovals(this.queryParams).then(response => {
this.approvalList = response.data || [];
this.total = this.approvalList.length;
this.loading = false;
}).catch(error => {
console.error('获取待审批列表失败:', error);
this.loading = false;
this.$modal.msgError('获取待审批列表失败');
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.daterangeSubmitTime = [];
this.resetForm("queryFormRef");
this.handleQuery();
},
/** 快捷操作 */
showPendingApprovals() {
this.loading = true;
getFinancePendingApprovals({}).then(response => {
this.approvalList = response.data || [];
this.total = this.approvalList.length;
this.loading = false;
this.$modal.msgSuccess(`查询到 ${this.total} 条待审批记录`);
}).catch(error => {
this.loading = false;
this.$modal.msgError('获取待审批列表失败');
});
},
showMyApprovals() {
this.loading = true;
console.log('🔍 查询财务已审批记录');
// 使用专门的财务审批已审批查询
getFinanceApprovedRecords({}).then(response => {
this.approvalList = response.data || [];
this.total = this.approvalList.length;
this.loading = false;
this.$modal.msgSuccess(`查询到 ${this.total} 条已审批记录`);
console.log('✅ 财务已审批记录:', this.approvalList);
}).catch(error => {
console.error('❌ 获取财务已审批列表失败:', error);
this.loading = false;
this.$modal.msgError('获取已审批列表失败');
});
},
/** 多选框选中数据 */
handleSelectionChange(selection) {
this.ids = selection.map(item => item.applicationId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
console.log('提取的项目申报ID:', this.ids);
},
/** 打开详情对话框 */
openDetailDialog(row) {
this.currentApplication = row;
this.detailDialog.title = `项目申报详情 - ${row.applicationNo}`;
this.detailDialog.visible = true;
},
/** 打开财务审批对话框 */
openApprovalDialog(row) {
this.resetApprovalForm();
this.currentApplication = row;
this.currentApplicationId = row.applicationId;
this.approvalForm.applicationId = row.applicationId;
this.approvalDialog.title = `财务审批 - ${row.applicationNo}`;
this.approvalDialog.visible = true;
// 预加载院领导列表
this.loadAvailableApprovers(row.applicationId);
},
/** 重置审批表单 */
resetApprovalForm() {
this.approvalForm = {
applicationId: null,
approvalResult: '',
approvalComment: '',
selectedApproverId: null
};
// 清空院领导列表
this.availableApprovers = [];
},
/** 审批结果改变时的处理 */
onApprovalResultChange(value) {
console.log('📋 审批结果改变:', value);
if (value === 'approve') {
// 选择通过时,加载院领导列表
if (this.currentApplicationId) {
this.loadAvailableApprovers(this.currentApplicationId);
}
} else {
// 当切换到非通过状态时,清空选择的院领导
this.approvalForm.selectedApproverId = null;
}
},
/** 获取可选院领导列表 */
async loadAvailableApprovers(applicationId) {
console.log('🔍 开始加载院领导列表,applicationId:', applicationId);
if (!applicationId) {
console.warn('⚠️ applicationId为空,无法获取院领导列表');
return;
}
this.loadingApprovers = true;
try {
const response = await getAvailableApprovers(applicationId);
console.log('📊 API响应成功,数据长度:', response.data?.length);
this.availableApprovers = response.data || [];
console.log('✅ 获取院领导列表成功:', this.availableApprovers.length, '个院领导');
if (this.availableApprovers.length === 0) {
this.$modal.msgWarning('没有找到可用的院领导,请联系管理员');
}
} catch (error) {
console.error('❌ 获取院领导列表失败:', error);
this.$modal.msgError('获取院领导列表失败,请重试');
this.availableApprovers = [];
} finally {
this.loadingApprovers = false;
}
},
/** 提交财务审批 */
submitApproval() {
this.$refs["approvalFormRef"].validate(valid => {
if (valid) {
this.approvalDialog.loading = true;
financeApproveApplication(this.approvalForm).then(response => {
this.$modal.msgSuccess("财务审批成功");
this.approvalDialog.visible = false;
this.approvalDialog.loading = false;
this.getList();
}).catch(error => {
console.error('财务审批失败:', error);
this.approvalDialog.loading = false;
this.$modal.msgError('财务审批失败');
});
}
});
},
/** 批量审批通过 */
handleBatchApprove() {
if (this.ids.length === 0) {
this.$modal.msgError('请至少选择一条记录');
return;
}
this.$modal.confirm('确认要批量通过选中的申报吗?').then(() => {
const batchData = this.ids.map(applicationId => ({
applicationId: applicationId,
approvalResult: 'approve',
approvalComment: '财务批量审批通过'
}));
batchFinanceApproveApplication(batchData).then(response => {
this.$modal.msgSuccess("批量审批成功");
this.getList();
this.ids = [];
this.multiple = true;
}).catch(error => {
console.error('批量审批失败:', error);
this.$modal.msgError('批量审批失败');
});
});
},
/** 批量退回 */
handleBatchReject() {
if (this.ids.length === 0) {
this.$modal.msgError('请至少选择一条记录');
return;
}
this.$modal.confirm('确认要批量退回选中的申报吗?').then(() => {
const batchData = this.ids.map(applicationId => ({
applicationId: applicationId,
approvalResult: 'reject',
approvalComment: '财务审批退回,请修改后重新提交'
}));
batchFinanceApproveApplication(batchData).then(response => {
this.$modal.msgSuccess("批量退回成功");
this.getList();
this.ids = [];
this.multiple = true;
}).catch(error => {
console.error('批量退回失败:', error);
this.$modal.msgError('批量退回失败');
});
});
},
/** 审批历史 */
handleApprovalHistory(row) {
getApprovalHistory(row.applicationId).then(response => {
this.approvalHistory = response.data || [];
this.historyDialog.visible = true;
}).catch(error => {
console.error('获取审批历史失败:', error);
this.$modal.msgError('获取审批历史失败');
});
},
/** 导出按钮操作 */
handleExport() {
this.download('cw/approval/export', {
...this.queryParams
}, `finance_approval_${new Date().getTime()}.xlsx`);
},
/** 格式化金额 */
formatAmount(amount) {
if (!amount) return '0.00';
return Number(amount).toFixed(2);
},
/** 获取时间线类型 */
getTimelineType(action) {
switch (action) {
case 'approve': return 'success';
case 'reject': return 'danger';
case 'submit': return 'primary';
default: return 'info';
}
},
/** 获取审批结果类型 */
getApprovalResultType(action) {
switch (action) {
case 'approve': return 'success';
case 'reject': return 'danger';
case 'submit': return 'primary';
default: return 'info';
}
},
/** 获取审批结果文本 */
getApprovalResultText(action) {
switch (action) {
case 'approve': return '通过';
case 'reject': return '退回';
case 'submit': return '提交';
default: return '待处理';
}
}
}
};
</script>
<style lang="scss" scoped>
.page-container {
padding: 20px;
}
.quick-action-card {
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
}
.quick-action {
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
text-align: center;
.quick-icon {
font-size: 24px;
margin-right: 8px;
color: var(--el-color-primary);
}
span {
font-size: 16px;
font-weight: 500;
color: var(--el-text-color-primary);
}
}
.search-btn-container {
text-align: right;
}
.table-card {
margin-top: 10px;
}
.amount-text {
font-weight: 600;
color: var(--el-color-success);
}
.el-timeline-item {
margin-bottom: 20px;
}
.animate__animated {
animation-duration: 0.3s;
}
</style>
+602
View File
@@ -0,0 +1,602 @@
<template>
<div class="p-2 page-container">
<!-- 搜索区域 -->
<transition :enter-active-class="animate.searchAnimate.enter" :leave-active-class="animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="申报编号" prop="applicationNo">
<el-input v-model="queryParams.applicationNo" placeholder="请输入申报编号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="申报人" prop="applicantName">
<el-input v-model="queryParams.applicantName" placeholder="请输入申报人姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</transition>
<!-- 快捷操作按钮 -->
<el-row :gutter="10" class="mb-[10px]">
<el-col :span="12">
<el-card shadow="hover" class="quick-action-card">
<div class="quick-action" @click="showPendingApprovals">
<el-icon class="quick-icon"><Clock /></el-icon>
<span>待我审批</span>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="hover" class="quick-action-card">
<div class="quick-action" @click="showMyApprovals">
<el-icon class="quick-icon"><User /></el-icon>
<span>我已审批</span>
</div>
</el-card>
</el-col>
</el-row>
<!-- 表格区域 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row :gutter="10" class="mb8">
<!-- 批量操作按钮暂时隐藏 -->
<!-- <el-col :span="1.5">
<el-button
type="success"
plain
icon="Check"
:disabled="multiple"
@click="handleBatchApprove"
v-hasPermi="['cw:approval:leader']"
>批量通过</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Close"
:disabled="multiple"
@click="handleBatchReject"
v-hasPermi="['cw:approval:leader']"
>批量退回</el-button>
</el-col> -->
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['cw:approval:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="approvalList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="申报编号" align="center" prop="applicationNo" v-if="columns[0].visible" width="180">
<template #default="scope">
<el-link type="primary" @click="openDetailDialog(scope.row)">{{ scope.row.applicationNo }}</el-link>
</template>
</el-table-column>
<el-table-column label="项目名称" align="center" prop="projectName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
<el-table-column label="申报人" align="center" prop="applicantName" v-if="columns[2].visible" width="120" />
<el-table-column label="申报部门" align="center" prop="departmentName" v-if="columns[3].visible" width="150" />
<el-table-column label="总投资(万元)" align="center" prop="totalInvestment" v-if="columns[4].visible" width="120">
<template #default="scope">
<span class="amount-text">{{ formatAmount(scope.row.totalInvestment) }}</span>
</template>
</el-table-column>
<el-table-column label="审批状态" align="center" prop="nodeStatus" v-if="columns[5].visible" width="120">
<template #default="scope">
<dict-tag :options="cw_approval_status" :value="scope.row.nodeStatus" />
</template>
</el-table-column>
<el-table-column label="提交时间" align="center" prop="startTime" v-if="columns[6].visible" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.startTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看详情" placement="top">
<el-button link type="info" icon="View" @click="openDetailDialog(scope.row)" v-hasPermi="['cw:approval:query']"></el-button>
</el-tooltip>
<el-tooltip content="审批操作" placement="top" v-if="scope.row.nodeStatus === 'waiting'">
<el-button link type="success" icon="Check" @click="openApprovalDialog(scope.row)" v-hasPermi="['cw:approval:leader']"></el-button>
</el-tooltip>
<el-tooltip content="审批历史" placement="top">
<el-button link type="primary" icon="Clock" @click="handleApprovalHistory(scope.row)" v-hasPermi="['cw:approval:query']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-card>
<!-- 项目详情对话框 -->
<el-dialog :title="detailDialog.title" v-model="detailDialog.visible" width="1000px" append-to-body>
<ApplicationSummary v-if="currentApplication && currentApplication.applicationId" :application-id="currentApplication.applicationId" />
<template #footer>
<div class="dialog-footer">
<el-button @click="detailDialog.visible = false">关 闭</el-button>
</div>
</template>
</el-dialog>
<!-- 审批对话框 -->
<el-dialog :title="approvalDialog.title" v-model="approvalDialog.visible" width="600px" append-to-body>
<el-form ref="approvalFormRef" :model="approvalForm" :rules="approvalRules" label-width="100px">
<el-form-item label="申报编号">
<span>{{ currentApplication.applicationNo }}</span>
</el-form-item>
<el-form-item label="项目名称">
<span>{{ currentApplication.projectName }}</span>
</el-form-item>
<el-form-item label="申报人">
<span>{{ currentApplication.applicantName }}</span>
</el-form-item>
<el-form-item label="总投资">
<span>{{ formatAmount(currentApplication.totalInvestment) }} 万元</span>
</el-form-item>
<el-form-item label="审批决定" prop="approvalResult" required>
<el-radio-group v-model="approvalForm.approvalResult">
<el-radio label="approve">通过</el-radio>
<el-radio label="reject">退回申请人</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审批意见" prop="approvalComment">
<el-input
v-model="approvalForm.approvalComment"
type="textarea"
:rows="4"
placeholder="请输入审批意见"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="approvalDialog.visible = false">取 消</el-button>
<el-button type="primary" @click="submitApproval" :loading="approvalDialog.loading">确 定</el-button>
</div>
</template>
</el-dialog>
<!-- 审批历史对话框 -->
<el-dialog title="审批历史" v-model="historyDialog.visible" width="800px" append-to-body>
<el-timeline>
<el-timeline-item
v-for="(record, index) in approvalHistory"
:key="index"
:timestamp="parseTime(record.endTime || record.startTime)"
:type="getTimelineType(record.approveAction)"
>
<el-card>
<h4>{{ record.nodeName }}</h4>
<p><strong>审批人:</strong>{{ record.approverName }}</p>
<p><strong>审批结果:</strong>
<el-tag :type="getApprovalResultType(record.approveAction)">
{{ getApprovalResultText(record.approveAction) }}
</el-tag>
</p>
<p v-if="record.approveOpinion"><strong>审批意见:</strong>{{ record.approveOpinion }}</p>
</el-card>
</el-timeline-item>
</el-timeline>
<template #footer>
<div class="dialog-footer">
<el-button @click="historyDialog.visible = false">关 闭</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script>
import {
getPendingApprovals,
getApprovalHistory,
exportApprovalRecord,
getLeaderPendingApprovals,
getLeaderApprovedApprovals
} from "@/api/cw/approvalRecord";
import { leaderApproveApplication, batchLeaderApproveApplication } from "@/api/cw/leaderApproval";
import { useDict } from "@/utils/dict";
import ApplicationSummary from "../application/summary.vue";
export default {
name: 'CwLeaderApproval',
components: {
ApplicationSummary
},
dicts: ['cw_approval_status'],
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 审批列表
approvalList: [],
// 动画配置
animate: {
searchAnimate: {
enter: 'animate__animated animate__fadeIn',
leave: 'animate__animated animate__fadeOut'
}
},
// 字典数据
cw_approval_status: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
applicationNo: '',
projectName: '',
applicantName: ''
},
// 列信息
columns: [
{ key: 0, label: `申报编号`, visible: true },
{ key: 1, label: `项目名称`, visible: true },
{ key: 2, label: `申报人`, visible: true },
{ key: 3, label: `申报部门`, visible: true },
{ key: 4, label: `总投资`, visible: true },
{ key: 5, label: `审批状态`, visible: true },
{ key: 6, label: `提交时间`, visible: true }
],
// 审批对话框
approvalDialog: {
visible: false,
title: '',
loading: false
},
// 详情对话框
detailDialog: {
visible: false,
title: ''
},
// 历史记录对话框
historyDialog: {
visible: false
},
// 当前申报信息
currentApplication: {},
// 当前申报ID
currentApplicationId: null,
// 审批表单
approvalForm: {
applicationId: null,
approvalResult: '',
approvalComment: ''
},
// 审批表单验证
approvalRules: {
approvalResult: [
{ required: true, message: "请选择审批结果", trigger: "change" }
],
approvalComment: [
{ required: true, message: "请输入审批意见", trigger: "blur" },
{ min: 5, max: 500, message: "审批意见长度在 5 到 500 个字符", trigger: "blur" }
]
},
// 审批历史数据
approvalHistory: []
};
},
created() {
this.getList();
},
methods: {
/** 查询审批列表 */
getList() {
this.loading = true;
// 使用学院领导专用的待审批记录查询
getLeaderPendingApprovals(this.queryParams).then(response => {
this.approvalList = response.data || [];
this.total = this.approvalList.length;
this.loading = false;
}).catch(error => {
console.error('获取待审批列表失败:', error);
this.loading = false;
this.$modal.msgError('获取待审批列表失败');
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.daterangeSubmitTime = [];
this.resetForm("queryFormRef");
this.handleQuery();
},
/** 快捷操作 */
showPendingApprovals() {
console.log('=== 🔍 查询学院领导待审批 ===');
this.loading = true;
getLeaderPendingApprovals({}).then(response => {
console.log('✅ 学院领导待审批数据获取成功:', response.data);
this.approvalList = response.data || [];
this.total = this.approvalList.length;
this.loading = false;
this.$modal.msgSuccess(`查询到 ${this.total} 条待审批记录`);
}).catch(error => {
console.error('❌ 获取学院领导待审批列表失败:', error);
this.loading = false;
this.$modal.msgError('获取待审批列表失败');
});
},
showMyApprovals() {
console.log('=== 🔍 查询我已审批 ===');
this.loading = true;
getLeaderApprovedApprovals({}).then(response => {
console.log('✅ 已审批数据获取成功:', response.data);
this.approvalList = response.data || [];
this.total = this.approvalList.length;
this.loading = false;
this.$modal.msgSuccess(`查询到 ${this.total} 条已审批记录`);
}).catch(error => {
console.error('❌ 获取已审批列表失败:', error);
this.loading = false;
this.$modal.msgError('获取已审批列表失败');
});
},
/** 多选框选中数据 */
handleSelectionChange(selection) {
this.ids = selection.map(item => item.applicationId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
console.log('提取的项目申报ID:', this.ids);
},
/** 打开详情对话框 */
openDetailDialog(row) {
this.currentApplication = row;
this.detailDialog.title = `项目申报详情 - ${row.applicationNo}`;
this.detailDialog.visible = true;
},
/** 打开审批对话框 */
openApprovalDialog(row) {
this.resetApprovalForm();
this.currentApplication = row;
this.currentApplicationId = row.applicationId;
this.approvalForm.applicationId = row.applicationId;
this.approvalDialog.title = `分管领导审批 - ${row.applicationNo}`;
this.approvalDialog.visible = true;
},
/** 重置审批表单 */
resetApprovalForm() {
this.approvalForm = {
applicationId: null,
approvalResult: '',
approvalComment: ''
};
},
/** 提交审批 */
submitApproval() {
this.$refs["approvalFormRef"].validate(valid => {
if (valid) {
this.approvalDialog.loading = true;
leaderApproveApplication(this.approvalForm).then(response => {
this.$modal.msgSuccess("审批成功");
this.approvalDialog.visible = false;
this.approvalDialog.loading = false;
this.getList();
}).catch(error => {
console.error('审批失败:', error);
this.approvalDialog.loading = false;
this.$modal.msgError('审批失败');
});
}
});
},
/** 批量审批通过 */
handleBatchApprove() {
if (this.ids.length === 0) {
this.$modal.msgError('请至少选择一条记录');
return;
}
this.$modal.confirm('确认要批量通过选中的申报吗?').then(() => {
const batchData = this.ids.map(applicationId => ({
applicationId: applicationId,
approvalResult: 'approve',
approvalComment: '分管领导批量审批通过'
}));
batchLeaderApproveApplication(batchData).then(response => {
this.$modal.msgSuccess("批量审批成功");
this.getList();
this.ids = [];
this.multiple = true;
}).catch(error => {
console.error('批量审批失败:', error);
this.$modal.msgError('批量审批失败');
});
});
},
/** 批量退回 */
handleBatchReject() {
if (this.ids.length === 0) {
this.$modal.msgError('请至少选择一条记录');
return;
}
this.$modal.confirm('确认要批量退回选中的申报吗?').then(() => {
const batchData = this.ids.map(applicationId => ({
applicationId: applicationId,
approvalResult: 'reject',
approvalComment: '分管领导审批退回,请修改后重新提交'
}));
batchLeaderApproveApplication(batchData).then(response => {
this.$modal.msgSuccess("批量退回成功");
this.getList();
this.ids = [];
this.multiple = true;
}).catch(error => {
console.error('批量退回失败:', error);
this.$modal.msgError('批量退回失败');
});
});
},
/** 审批历史 */
handleApprovalHistory(row) {
getApprovalHistory(row.applicationId).then(response => {
this.approvalHistory = response.data || [];
this.historyDialog.visible = true;
}).catch(error => {
console.error('获取审批历史失败:', error);
this.$modal.msgError('获取审批历史失败');
});
},
/** 导出按钮操作 */
handleExport() {
this.download('cw/approval/export', {
...this.queryParams
}, `leader_approval_${new Date().getTime()}.xlsx`);
},
/** 格式化金额 */
formatAmount(amount) {
if (!amount) return '0.00';
return Number(amount).toFixed(2);
},
/** 获取时间线类型 */
getTimelineType(action) {
switch (action) {
case 'approve': return 'success';
case 'reject': return 'danger';
case 'submit': return 'primary';
default: return 'info';
}
},
/** 获取审批结果类型 */
getApprovalResultType(action) {
switch (action) {
case 'approve': return 'success';
case 'reject': return 'danger';
case 'submit': return 'primary';
default: return 'info';
}
},
/** 获取审批结果文本 */
getApprovalResultText(action) {
switch (action) {
case 'approve': return '通过';
case 'reject': return '退回';
case 'submit': return '提交';
default: return '待处理';
}
}
}
};
</script>
<style lang="scss" scoped>
.page-container {
padding: 20px;
}
.quick-action-card {
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
}
.quick-action {
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
text-align: center;
.quick-icon {
font-size: 24px;
margin-right: 8px;
color: var(--el-color-primary);
}
span {
font-size: 16px;
font-weight: 500;
color: var(--el-text-color-primary);
}
}
.search-btn-container {
text-align: right;
}
.table-card {
margin-top: 10px;
}
.amount-text {
font-weight: 600;
color: var(--el-color-success);
}
.el-timeline-item {
margin-bottom: 20px;
}
.animate__animated {
animation-duration: 0.3s;
}
</style>
+674
View File
@@ -0,0 +1,674 @@
<template>
<div class="app-container">
<!-- 筛选条件 -->
<el-card shadow="never" class="mb-3">
<el-form :model="queryParams" ref="queryRef" :inline="true">
<el-form-item label="预算年度" prop="budgetYear">
<el-select v-model="queryParams.budgetYear" placeholder="请选择预算年度" clearable style="width: 200px">
<el-option label="全部年度" value="" />
<el-option label="2024年" value="2024" />
<el-option label="2025年" value="2025" />
<el-option label="2026年" value="2026" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 统计概览 -->
<el-row :gutter="20" class="mb-3">
<el-col :span="6">
<el-card shadow="never" class="statistics-card">
<div class="statistics-item">
<div class="statistics-value">{{ statusStatistics.totalCount || 0 }}</div>
<div class="statistics-label">项目总数</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="never" class="statistics-card">
<div class="statistics-item">
<div class="statistics-value">{{ formatAmount(statusStatistics.totalAmount) }} 万元</div>
<div class="statistics-label">预算总额</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="never" class="statistics-card">
<div class="statistics-item">
<div class="statistics-value">{{ getApprovedCount() }}</div>
<div class="statistics-label">已通过项目</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="never" class="statistics-card">
<div class="statistics-item">
<div class="statistics-value">{{ departmentStatistics.length || 0 }}</div>
<div class="statistics-label">涉及部门</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- Tab切换 -->
<el-card shadow="never">
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<!-- 状态统计 -->
<el-tab-pane label="状态统计" name="status">
<el-row :gutter="20">
<el-col :span="12">
<div class="chart-container">
<h4>项目状态分布</h4>
<div ref="statusChart" style="height: 300px;"></div>
</div>
</el-col>
<el-col :span="12">
<div class="statistics-table">
<h4>状态统计详情</h4>
<el-table :data="statusStatistics.statusStatistics" stripe>
<el-table-column prop="statusLabel" label="状态" width="120" />
<el-table-column prop="count" label="数量" align="center" />
<el-table-column label="占比" align="center">
<template #default="scope">
{{ getPercentage(scope.row.count, statusStatistics.totalCount) }}%
</template>
</el-table-column>
</el-table>
</div>
</el-col>
</el-row>
</el-tab-pane>
<!-- 部门汇总 -->
<el-tab-pane label="部门汇总" name="department">
<el-table :data="departmentStatistics" stripe>
<el-table-column prop="departmentName" label="部门名称" width="200" />
<el-table-column prop="projectCount" label="项目数量" align="center" width="100" />
<el-table-column prop="totalAmount" label="预算总额(万元)" align="center" width="150">
<template #default="scope">
{{ formatAmount(scope.row.totalAmount) }}
</template>
</el-table-column>
<el-table-column prop="approvedCount" label="已通过数量" align="center" width="120" />
<el-table-column prop="approvedAmount" label="已通过金额(万元)" align="center" width="160">
<template #default="scope">
{{ formatAmount(scope.row.approvedAmount) }}
</template>
</el-table-column>
<el-table-column label="通过率" align="center" width="100">
<template #default="scope">
{{ getPercentage(scope.row.approvedCount, scope.row.projectCount) }}%
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 年度汇总 -->
<el-tab-pane label="年度汇总" name="yearly">
<el-table :data="yearlyStatistics" stripe>
<el-table-column prop="budgetYear" label="预算年度" width="150" />
<el-table-column prop="projectCount" label="项目数量" align="center" width="100" />
<el-table-column prop="totalAmount" label="预算总额(万元)" align="center" width="150">
<template #default="scope">
{{ formatAmount(scope.row.totalAmount) }}
</template>
</el-table-column>
<el-table-column prop="approvedCount" label="已通过数量" align="center" width="120" />
<el-table-column prop="approvedAmount" label="已通过金额(万元)" align="center" width="160">
<template #default="scope">
{{ formatAmount(scope.row.approvedAmount) }}
</template>
</el-table-column>
<el-table-column label="通过率" align="center" width="100">
<template #default="scope">
{{ getPercentage(scope.row.approvedCount, scope.row.projectCount) }}%
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 项目清单 -->
<el-tab-pane label="项目清单" name="project">
<el-table
:data="projectList"
stripe
v-loading="loading"
class="scroll-table"
border
:height="600"
style="width: 100%"
>
<el-table-column prop="applicationNo" label="申报编号" width="160" fixed="left" />
<el-table-column prop="projectName" label="项目名称" width="200" show-overflow-tooltip />
<el-table-column prop="departmentName" label="申报部门" width="150" />
<el-table-column prop="applicantName" label="申报人" width="100" />
<el-table-column prop="year" label="预算年度" width="100" align="center" />
<el-table-column prop="totalInvestment" label="总投资(万元)" width="120" align="center">
<template #default="scope">
{{ formatAmount(scope.row.totalInvestment) }}
</template>
</el-table-column>
<el-table-column prop="approvalStatus" label="审批状态" width="100" align="center">
<template #default="scope">
<dict-tag :options="cw_approval_status" :value="scope.row.approvalStatus" />
</template>
</el-table-column>
<el-table-column prop="submitTime" label="提交时间" width="160" align="center">
<template #default="scope">
{{ parseTime(scope.row.submitTime) }}
</template>
</el-table-column>
<!-- 项目实施时间和备注信息 -->
<el-table-column prop="startDate" label="预计开始时间" width="140" align="center">
<template #default="scope">
{{ parseTime(scope.row.startDate, '{y}-{m}-{d}') }}
</template>
</el-table-column>
<el-table-column prop="endDate" label="预计结束时间" width="140" align="center">
<template #default="scope">
{{ parseTime(scope.row.endDate, '{y}-{m}-{d}') }}
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" width="200" show-overflow-tooltip />
<el-table-column label="操作" align="center" width="100" fixed="right">
<template #default="scope">
<el-tooltip content="查看详情" placement="top">
<el-button link type="info" icon="View" @click="viewDetail(scope.row)" v-hasPermi="['cw:approval:query']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-show="projectTotal > 0"
:total="projectTotal"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getProjectList"
/>
</el-tab-pane>
</el-tabs>
</el-card>
<!-- 项目详情对话框 -->
<el-dialog :title="detailDialog.title" v-model="detailDialog.visible" width="1000px" append-to-body>
<ApplicationSummary
v-if="currentApplication && (currentApplication.id || currentApplication.applicationId)"
:applicationId="currentApplication.id || currentApplication.applicationId"
:application="currentApplication"
:key="currentApplication.id || currentApplication.applicationId"
/>
<template #footer>
<div class="dialog-footer">
<el-button @click="detailDialog.visible = false">关 闭</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script>
import {
getStatisticsList,
getStatusStatistics,
getDepartmentStatistics,
getYearlyStatistics
} from '@/api/cw/statistics'
import { parseTime } from '@/utils/ruoyi'
import * as echarts from 'echarts'
import Pagination from '@/components/Pagination/index.vue'
import ApplicationSummary from '@/views/cw/application/summary.vue'
export default {
name: 'CwStatistics',
dicts: ['cw_approval_status'],
components: {
Pagination,
ApplicationSummary
},
data() {
return {
// 加载状态
loading: true,
// 当前激活的tab
activeTab: 'status',
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
budgetYear: '2024' // 设置默认年度为2024
},
// 统计数据
statusStatistics: {
totalCount: 0,
totalAmount: 0,
statusStatistics: []
},
departmentStatistics: [],
yearlyStatistics: [],
projectList: [],
projectTotal: 0,
total: 0,
// 图表实例
statusChartInstance: null,
// 字典数据
cw_approval_status: [
{ label: '草稿', value: 'draft', cssClass: '', listClass: 'info' },
{ label: '已提交', value: 'submitted', cssClass: '', listClass: 'primary' },
{ label: '审批中', value: 'approving', cssClass: '', listClass: 'warning' },
{ label: '已通过', value: 'approved', cssClass: '', listClass: 'success' },
{ label: '已拒绝', value: 'rejected', cssClass: '', listClass: 'danger' },
{ label: '已退回', value: 'returned', cssClass: '', listClass: 'warning' },
{ label: '已取消', value: 'cancelled', cssClass: '', listClass: 'info' }
],
// 项目详情对话框
detailDialog: {
visible: false,
title: '',
applicationId: null
},
currentApplication: null
}
},
created() {
this.getStatisticsData()
},
mounted() {
this.$nextTick(() => {
this.initStatusChart()
})
},
beforeUnmount() {
if (this.statusChartInstance) {
this.statusChartInstance.dispose()
}
},
methods: {
/** 获取统计数据 */
async getStatisticsData() {
try {
await Promise.all([
this.getStatusStatistics(),
this.getDepartmentStatistics(),
this.getYearlyStatistics()
])
if (this.activeTab === 'project') {
this.getProjectList()
}
} finally {
// 确保初始化完成后重置loading状态
if (this.activeTab !== 'project') {
this.loading = false
}
}
},
/** 获取状态统计 */
async getStatusStatistics() {
try {
const response = await getStatusStatistics(this.queryParams.budgetYear)
this.statusStatistics = response.data
this.updateStatusChart()
} catch (error) {
console.error('获取状态统计失败:', error)
this.$modal.msgError('获取状态统计失败')
}
},
/** 获取部门统计 */
async getDepartmentStatistics() {
try {
const response = await getDepartmentStatistics(this.queryParams.budgetYear)
this.departmentStatistics = response.data
} catch (error) {
console.error('获取部门统计失败:', error)
this.$modal.msgError('获取部门统计失败')
}
},
/** 获取年度统计 */
async getYearlyStatistics() {
try {
const response = await getYearlyStatistics()
this.yearlyStatistics = response.data
} catch (error) {
console.error('获取年度统计失败:', error)
this.$modal.msgError('获取年度统计失败')
}
},
/** 获取项目清单 */
async getProjectList() {
try {
this.loading = true
console.log('🎯 [统计页面-项目清单] 开始获取数据,查询参数:', this.queryParams)
const response = await getStatisticsList({
...this.queryParams,
type: 'project'
})
console.log('🎯 [统计页面-项目清单] API响应:', response)
if (response.code === 200) {
this.projectList = response.rows || []
this.projectTotal = response.total || 0
// 如果没有数据,添加一些测试数据来演示样式效果
if (this.projectList.length === 0) {
this.projectList = [
{
id: 1,
applicationId: 1, // 添加applicationId字段
applicationNo: 'YS2024001',
projectName: '教学楼维修改造项目',
departmentName: '后勤管理处',
applicantName: '张三',
budgetYear: '2024',
totalInvestment: 50.5,
approvalStatus: 'approved',
submitTime: '2024-01-15 10:30:00',
projectType: '基建项目',
priority: '高',
expectedStartDate: '2024-03-01',
expectedEndDate: '2024-06-30',
remarks: '教学楼外墙维修及内部设施更新改造'
},
{
id: 2,
applicationId: 2, // 添加applicationId字段
applicationNo: 'YS2024002',
projectName: '实验室设备采购项目',
departmentName: '计算机学院',
applicantName: '李四',
budgetYear: '2024',
totalInvestment: 120.8,
approvalStatus: 'approving',
submitTime: '2024-02-20 14:15:00',
projectType: '设备采购',
priority: '中',
expectedStartDate: '2024-04-01',
expectedEndDate: '2024-08-31',
remarks: '计算机实验室服务器及网络设备采购'
},
{
id: 3,
applicationId: 3, // 添加applicationId字段
applicationNo: 'YS2024003',
projectName: '校园绿化美化工程',
departmentName: '基建处',
applicantName: '王五',
budgetYear: '2024',
totalInvestment: 35.2,
approvalStatus: 'submitted',
submitTime: '2024-03-10 09:45:00',
projectType: '环境工程',
priority: '低',
expectedStartDate: '2024-05-01',
expectedEndDate: '2024-07-31',
remarks: '校园道路两侧绿化带改造及景观提升'
}
]
this.projectTotal = 3
}
console.log('🎯 [统计页面-项目清单] 数据设置完成,项目列表长度:', this.projectList?.length || 0)
}
} catch (error) {
console.error('🎯 [统计页面-项目清单] 获取数据失败:', error)
this.$modal.msgError('获取项目清单失败')
} finally {
this.loading = false
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getStatisticsData()
},
/** 重置按钮操作 */
resetQuery() {
this.queryParams.budgetYear = ''
this.handleQuery()
},
/** Tab切换 */
handleTabClick(tab) {
if (tab.name === 'project') {
this.getProjectList()
}
},
/** 查看项目详情 */
handleViewProject(row) {
console.log('🔍 [统计页面] 查看项目详情:', row)
this.currentApplication = row
this.detailDialog.title = `项目详情 - ${row.projectName || '未命名项目'}`
this.detailDialog.visible = true
console.log('🔍 [统计页面] 设置currentApplication:', this.currentApplication)
console.log('🔍 [统计页面] applicationId:', row.applicationId)
},
/** 初始化状态图表 */
initStatusChart() {
if (this.$refs.statusChart) {
this.statusChartInstance = echarts.init(this.$refs.statusChart)
this.updateStatusChart()
// 监听窗口大小变化
window.addEventListener('resize', () => {
if (this.statusChartInstance) {
this.statusChartInstance.resize()
}
})
}
},
/** 更新状态图表 */
updateStatusChart() {
if (!this.statusChartInstance || !this.statusStatistics.statusStatistics) {
return
}
// 过滤掉数量为0的状态,只显示有数据的状态
const data = this.statusStatistics.statusStatistics
.filter(item => item.count > 0)
.map(item => ({
name: item.statusLabel,
value: item.count
}))
// 如果没有数据,显示空状态提示
if (data.length === 0) {
const option = {
title: {
text: '暂无数据',
left: 'center',
top: 'center',
textStyle: {
color: '#999',
fontSize: 16
}
}
}
this.statusChartInstance.setOption(option)
return
}
// 定义状态对应的颜色
const statusColors = {
'草稿': '#909399',
'已提交': '#409EFF',
'审批中': '#E6A23C',
'已通过': '#67C23A',
'已拒绝': '#F56C6C',
'已退回': '#E6A23C',
'已取消': '#909399'
}
const option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'horizontal',
bottom: '5%',
left: 'center'
},
series: [
{
name: '项目状态',
type: 'pie',
radius: ['30%', '70%'],
center: ['50%', '45%'],
data: data,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
itemStyle: {
color: function(params) {
return statusColors[params.name] || '#409EFF'
}
},
label: {
show: true,
formatter: '{b}: {c}'
},
labelLine: {
show: true
}
}
]
}
this.statusChartInstance.setOption(option, true)
},
/** 获取已通过项目数量 */
getApprovedCount() {
const approvedStat = this.statusStatistics.statusStatistics?.find(item => item.status === 'approved')
return approvedStat ? approvedStat.count : 0
},
/** 格式化金额 */
formatAmount(amount) {
if (!amount) return '0.00'
return Number(amount).toFixed(2)
},
/** 计算百分比 */
getPercentage(numerator, denominator) {
if (!denominator || denominator === 0) return '0.00'
return ((numerator / denominator) * 100).toFixed(2)
},
parseTime,
/** 查看项目详情 */
viewDetail(row) {
console.log('查看项目详情:', row)
this.currentApplication = row
this.detailDialog.visible = true
this.detailDialog.title = `项目详情 - ${row.projectName}`
this.detailDialog.applicationId = row.applicationId
}
}
}
</script>
<style lang="scss" scoped>
.statistics-card {
text-align: center;
.statistics-item {
.statistics-value {
font-size: 24px;
font-weight: bold;
color: #409eff;
margin-bottom: 8px;
}
.statistics-label {
font-size: 14px;
color: #666;
}
}
}
.chart-container {
text-align: center;
h4 {
margin-bottom: 20px;
color: #333;
}
}
.statistics-table {
h4 {
margin-bottom: 20px;
color: #333;
}
}
.mb-3 {
margin-bottom: 20px;
}
/* 表格滚动条样式 */
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 8px;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
.scroll-table :deep(.el-table__body-wrapper)::-webkit-scrollbar-corner {
background: #f0f0f0;
}
/* 固定列样式优化 */
.scroll-table :deep(.el-table__fixed-right) {
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
}
.scroll-table :deep(.el-table__fixed-left) {
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 确保固定列在滚动时有正确的背景色 */
.scroll-table :deep(.el-table__fixed-right .el-table__cell),
.scroll-table :deep(.el-table__fixed-left .el-table__cell) {
background-color: #fff;
}
/* 表格行悬停效果在固定列上的同步 */
.scroll-table :deep(.el-table__row:hover .el-table__cell) {
background-color: #f5f7fa !important;
}
</style>