Browse Source

feat:代码

master
wang 1 month ago
parent
commit
4d62db1d16
  1. BIN
      dist.zip
  2. 14
      src/api/business/cost/firm-reports-api.js
  3. 101
      src/views/business/erp/cost/firm-reports-list.vue
  4. 4
      src/views/business/erp/service/law-firm-statistics.vue
  5. 26
      src/views/business/erp/service/lawyer-service-report-statistics.vue
  6. 18
      src/views/business/erp/service/service-applications-list.vue
  7. 18
      src/views/business/erp/service/service-applications-report-list.vue
  8. 4
      src/views/mobile/service/detail.vue
  9. 12
      vite.config.js

BIN
dist.zip

Binary file not shown.

14
src/api/business/cost/firm-reports-api.js

@ -80,4 +80,18 @@ export const firmReportsApi = {
return postRequest('/firmReports/reject', idList); return postRequest('/firmReports/reject', idList);
}, },
/**
* 提交前预检查 @author wzh
*/
preCommitCheck: (idList) => {
return postRequest('/firmReports/preCommitCheck', idList);
},
/**
* 批量创建缺失季度记录 @author wzh
*/
batchCreateMissing: (param) => {
return postRequest('/firmReports/batchCreateMissing', param);
},
}; };

101
src/views/business/erp/cost/firm-reports-list.vue

@ -119,7 +119,32 @@
<FirmReportsForm ref="formRef" @reloadList="queryData"/> <FirmReportsForm ref="formRef" @reloadList="queryData"/>
<!-- 缺失季度选择弹窗 -->
<a-modal
v-model:open="quarterModalVisible"
title="检测到未提交的季度数据"
:width="420"
:maskClosable="false"
@ok="handleCreateQuarters"
okText="创建选中的季度"
cancelText="取消"
>
<div style="padding: 8px 0;">
<p style="margin-bottom: 10px; color: #333; font-weight: 500;">
提交{{ quarterYear }}年第{{ quarterCurrentQuarter }}季度前以下季度成本报表尚未提交
</p>
<div style="background: #fafafa; border: 1px solid #f0f0f0; padding: 12px; border-radius: 6px; margin-bottom: 10px;">
<div v-for="(q, idx) in quarterMissingList" :key="idx"
style="padding: 6px 0; display: flex; align-items: center;"
:style="{ borderBottom: idx < quarterMissingList.length - 1 ? '1px dashed #e8e8e8' : 'none' }">
<a-checkbox v-model:checked="q.checked" />
<span style="margin-left: 8px; font-size: 14px;">{{ q.label }}</span>
<span style="margin-left: auto; color: #999; font-size: 12px;">空白报表</span>
</div>
</div>
<p style="color: #999; font-size: 12px;">勾选需要创建的季度取消勾选则跳过创建后请逐季度填写收入后提交</p>
</div>
</a-modal>
</a-card> </a-card>
</template> </template>
@ -314,6 +339,46 @@
} }
} }
// ---------------------------- ----------------------------
const quarterModalVisible = ref(false);
const quarterYear = ref(null);
const quarterCurrentQuarter = ref(null);
const quarterFirmId = ref(null);
const quarterMissingList = ref([]);
const pendingSubmitRecord = ref(null);
async function handleCreateQuarters() {
const checkedItems = quarterMissingList.value.filter(q => q.checked);
if (checkedItems.length === 0) {
message.warning('请至少勾选一个需要创建的季度');
return;
}
try {
SmartLoading.show();
const yearGroups = {};
checkedItems.forEach(q => {
if (!yearGroups[q.year]) {
yearGroups[q.year] = [];
}
yearGroups[q.year].push(q.quarterNum);
});
for (const [yr, quarters] of Object.entries(yearGroups)) {
await firmReportsApi.batchCreateMissing({
firmId: quarterFirmId.value,
year: parseInt(yr),
quarters: quarters,
});
}
message.success(`成功创建 ${checkedItems.length} 个季度的空白报表`);
quarterModalVisible.value = false;
queryData();
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
// ---------------------------- ---------------------------- // ---------------------------- ----------------------------
// //
function onSubmit(data){ function onSubmit(data){
@ -332,8 +397,6 @@
// //
async function requestSubmit(data){ async function requestSubmit(data){
//
// staffcto
if (data.approvalStatus !== 0) { if (data.approvalStatus !== 0) {
message.warning('只能提交未提交状态的数据'); message.warning('只能提交未提交状态的数据');
return; return;
@ -341,7 +404,32 @@
try { try {
SmartLoading.show(); SmartLoading.show();
// idListID
const checkResult = await firmReportsApi.preCommitCheck([data.id]);
if (checkResult.data && checkResult.data.hasMissing) {
SmartLoading.hide();
const missingQuarters = checkResult.data.missingQuarters || [];
const firmId = checkResult.data.firmId;
const year = checkResult.data.year;
const currentQuarter = checkResult.data.currentQuarter;
quarterYear.value = year;
quarterCurrentQuarter.value = currentQuarter;
quarterFirmId.value = firmId;
quarterMissingList.value = missingQuarters.map((q) => {
const parsed = parseQuarterLabel(q);
return {
label: q,
checked: true,
year: parsed.year,
quarterNum: parsed.quarterNum,
};
});
pendingSubmitRecord.value = data;
quarterModalVisible.value = true;
return;
}
await firmReportsApi.commit([data.id]); await firmReportsApi.commit([data.id]);
message.success('提交成功'); message.success('提交成功');
queryData(); queryData();
@ -352,6 +440,11 @@
} }
} }
function parseQuarterLabel(label) {
const match = label.match(/(\d+)年第(\d+)季度/);
return match ? { year: parseInt(match[1]), quarterNum: parseInt(match[2]) } : null;
}
// ---------------------------- ---------------------------- // ---------------------------- ----------------------------
// //

4
src/views/business/erp/service/law-firm-statistics.vue

@ -258,7 +258,7 @@ async function handleApproveConfirm() {
// ID // ID
await serviceApplicationsApi.batchReviewByDepartmentId({ await serviceApplicationsApi.batchReviewByDepartmentId({
firmId: currentFirm.value.firmId, // ID firmId: currentFirm.value.firmId, // ID
associationAuditStatus: approveForm.auditResult, auditResult: approveForm.auditResult,
auditRemark: approveForm.auditRemark auditRemark: approveForm.auditRemark
}); });
@ -306,4 +306,4 @@ onMounted(() => {
.statistics-table { .statistics-table {
margin-top: 16px; margin-top: 16px;
} }
</style> </style>

26
src/views/business/erp/service/lawyer-service-report-statistics.vue

@ -58,9 +58,10 @@
<div class="custom-table-header"> <div class="custom-table-header">
<table class="header-table" border="1" cellspacing="0" cellpadding="0"> <table class="header-table" border="1" cellspacing="0" cellpadding="0">
<thead> <thead>
<!-- 第一行序号 + 律师名称 + 活动分类含合计 + 服务总次数 --> <!-- 第一行序号 + 所名称 + 师名称 + 活动分类含合计 + 服务总次数 -->
<tr> <tr>
<th rowspan="2" class="fixed-col index-col">序号</th> <th rowspan="2" class="fixed-col index-col">序号</th>
<th rowspan="2" class="fixed-col firm-name-col">律所名称</th>
<th rowspan="2" class="fixed-col fixed-left">律师名称</th> <th rowspan="2" class="fixed-col fixed-left">律师名称</th>
<template v-for="category in categoryStructure" :key="category.categoryId"> <template v-for="category in categoryStructure" :key="category.categoryId">
<!-- 如果有多个活动先添加合计列跨两行 --> <!-- 如果有多个活动先添加合计列跨两行 -->
@ -84,6 +85,7 @@
<tbody> <tbody>
<tr v-for="(record, index) in tableData" :key="record.lawyerId"> <tr v-for="(record, index) in tableData" :key="record.lawyerId">
<td class="fixed-col index-col">{{ index + 1 }}</td> <td class="fixed-col index-col">{{ index + 1 }}</td>
<td class="fixed-col firm-name-col data-firm-name">{{ record.firmName || '-' }}</td>
<td class="fixed-col fixed-left lawyer-name">{{ record.lawyerName }}</td> <td class="fixed-col fixed-left lawyer-name">{{ record.lawyerName }}</td>
<template v-for="category in categoryStructure" :key="'data_' + category.categoryId"> <template v-for="category in categoryStructure" :key="'data_' + category.categoryId">
<td v-if="category.activityList.length > 1" class="data-col subtotal-data-col"> <td v-if="category.activityList.length > 1" class="data-col subtotal-data-col">
@ -98,6 +100,7 @@
<!-- 合计行 --> <!-- 合计行 -->
<tr class="total-row" v-if="tableData.length > 0"> <tr class="total-row" v-if="tableData.length > 0">
<td class="fixed-col index-col">-</td> <td class="fixed-col index-col">-</td>
<td class="fixed-col firm-name-col">-</td>
<td class="fixed-col fixed-left">合计</td> <td class="fixed-col fixed-left">合计</td>
<template v-for="category in categoryStructure" :key="'total_' + category.categoryId"> <template v-for="category in categoryStructure" :key="'total_' + category.categoryId">
<td v-if="category.activityList.length > 1" class="data-col"> <td v-if="category.activityList.length > 1" class="data-col">
@ -355,6 +358,7 @@
const row = { const row = {
lawyerId: item.userId, lawyerId: item.userId,
lawyerName: item.lawyerName, lawyerName: item.lawyerName,
firmName: item.firmName,
totalCount: item.totalCount || 0 totalCount: item.totalCount || 0
}; };
@ -479,9 +483,19 @@
padding: 8px 2px !important; padding: 8px 2px !important;
} }
/* 左侧固定列 - 律所名称 */
.firm-name-col {
left: 40px;
width: 150px !important;
min-width: 150px !important;
max-width: 150px !important;
z-index: 3;
padding: 8px 6px !important;
}
/* 左侧固定列 - 律师名称 */ /* 左侧固定列 - 律师名称 */
.fixed-left { .fixed-left {
left: 40px; left: 190px;
} }
/* 右侧固定列 */ /* 右侧固定列 */
@ -499,6 +513,14 @@
color: #333; color: #333;
} }
.data-firm-name {
color: #555;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.total-count { .total-count {
font-weight: 700; font-weight: 700;
color: #333; color: #333;

18
src/views/business/erp/service/service-applications-list.vue

@ -346,6 +346,7 @@ import { loginApi } from '/@/api/system/login-api';
import { firmReportsApi } from '/@/api/business/cost/firm-reports-api'; import { firmReportsApi } from '/@/api/business/cost/firm-reports-api';
import AgreementModal from '/@/views/system/home/components/agreement-modal.vue'; import AgreementModal from '/@/views/system/home/components/agreement-modal.vue';
import { getRoleInfo } from '/@/utils/role-util'; import { getRoleInfo } from '/@/utils/role-util';
import dayjs from 'dayjs';
// ---------------------------- ---------------------------- // ---------------------------- ----------------------------
const columns = ref([ const columns = ref([
@ -883,21 +884,30 @@ import { getRoleInfo } from '/@/utils/role-util';
// //
if (params.firmAuditTimeRange && params.firmAuditTimeRange.length === 2) { if (params.firmAuditTimeRange && params.firmAuditTimeRange.length === 2) {
processedParams.firmAuditTimeStart = params.firmAuditTimeRange[0]; processedParams.firmAuditTimeStart = formatQueryTime(params.firmAuditTimeRange[0], 'start');
processedParams.firmAuditTimeEnd = params.firmAuditTimeRange[1]; processedParams.firmAuditTimeEnd = formatQueryTime(params.firmAuditTimeRange[1], 'end');
delete processedParams.firmAuditTimeRange; delete processedParams.firmAuditTimeRange;
} }
// //
if (params.associationAuditTimeRange && params.associationAuditTimeRange.length === 2) { if (params.associationAuditTimeRange && params.associationAuditTimeRange.length === 2) {
processedParams.associationAuditTimeStart = params.associationAuditTimeRange[0]; processedParams.associationAuditTimeStart = formatQueryTime(params.associationAuditTimeRange[0], 'start');
processedParams.associationAuditTimeEnd = params.associationAuditTimeRange[1]; processedParams.associationAuditTimeEnd = formatQueryTime(params.associationAuditTimeRange[1], 'end');
delete processedParams.associationAuditTimeRange; delete processedParams.associationAuditTimeRange;
} }
return processedParams; return processedParams;
} }
function formatQueryTime(value, boundary) {
if (!value) {
return undefined;
}
const date = dayjs(value);
const normalized = boundary === 'end' ? date.endOf('day') : date.startOf('day');
return normalized.format('YYYY-MM-DD HH:mm:ss');
}
// //
function onSearch(){ function onSearch(){
queryForm.pageNum = 1; queryForm.pageNum = 1;

18
src/views/business/erp/service/service-applications-report-list.vue

@ -263,6 +263,7 @@ import { REVIEW_ENUM,SERVICEC_REVIEW_ENUM } from '/@/constants/system/review-con
import { PlusOutlined, DeleteOutlined, SendOutlined, ImportOutlined, ExportOutlined, DownloadOutlined, UploadOutlined, CheckCircleOutlined } from '@ant-design/icons-vue'; import { PlusOutlined, DeleteOutlined, SendOutlined, ImportOutlined, ExportOutlined, DownloadOutlined, UploadOutlined, CheckCircleOutlined } from '@ant-design/icons-vue';
import { loginApi } from '/@/api/system/login-api'; import { loginApi } from '/@/api/system/login-api';
import AgreementModal from '/@/views/system/home/components/agreement-modal.vue'; import AgreementModal from '/@/views/system/home/components/agreement-modal.vue';
import dayjs from 'dayjs';
import { getRoleInfo } from '/@/utils/role-util'; import { getRoleInfo } from '/@/utils/role-util';
// ---------------------------- ---------------------------- // ---------------------------- ----------------------------
@ -652,21 +653,30 @@ import { getRoleInfo } from '/@/utils/role-util';
// //
if (params.firmAuditTimeRange && params.firmAuditTimeRange.length === 2) { if (params.firmAuditTimeRange && params.firmAuditTimeRange.length === 2) {
processedParams.firmAuditTimeStart = params.firmAuditTimeRange[0]; processedParams.firmAuditTimeStart = formatQueryTime(params.firmAuditTimeRange[0], 'start');
processedParams.firmAuditTimeEnd = params.firmAuditTimeRange[1]; processedParams.firmAuditTimeEnd = formatQueryTime(params.firmAuditTimeRange[1], 'end');
delete processedParams.firmAuditTimeRange; delete processedParams.firmAuditTimeRange;
} }
// //
if (params.associationAuditTimeRange && params.associationAuditTimeRange.length === 2) { if (params.associationAuditTimeRange && params.associationAuditTimeRange.length === 2) {
processedParams.associationAuditTimeStart = params.associationAuditTimeRange[0]; processedParams.associationAuditTimeStart = formatQueryTime(params.associationAuditTimeRange[0], 'start');
processedParams.associationAuditTimeEnd = params.associationAuditTimeRange[1]; processedParams.associationAuditTimeEnd = formatQueryTime(params.associationAuditTimeRange[1], 'end');
delete processedParams.associationAuditTimeRange; delete processedParams.associationAuditTimeRange;
} }
return processedParams; return processedParams;
} }
function formatQueryTime(value, boundary) {
if (!value) {
return undefined;
}
const date = dayjs(value);
const normalized = boundary === 'end' ? date.endOf('day') : date.startOf('day');
return normalized.format('YYYY-MM-DD HH:mm:ss');
}
// //
function onSearch(){ function onSearch(){
queryForm.pageNum = 1; queryForm.pageNum = 1;

4
src/views/mobile/service/detail.vue

@ -1024,7 +1024,7 @@ async function handleSubmit() {
status: 1 // status: 1 //
} }
const res = await serviceApplicationsApi.submit(form.applicationId) const res = await serviceApplicationsApi.submit(submitData)
if (res.code === 0) { if (res.code === 0) {
message.success('申报提交成功') message.success('申报提交成功')
@ -1313,4 +1313,4 @@ onMounted(() => {
padding: 12px; padding: 12px;
} }
} }
</style> </style>

12
vite.config.js

@ -35,20 +35,20 @@ export default {
proxy: { proxy: {
// 代理API路径 // 代理API路径
'/api': { '/api': {
target: 'http://8.148.67.92:8080/', // 目标服务器地址 //target: 'http://8.148.67.92:8080/', // 目标服务器地址
//target: 'http://127.0.0.1:8080/', target: 'http://127.0.0.1:8080/',
changeOrigin: true, // 是否修改请求头中的 Origin 字段 changeOrigin: true, // 是否修改请求头中的 Origin 字段
rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径 rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径
}, },
'/login': { '/login': {
target: 'http://8.148.67.92:8080/', // 目标服务器地址 //target: 'http://8.148.67.92:8080/', // 目标服务器地址
//target: 'http://127.0.0.1:8080/', target: 'http://127.0.0.1:8080/',
changeOrigin: true, // 是否修改请求头中的 Origin 字段 changeOrigin: true, // 是否修改请求头中的 Origin 字段
rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径 rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径
}, },
'/mobile/login': { '/mobile/login': {
target: 'http://8.148.67.92:8080/', // 目标服务器地址 //target: 'http://8.148.67.92:8080/', // 目标服务器地址
//target: 'http://127.0.0.1:8080/', target: 'http://127.0.0.1:8080/',
changeOrigin: true, // 是否修改请求头中的 Origin 字段 changeOrigin: true, // 是否修改请求头中的 Origin 字段
rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径 rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径
}, },

Loading…
Cancel
Save