diff --git a/dist.zip b/dist.zip index f87c75b..37c5042 100644 Binary files a/dist.zip and b/dist.zip differ diff --git a/src/api/business/cost/firm-reports-api.js b/src/api/business/cost/firm-reports-api.js index 8d4e638..8d87758 100644 --- a/src/api/business/cost/firm-reports-api.js +++ b/src/api/business/cost/firm-reports-api.js @@ -52,6 +52,20 @@ export const firmReportsApi = { return postRequest('/firmReports/commit', idList); }, + /** + * 查询有无填报 @author wzh + */ + query: () => { + return getRequest('/firmReports/query'); + }, + + /** + * 查询本年度已经提交的收入 @author wzh + */ + income: () => { + return postRequest('/firmReports/income'); + }, + /** * 审核 @author wzh */ diff --git a/src/constants/business/erp/cost-const.js b/src/constants/business/erp/cost-const.js index f9e9abf..91f6830 100644 --- a/src/constants/business/erp/cost-const.js +++ b/src/constants/business/erp/cost-const.js @@ -1,7 +1,6 @@ /* * 成本管理常量 */ - // 季度枚举 export const QUARTER_ENUM = { Q1: { @@ -20,6 +19,57 @@ export const QUARTER_ENUM = { value: 'Q4', desc: '第四季度', }, +} +// 月份枚举 +export const MONTH_ENUM = { + M1: { + value: '1月', + desc: '1月', + }, + M2: { + value: '2月', + desc: '2月', + }, + M3: { + value: '3月', + desc: '3月', + }, + M4: { + value: '4月', + desc: '4月', + }, + M5: { + value: '5月', + desc: '5月', + }, + M6: { + value: '6月', + desc: '6月', + }, + M7: { + value: '7月', + desc: '7月', + }, + M8: { + value: '8月', + desc: '8月', + }, + M9: { + value: '9月', + desc: '9月', + }, + M10: { + value: '10月', + desc: '10月', + }, + M11: { + value: '11月', + desc: '11月', + }, + M12: { + value: '12月', + desc: '12月', + }, }; // 审核状态枚举 diff --git a/src/views/business/erp/cost/firm-reports-form.vue b/src/views/business/erp/cost/firm-reports-form.vue index 3d5fae8..9946589 100644 --- a/src/views/business/erp/cost/firm-reports-form.vue +++ b/src/views/business/erp/cost/firm-reports-form.vue @@ -28,16 +28,28 @@ :max="2100" /> - - + + > + + {{ month.label }} + + + + + + 本年度已填报收入:{{ annualIncomeInfo.revenue || 0 }} 万元 + ,合计:{{ formatNumber((annualIncomeInfo.revenue || 0) + (form.revenue || 0)) }} 万元 + @@ -116,7 +132,6 @@ import { serviceApplicationsApi } from '/@/api/business/service-applications/service-applications-api'; import { smartSentry } from '/@/lib/smart-sentry'; import { useUserStore } from '/@/store/modules/system/user'; - import DictSelect from '/@/components/support/dict-select/index.vue'; // ------------------------ 事件 ------------------------ @@ -134,10 +149,72 @@ // ------------------------ 显示与隐藏 ------------------------ // 是否显示 const visibleFlag = ref(false); + + // 本年度已填报收入信息 + const annualIncomeInfo = ref({ + revenue: 0, // 营业收入 + totalCost: 0 // 总成本支出 + }); + + // 月份选项(只能填报上一个月) + const monthOptions = ref([]); + + // 初始化月份选项,只能选择上一个月 + async function initMonthOptions() { + const currentDate = new Date(); + const currentYear = currentDate.getFullYear(); + const currentMonth = currentDate.getMonth() + 1; // 当前月份(1-12) + + // 计算上一个月 + let lastMonth = currentMonth - 1; + let lastMonthYear = currentYear; + + if (lastMonth === 0) { + lastMonth = 12; + lastMonthYear = currentYear - 1; + } + + // 设置默认年份为上一个月所在的年份 + form.declareYear = lastMonthYear; + + // 只能选择上一个月 + monthOptions.value = [ + { label: `${lastMonth}月`, value: `${lastMonth}月` } + ]; + + // 默认选中上一个月 + form.declareMonth = `${lastMonth}月`; + + // 设置默认月份后,立即获取公益成本数据 + await getPublicWelfareCost(); + } + + // 格式化数字 + function formatNumber(value) { + if (value === null || value === undefined) return '-'; + const num = Number(value); + return isNaN(num) ? '-' : num.toFixed(2); + } + + // 获取本年度已填报收入 + async function fetchAnnualIncome() { + try { + const result = await firmReportsApi.income(); + if (result.data) { + annualIncomeInfo.value = { + revenue: result.data.revenue || 0, + totalCost: result.data.totalCost || 0 + }; + } + } catch (error) { + console.error('获取本年度已填报收入失败:', error); + annualIncomeInfo.value = { revenue: 0, totalCost: 0 }; + } + } // 获取系统计算的公益成本 async function getPublicWelfareCost() { - console.log('开始获取公益成本,部门ID:', departmentId.value, '年份:', form.declareYear, '季度:', form.declareQuarter); + console.log('开始获取公益成本,部门ID:', departmentId.value, '年份:', form.declareYear, '月份:', form.declareMonth); if (!departmentId.value) { message.warning('无法获取机构信息,请重新登录'); @@ -150,20 +227,28 @@ return; } - if (!form.declareQuarter) { - console.log('季度未选择,跳过API调用'); + if (!form.declareMonth) { + console.log('月份未选择,跳过API调用'); return; } try { - // 将季度字符串转换为数字(Q1 -> 1, Q2 -> 2, Q3 -> 3, Q4 -> 4) - const quarterNumber = form.declareQuarter.replace('Q', ''); + // 将月度字符串转换为数字(如"1月" -> 1, "2月" -> 2, ..., "12月" -> 12) + let monthNumber; + if (typeof form.declareMonth === 'string') { + const monthMatch = form.declareMonth.match(/^(\d+)/); + if (monthMatch && monthMatch[1]) { + monthNumber = parseInt(monthMatch[1]); + } + } else if (typeof form.declareMonth === 'number') { + monthNumber = form.declareMonth; + } - // 构建请求参数:包含部门ID、年份和季度数字 + // 构建请求参数:包含部门ID、年份和月度数字 const queryForm = { firmId: departmentId.value, year: form.declareYear, // 后端期望的year字段 - quarter: parseInt(quarterNumber) // 季度字段转换为数字 + month: monthNumber // 月度字段转换为数字 }; console.log('调用接口: /serviceApplications/statistics/cost, 参数:', queryForm); @@ -198,20 +283,23 @@ console.log('编辑模式,使用现有数据'); Object.assign(form, rowData); - // 确保declareQuarter字段格式与字典系统匹配 - if (form.declareQuarter && typeof form.declareQuarter === 'number') { + // 确保declareMonth字段格式与字典系统匹配 + if (form.declareMonth && typeof form.declareMonth === 'number') { // 如果后端返回的是数字格式,转换为字典期望的字符串格式 - form.declareQuarter = 'Q' + form.declareQuarter; - } else if (form.declareQuarter && form.declareQuarter.includes('(')) { - // 如果返回的是显示名称格式(如"第一季度 (Q1)"),提取字典值 - const match = form.declareQuarter.match(/\(([^)]+)\)/); + form.declareMonth = form.declareMonth + '月'; + } else if (form.declareMonth && form.declareMonth.includes('(')) { + // 如果返回的是显示名称格式(如"1月 (1)"),提取字典值 + const match = form.declareMonth.match(/\(([^)]+)\)/); if (match && match[1]) { - form.declareQuarter = match[1]; // 提取Q1这样的字典值 + form.declareMonth = match[1] + '月'; // 提取数字并添加"月"后缀 } } } else { - console.log('新建模式,等待用户选择季度后获取公益成本'); - // 新建时不立即获取公益成本,等待用户选择季度 + console.log('新建模式,等待用户选择月份后获取公益成本'); + // 新建模式下初始化月份选项(只能选择上一个月) + initMonthOptions(); + // 新建模式下获取本年度已填报收入 + fetchAnnualIncome(); } visibleFlag.value = true; @@ -236,7 +324,7 @@ id: undefined, firmId: undefined, // 律师事务所ID(部门ID) declareYear: new Date().getFullYear(), // 报表年份 - declareQuarter: undefined, // 季度ID (1,2,3,4) + declareMonth: undefined, // 月度ID (1,2,3,4,5,6,7,8,9,10,11,12) revenue: undefined, // 营业收入(万元) //totalCost: 0, // 总成本支出(万元) publicWelfareCost: undefined, // 公益成本支出(万元) @@ -246,7 +334,7 @@ let form = reactive({ ...formDefault }); const rules = { - declareQuarter: [{ required: true, message: '请选择季度' }], + declareMonth: [{ required: true, message: '请选择月份' }], declareYear: [{ required: true, message: '请输入报表年份' }], revenue: [{ required: true, message: '请输入律所收入' }], publicWelfareCost: [{ required: true, message: '系统正在计算公益成本,请稍后' }], @@ -257,12 +345,12 @@ const showCostWarning = ref(false); const costRatio = ref(0); // 当前成本比例 - // 季度选择变化时的处理 - function onQuarterChange() { - console.log('季度选择变化:', form.declareQuarter); + // 月份选择变化时的处理 + function onMonthChange() { + console.log('月份选择变化:', form.declareMonth); - // 只有当选择了季度时才获取公益成本 - if (form.declareQuarter) { + // 只有当选择了月份时才获取公益成本 + if (form.declareMonth) { getPublicWelfareCost(); } @@ -272,12 +360,21 @@ // 自动计算总成本和成本/收入比例 function calculateCosts() { - const revenue = parseFloat(form.revenue) || 0; - const publicWelfareCost = parseFloat(form.publicWelfareCost) || 0; + // 安全转换数值,防止NaN + const currentRevenue = safeParseFloat(form.revenue); + const publicWelfareCost = safeParseFloat(form.publicWelfareCost); + const annualRevenue = safeParseFloat(annualIncomeInfo.value.revenue); + const annualTotalCost = safeParseFloat(annualIncomeInfo.value.totalCost); + + // 计算总收入:当前填写的收入 + 本年度已填报收入 + const totalRevenue = currentRevenue + annualRevenue; - // 计算当前成本比例 - if (revenue > 0) { - costRatio.value = (publicWelfareCost / revenue) * 100; + // 计算年度累计成本:当前填报成本 + 本年度已填报成本 + const totalCost = publicWelfareCost + annualTotalCost; + + // 计算年度成本比例 + if (totalRevenue > 0) { + costRatio.value = (totalCost / totalRevenue) * 100; // 检查是否达到或超过20%阈值 if (costRatio.value >= 20) { @@ -285,21 +382,25 @@ // 如果超过25%上限,按25%计算 if (costRatio.value > 25) { - const maxAllowedCost = revenue * 0.25; // 25%上限 - calculatedPublicWelfareCost.value = maxAllowedCost.toFixed(2); + const maxAllowedAnnualCost = totalRevenue * 0.25; // 年度成本上限 + const remainingAllowedCost = Math.max(0, maxAllowedAnnualCost - annualTotalCost); + + // 当前填报允许的最大成本 + calculatedPublicWelfareCost.value = Math.min(publicWelfareCost, remainingAllowedCost).toFixed(2); // 计算成本/收入比例(使用上限值) - const ratio = (maxAllowedCost / revenue) * 100; + const actualAnnualCost = annualTotalCost + parseFloat(calculatedPublicWelfareCost.value); + const ratio = (actualAnnualCost / totalRevenue) * 100; form.costIncomeRatio = ratio.toFixed(2); } else { // 达到20%但未超过25%,使用实际值 - calculatedPublicWelfareCost.value = publicWelfareCost; + calculatedPublicWelfareCost.value = publicWelfareCost.toFixed(2); form.costIncomeRatio = costRatio.value.toFixed(2); } } else { // 未达到20%,隐藏警告 showCostWarning.value = false; - calculatedPublicWelfareCost.value = publicWelfareCost; + calculatedPublicWelfareCost.value = publicWelfareCost.toFixed(2); form.costIncomeRatio = costRatio.value.toFixed(2); } } else { @@ -307,18 +408,24 @@ showCostWarning.value = false; costRatio.value = 0; form.costIncomeRatio = '0.00'; - calculatedPublicWelfareCost.value = publicWelfareCost; + calculatedPublicWelfareCost.value = publicWelfareCost.toFixed(2); } - // 计算总成本(只包含公益活动成本) - const totalCost = showCostWarning.value ? parseFloat(calculatedPublicWelfareCost.value) : publicWelfareCost; - form.totalCost = totalCost; + // 设置总成本 + form.totalCost = showCostWarning.value ? parseFloat(calculatedPublicWelfareCost.value) : publicWelfareCost; + } + + // 安全的数值转换函数 + function safeParseFloat(value) { + if (value === null || value === undefined || value === '') return 0; + const num = parseFloat(value); + return isNaN(num) ? 0 : num; } // 保存草稿 async function saveAsDraft() { try { - await formRef.value.validateFields(['declareQuarter', 'declareYear', 'revenue', 'publicWelfareCost']); + await formRef.value.validateFields(['declareMonth', 'declareYear', 'revenue', 'publicWelfareCost']); form.approvalStatus = '0'; // 草稿状态 await save(); } catch (err) { @@ -348,6 +455,19 @@ // 准备保存的数据,如果超过上限则使用计算值 const saveData = { ...form }; + // 将月份格式转换为数字格式(如"1月" -> 1, "2月" -> 2) + if (saveData.declareMonth && typeof saveData.declareMonth === 'string') { + const monthMatch = saveData.declareMonth.match(/^(\d+)/); + if (monthMatch && monthMatch[1]) { + saveData.declareMonth = parseInt(monthMatch[1]); + } + } + + // 确保年份是整数类型 + if (saveData.declareYear) { + saveData.declareYear = parseInt(saveData.declareYear); + } + if (showCostWarning.value) { // 使用计算后的公益成本值 saveData.publicWelfareCost = parseFloat(calculatedPublicWelfareCost.value); diff --git a/src/views/business/erp/cost/firm-reports-list.vue b/src/views/business/erp/cost/firm-reports-list.vue index 1eaf647..abfde60 100644 --- a/src/views/business/erp/cost/firm-reports-list.vue +++ b/src/views/business/erp/cost/firm-reports-list.vue @@ -13,16 +13,19 @@ - + - - + + > + + {{ month.label }} + + @@ -46,12 +49,12 @@ - + @@ -121,7 +124,6 @@ import FirmReportsForm from './firm-reports-form.vue'; import { REVIEW_STATUS_ENUM } from '/@/constants/business/erp/cost-const'; import DepartmentTreeSelect from '/@/components/system/department-tree-select/index.vue'; - import DictSelect from '/@/components/support/dict-select/index.vue'; import { loginApi } from '/@/api/system/login-api'; // ---------------------------- 用户信息和角色 ---------------------------- @@ -157,13 +159,13 @@ ellipsis: true, }, { - title: '报表年份', + title: '年份', dataIndex: 'declareYear', ellipsis: true, }, { - title: '报表季度', - dataIndex: 'declareQuarter', + title: '月份', + dataIndex: 'declareMonth', ellipsis: true, }, { @@ -223,8 +225,24 @@ pageSize: 10, firmId: undefined, // 律师事务所ID declareYear: undefined, // 报表年份 - declareQuarter: undefined, // 报表季度 + declareMonth: undefined, // 报表月份 }; + // 月份选项 + const monthOptions = ref([ + { label: '1月', value: '1月' }, + { label: '2月', value: '2月' }, + { label: '3月', value: '3月' }, + { label: '4月', value: '4月' }, + { label: '5月', value: '5月' }, + { label: '6月', value: '6月' }, + { label: '7月', value: '7月' }, + { label: '8月', value: '8月' }, + { label: '9月', value: '9月' }, + { label: '10月', value: '10月' }, + { label: '11月', value: '11月' }, + { label: '12月', value: '12月' } + ]); + // 查询表单form const queryForm = reactive({ ...queryFormState }); // 表格加载loading @@ -252,7 +270,18 @@ async function queryData() { tableLoading.value = true; try { - let queryResult = await firmReportsApi.queryPage(queryForm); + // 准备查询参数,将月份格式转换为数字 + const queryParams = { ...queryForm }; + + // 将月份格式转换为数字(如"2月" -> 2) + if (queryParams.declareMonth && typeof queryParams.declareMonth === 'string') { + const monthMatch = queryParams.declareMonth.match(/^(\d+)/); + if (monthMatch && monthMatch[1]) { + queryParams.declareMonth = parseInt(monthMatch[1]); + } + } + + let queryResult = await firmReportsApi.queryPage(queryParams); tableData.value = queryResult.data.list; total.value = queryResult.data.total; } catch (e) { diff --git a/src/views/business/erp/service/service-applications-list.vue b/src/views/business/erp/service/service-applications-list.vue index caa8ee4..4ab3d30 100644 --- a/src/views/business/erp/service/service-applications-list.vue +++ b/src/views/business/erp/service/service-applications-list.vue @@ -57,7 +57,7 @@ - + @@ -90,13 +90,13 @@ 新建申报 --> - + - + + @@ -309,10 +310,12 @@ import TableOperator from '/@/components/support/table-operator/index.vue'; import ServiceApplicationsForm from './service-applications-form.vue'; import DepartmentTreeSelect from '/@/components/system/department-tree-select/index.vue'; import PositionSelect from '/@/components/system/position-select/index.vue'; +import FirmReportsForm from '/@/views/business/erp/cost/firm-reports-form.vue'; import { employeeApi } from '/@/api/system/employee-api'; import { REVIEW_ENUM, SERVICEC_REVIEW_ENUM} from '/@/constants/system/review-const'; import { PlusOutlined, DeleteOutlined, SendOutlined, ImportOutlined, ExportOutlined, DownloadOutlined, UploadOutlined, CheckCircleOutlined } from '@ant-design/icons-vue'; import { loginApi } from '/@/api/system/login-api'; +import { firmReportsApi } from '/@/api/business/cost/firm-reports-api'; import AgreementModal from '/@/views/system/home/components/agreement-modal.vue'; // ---------------------------- 表格列 ---------------------------- @@ -357,6 +360,7 @@ import AgreementModal from '/@/views/system/home/components/agreement-modal.vue' dataIndex: 'beneficiaryCount', ellipsis: true, }, + { title: '组织单位名称', dataIndex: 'organizerName', @@ -372,6 +376,12 @@ import AgreementModal from '/@/views/system/home/components/agreement-modal.vue' dataIndex: 'organizerPhone', ellipsis: true, }, + { + title: '提交时间', + dataIndex: 'reportTime', + ellipsis: true, + width: 150, + }, { title: '执业机构审核状态', dataIndex: 'firmAuditStatus', @@ -638,7 +648,7 @@ import AgreementModal from '/@/views/system/home/components/agreement-modal.vue' } // 批量上报 - function batchReport() { + async function batchReport() { if (selectedRowKeyList.value.length === 0) { message.warning('请选择要上报的记录'); return; @@ -650,13 +660,33 @@ import AgreementModal from '/@/views/system/home/components/agreement-modal.vue' return; } + // 如果已签署律所承诺书,检查成本权限 + if (loginInfo.value && loginInfo.value.lawFirmAgreementSignFlag === true) { + // 检查是否有成本查询权限 + if (loginInfo.value.costVisibleFlag === true) { + try { + const costPermissionResult = await firmReportsApi.query(); + if (costPermissionResult.data === false) { + // 成本权限检查返回false,需要先填报成本报表 + message.warning('您需要先填报和提交律所收入,才能进行服务上报。'); + // 调用成本管理新增弹框 + showCostReportForm(); + return; + } + } catch (error) { + console.error('检查成本权限失败:', error); + } + } + // 如果没有成本查询权限,直接跳过成本检查 + } + // 检查选中的记录是否符合上报条件 const canReportRecords = tableData.value.filter(record => selectedRowKeyList.value.includes(record.applicationId) && canReportRecord(record) ); if (canReportRecords.length === 0) { - message.warning('选中的记录不符合上报条件'); + message.warning('选中的记录不符合上报条件,请勿提交其它月份数据'); return; } @@ -674,8 +704,46 @@ import AgreementModal from '/@/views/system/home/components/agreement-modal.vue' function canReportRecord(record) { // 只能上报已通过执业机构审核的记录(状态为3:已通过) // 且协会审核状态必须是未提交(0) - return record.firmAuditStatus === 3 && - record.associationAuditStatus === 0; + const isAuditPassed = record.firmAuditStatus === 3 && + record.associationAuditStatus === 0; + + // 如果有成本查询权限,需要检查月份限制;否则不限制月份 + const hasCostPermission = loginInfo.value && loginInfo.value.costVisibleFlag === true; + + if (hasCostPermission) { + // 有成本权限:只能上报上个月的数据 + const isLastMonthData = isLastMonthRecord(record); + return isAuditPassed && isLastMonthData; + } else { + // 无成本权限:不限制月份,可以上报所有月份的数据 + return isAuditPassed; + } + } + + // 判断记录是否为上个月的数据 + function isLastMonthRecord(record) { + if (!record.reportTime) return false; + + const currentDate = new Date(); + const currentYear = currentDate.getFullYear(); + const currentMonth = currentDate.getMonth() + 1; + + // 计算上一个月 + let lastMonth = currentMonth - 1; + let lastMonthYear = currentYear; + + if (lastMonth === 0) { + lastMonth = 12; + lastMonthYear = currentYear - 1; + } + + // 解析记录的提交时间 + const reportDate = new Date(record.reportTime); + const reportYear = reportDate.getFullYear(); + const reportMonth = reportDate.getMonth() + 1; + + // 检查是否为上个月的数据 + return reportYear === lastMonthYear && reportMonth === lastMonth; } // 执行批量上报 @@ -839,6 +907,7 @@ onMounted(async () => { // ---------------------------- 添加/修改 ---------------------------- const formRef = ref(); + const costReportFormRef = ref(); function showForm(data) { // 检查用户是否已签约 @@ -851,6 +920,12 @@ onMounted(async () => { } } + // ---------------------------- 成本报表 ---------------------------- + function showCostReportForm() { + // 打开成本报表新增弹框 + costReportFormRef.value.show(); + } + // ---------------------------- 详情 ---------------------------- async function showDetail(record) { try { diff --git a/src/views/business/erp/service/service-applications-report-list.vue b/src/views/business/erp/service/service-applications-report-list.vue index c71ce5b..54f003d 100644 --- a/src/views/business/erp/service/service-applications-report-list.vue +++ b/src/views/business/erp/service/service-applications-report-list.vue @@ -33,7 +33,7 @@ - + @@ -313,6 +313,12 @@ import AgreementModal from '/@/views/system/home/components/agreement-modal.vue' dataIndex: 'organizerPhone', ellipsis: true, }, + { + title: '提交时间', + dataIndex: 'reportTime', + ellipsis: true, + width: 150, + }, { title: '执业机构审核状态', dataIndex: 'firmAuditStatus',