Browse Source

fix:修改问题

master
“wangzihua” 3 months ago
parent
commit
2449c2d430
  1. 703
      src/views/business/erp/service/ceo-service-detail.vue
  2. 217
      src/views/business/erp/service/law-firm-statistics.vue

703
src/views/business/erp/service/ceo-service-detail.vue

@ -0,0 +1,703 @@
<!--
* 服务申报详情视图
*
* @Author: Assistant
* @Date: 2026-01-11
* @Description: 查看服务申报详情时打开的完整功能视图
-->
<template>
<div class="service-applications-detail">
<a-card size="small" :bordered="false" :hoverable="true" title="服务申报表">
<!-- 返回按钮 -->
<div class="detail-header">
<a-button type="link" @click="backToList" style="margin-bottom: 16px">
&lt; 返回列表
</a-button>
</div>
<!---------- 查询表单form begin ----------->
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-form-item label="执业机构" class="smart-query-form-item">
<DepartmentTreeSelect style="width: 150px" v-model:value="queryForm.firmId" placeholder="请选择执业机构" />
</a-form-item>
<a-form-item label="律师名称" class="smart-query-form-item">
<a-select
v-model:value="queryForm.userId"
style="width: 150px"
placeholder="请选择律师"
:showSearch="true"
:allowClear="true"
@focus="loadAllEmployees"
>
<a-select-option v-for="item in employeeList" :key="item.employeeId" :value="item.employeeId">
{{ item.actualName }}
<template v-if="item.departmentName"> {{ item.departmentName }} </template>
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="服务开始时间" class="smart-query-form-item">
<a-date-picker style="width: 150px" v-model:value="queryForm.serviceStart" placeholder="开始时间" />
</a-form-item>
<a-form-item label="服务结束时间" class="smart-query-form-item">
<a-date-picker style="width: 150px" v-model:value="queryForm.serviceEnd" placeholder="结束时间" />
</a-form-item>
<a-form-item label="组织单位名称" class="smart-query-form-item">
<a-input style="width: 150px" v-model:value="queryForm.organizerName" placeholder="组织单位名称" />
</a-form-item>
<a-form-item label="服务对象负责人姓名" class="smart-query-form-item">
<a-input style="width: 150px" v-model:value="queryForm.managerName" placeholder="服务对象负责人姓名" />
</a-form-item>
<a-form-item label="执业机构审核状态" class="smart-query-form-item">
<a-select style="width: 150px" v-model:value="queryForm.firmAuditStatus" placeholder="请选择审核状态">
<a-select-option v-for="status in Object.values(REVIEW_ENUM)" :key="status.value" :value="status.value">
{{ status.desc }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="协会审核状态" class="smart-query-form-item">
<a-select style="width: 150px" v-model:value="queryForm.associationAuditStatus" placeholder="请选择审核状态">
<a-select-option v-for="status in Object.values(REVIEW_ENUM)" :key="status.value" :value="status.value">
{{ status.desc }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button v-privilege="'serviceApplications:query'" type="primary" @click="onSearch">
<template #icon>
<SearchOutlined />
</template>
查询
</a-button>
<a-button @click="resetQuery" class="smart-margin-left10">
<template #icon>
<ReloadOutlined />
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<!---------- 查询表单form end ----------->
<!---------- 表格操作行 begin ----------->
<a-row class="smart-table-btn-block">
<div class="smart-table-operate-block">
<a-button v-privilege="'serviceApplications:add'" @click="showForm" type="primary">
<template #icon>
<PlusOutlined />
</template>
新建申报
</a-button>
<a-button @click="batchSubmit" type="primary" :disabled="selectedRowKeyList.length == 0">
<template #icon>
<SendOutlined />
</template>
批量提交
</a-button>
<!-- 批量审核按钮不是user角色显示 -->
<a-button
v-if="isNotUser"
@click="batchAudit"
type="primary"
:disabled="selectedRowKeyList.length == 0"
>
<template #icon>
<CheckCircleOutlined />
</template>
批量审核
</a-button>
<!-- 批量上报按钮cto角色显示 -->
<a-button
v-if="isCtoRole"
@click="batchReport"
type="primary"
:disabled="selectedRowKeyList.length == 0"
>
<template #icon>
<UploadOutlined />
</template>
批量上报
</a-button>
<a-button @click="confirmBatchDelete" type="primary" danger :disabled="selectedRowKeyList.length == 0">
<template #icon>
<DeleteOutlined />
</template>
批量删除
</a-button>
</div>
<div class="smart-table-setting-block">
<TableOperator v-model="columns" :tableId="null" :refresh="queryData" />
</div>
</a-row>
<!---------- 表格操作行 end ----------->
<!---------- 表格 begin ----------->
<a-table
size="small"
:scroll="{ x: 1800, y: 800 }"
:dataSource="tableData"
:columns="columns"
rowKey="applicationId"
bordered
:loading="tableLoading"
:pagination="false"
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
>
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="showDetail(record)" type="link">详情</a-button>
<a-button v-if="(record.firmAuditStatus === 0 || record.firmAuditStatus === 4) && record.userId === loginInfo?.userId" @click="showForm(record)" type="link">编辑</a-button>
<a-button v-if="(record.firmAuditStatus === 0 && record.userId === loginInfo?.userId)" @click="onSubmit(record)" type="link">提交</a-button>
<a-button v-if="canAuditRecord(record)" @click="showAuditModal(record)" type="link">审核</a-button>
<a-button v-if="canRejectRecord(record)" @click="showRejectModal(record)" danger type="link">驳回</a-button>
<a-button v-if="record.firmAuditStatus === 0 && record.userId === loginInfo?.userId" @click="onDelete(record)" danger type="link">删除</a-button>
</div>
</template>
</template>
</a-table>
<!---------- 表格 end ----------->
<!---------- 审核弹框 begin ----------->
<a-modal
v-model:visible="auditModalVisible"
title="服务申报审核"
:confirm-loading="auditLoading"
@ok="handleAudit"
@cancel="handleAuditCancel"
width="400px"
>
<div style="text-align: center; padding: 20px 0;">
<p style="margin-bottom: 20px; font-size: 16px;">请选择审核结果</p>
<a-radio-group v-model:value="auditForm.auditResult" size="large">
<a-radio :value="3" style="margin-right: 30px;">
<span style="font-size: 16px;">同意</span>
</a-radio>
<a-radio :value="4">
<span style="font-size: 16px;">拒绝</span>
</a-radio>
</a-radio-group>
</div>
</a-modal>
<!---------- 审核弹框 end ----------->
<!---------- 批量审核弹框 begin ----------->
<a-modal
v-model:visible="batchAuditModalVisible"
title="批量审核服务申报"
:confirm-loading="batchAuditLoading"
@ok="handleBatchAudit"
@cancel="handleBatchAuditCancel"
width="500px"
>
<a-form :model="batchAuditForm" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-item label="审核结果" required>
<a-radio-group v-model:value="batchAuditForm.auditResult">
<a-radio :value="3">同意</a-radio>
<a-radio :value="4">拒绝</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="选中记录">
<span style="color: #1890ff;">{{ selectedRowKeyList.length }} 条记录</span>
</a-form-item>
</a-form>
</a-modal>
<!---------- 批量审核弹框 end ----------->
<!---------- 驳回弹框 begin ----------->
<a-modal
v-model:visible="rejectModalVisible"
title="驳回服务申报"
:confirm-loading="rejectLoading"
@ok="handleReject"
@cancel="handleRejectCancel"
width="500px"
>
<a-form :model="rejectForm" layout="vertical">
<a-form-item label="驳回原因" required>
<a-textarea
v-model:value="rejectForm.rejectReason"
placeholder="请输入驳回原因"
rows="4"
style="resize: vertical;"
/>
</a-form-item>
</a-form>
</a-modal>
<!---------- 驳回弹框 end ----------->
<div class="smart-query-table-page">
<a-pagination
showSizeChanger
showQuickJumper
show-less-items
:pageSizeOptions="PAGE_SIZE_OPTIONS"
:defaultPageSize="queryForm.pageSize"
v-model:current="queryForm.pageNum"
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total) => `共${total}条`"
/>
</div>
<ServiceApplicationsForm ref="formRef" @reloadList="queryData"/>
</a-card>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import {
SearchOutlined,
ReloadOutlined,
PlusOutlined,
SendOutlined,
CheckCircleOutlined,
UploadOutlined,
DeleteOutlined
} from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { serviceApplicationsApi } from '/@/api/business/service-applications/service-applications-api';
import DepartmentTreeSelect from '/@/components/system/department-tree-select/index.vue';
import ServiceApplicationsForm from './service-applications-form.vue';
import TableOperator from '/@/components/smart-table/table-operator/index.vue';
const router = useRouter();
const route = useRoute();
//
const tableLoading = ref(false);
const tableData = ref([]);
const total = ref(0);
const selectedRowKeyList = ref([]);
const employeeList = ref([]);
const loginInfo = ref({});
//
const auditModalVisible = ref(false);
const auditLoading = ref(false);
const rejectModalVisible = ref(false);
const rejectLoading = ref(false);
const batchAuditModalVisible = ref(false);
const batchAuditLoading = ref(false);
//
const auditForm = reactive({
applicationId: undefined,
auditResult: 3
});
const rejectForm = reactive({
applicationId: undefined,
rejectReason: '',
approvalRemark: ''
});
const batchAuditForm = reactive({
auditResult: 3
});
//
const queryForm = reactive({
firmId: undefined,
userId: undefined,
serviceStart: undefined,
serviceEnd: undefined,
organizerName: undefined,
managerName: undefined,
firmAuditStatus: undefined,
associationAuditStatus: undefined,
pageNum: 1,
pageSize: 10
});
//
const formRef = ref();
//
const PAGE_SIZE_OPTIONS = ['10', '20', '50', '100'];
const REVIEW_ENUM = {
0: { value: 0, desc: '待提交' },
1: { value: 1, desc: '待审核' },
2: { value: 2, desc: '审核中' },
3: { value: 3, desc: '审核通过' },
4: { value: 4, desc: '审核不通过' }
};
//
const isNotUser = computed(() => {
// true
return true;
});
const isCtoRole = computed(() => {
// CTO
return false;
});
//
function canAuditRecord(record) {
return record.firmAuditStatus === 1 && isNotUser.value;
}
function canRejectRecord(record) {
return record.firmAuditStatus === 1 && isNotUser.value;
}
//
function onSelectChange(selectedRowKeys) {
selectedRowKeyList.value = selectedRowKeys;
}
//
function backToList() {
router.push('/business/erp/service/service-applications-count');
}
//
function showDetail(record) {
message.info(`查看详情: ${record.applicationId}`);
}
//
function showForm(record) {
if (formRef.value) {
formRef.value.show(record);
}
}
//
function onSubmit(record) {
message.info(`提交申报: ${record.applicationId}`);
}
//
function showAuditModal(record) {
auditForm.applicationId = record.applicationId;
auditModalVisible.value = true;
}
//
function showRejectModal(record) {
rejectForm.applicationId = record.applicationId;
rejectModalVisible.value = true;
}
//
function onDelete(record) {
message.info(`删除申报: ${record.applicationId}`);
}
//
function batchSubmit() {
if (selectedRowKeyList.value.length === 0) {
message.warning('请选择要提交的记录');
return;
}
message.info(`批量提交 ${selectedRowKeyList.value.length} 条记录`);
}
//
function batchAudit() {
if (selectedRowKeyList.value.length === 0) {
message.warning('请选择要审核的记录');
return;
}
batchAuditModalVisible.value = true;
}
//
function batchReport() {
if (selectedRowKeyList.value.length === 0) {
message.warning('请选择要上报的记录');
return;
}
message.info(`批量上报 ${selectedRowKeyList.value.length} 条记录`);
}
//
function confirmBatchDelete() {
if (selectedRowKeyList.value.length === 0) {
message.warning('请选择要删除的记录');
return;
}
message.info(`批量删除 ${selectedRowKeyList.value.length} 条记录`);
}
//
function handleAudit() {
auditLoading.value = true;
setTimeout(() => {
auditLoading.value = false;
auditModalVisible.value = false;
message.success('审核成功');
queryData();
}, 1000);
}
function handleAuditCancel() {
auditModalVisible.value = false;
auditForm.applicationId = undefined;
auditForm.auditResult = 3;
}
//
function handleReject() {
if (!rejectForm.rejectReason.trim()) {
message.warning('请输入驳回原因');
return;
}
rejectLoading.value = true;
setTimeout(() => {
rejectLoading.value = false;
rejectModalVisible.value = false;
message.success('驳回成功');
queryData();
}, 1000);
}
function handleRejectCancel() {
rejectModalVisible.value = false;
rejectForm.applicationId = undefined;
rejectForm.rejectReason = '';
rejectForm.approvalRemark = '';
}
//
function handleBatchAudit() {
batchAuditLoading.value = true;
setTimeout(() => {
batchAuditLoading.value = false;
batchAuditModalVisible.value = false;
message.success('批量审核成功');
queryData();
}, 1000);
}
function handleBatchAuditCancel() {
batchAuditModalVisible.value = false;
batchAuditForm.auditResult = 3;
}
//
async function queryData() {
tableLoading.value = true;
try {
const res = await serviceApplicationsApi.queryPage(queryForm);
if (res.data) {
tableData.value = res.data.list || [];
total.value = res.data.total || 0;
}
} catch (error) {
console.error('查询数据失败:', error);
message.error('查询数据失败');
} finally {
tableLoading.value = false;
}
}
//
function onSearch() {
queryForm.pageNum = 1;
queryData();
}
//
function resetQuery() {
Object.assign(queryForm, {
firmId: undefined,
userId: undefined,
serviceStart: undefined,
serviceEnd: undefined,
organizerName: undefined,
managerName: undefined,
firmAuditStatus: undefined,
associationAuditStatus: undefined,
pageNum: 1,
pageSize: 10
});
queryData();
}
//
async function loadAllEmployees() {
//
}
//
onMounted(() => {
//
const { firmId, firmName, year, quarter } = route.query;
//
if (firmId) {
queryForm.firmId = firmId;
}
//
queryData();
});
//
const columns = ref([
{
title: '执业机构',
dataIndex: 'departmentName',
key: 'departmentName',
ellipsis: true,
width: 150
},
{
title: '律师姓名',
dataIndex: 'userName',
key: 'userName',
ellipsis: true,
width: 100
},
{
title: '职务名称',
dataIndex: 'positionName',
key: 'positionName',
ellipsis: true,
width: 100
},
{
title: '服务开始时间',
dataIndex: 'serviceStart',
key: 'serviceStart',
ellipsis: true,
width: 150
},
{
title: '服务结束时间',
dataIndex: 'serviceEnd',
key: 'serviceEnd',
ellipsis: true,
width: 150
},
{
title: '服务时长(小时)',
dataIndex: 'serviceDuration',
key: 'serviceDuration',
ellipsis: true,
width: 100,
align: 'center'
},
{
title: '受益人数',
dataIndex: 'beneficiaryCount',
key: 'beneficiaryCount',
ellipsis: true,
width: 100,
align: 'center'
},
{
title: '组织单位名称',
dataIndex: 'organizerName',
key: 'organizerName',
ellipsis: true,
width: 150
},
{
title: '服务对象负责人姓名',
dataIndex: 'organizerContact',
key: 'organizerContact',
ellipsis: true,
width: 120
},
{
title: '联系方式',
dataIndex: 'organizerPhone',
key: 'organizerPhone',
ellipsis: true,
width: 120
},
{
title: '执业机构审核状态',
dataIndex: 'firmAuditStatus',
key: 'firmAuditStatus',
ellipsis: true,
width: 120,
align: 'center',
customRender: ({ text }) => {
const status = Object.values(REVIEW_ENUM).find(item => item.value === text);
return status ? status.desc : text;
},
},
{
title: '执业机构审核人',
dataIndex: 'firmAuditUserName',
key: 'firmAuditUserName',
ellipsis: true,
width: 120
},
{
title: '执业机构审核时间',
dataIndex: 'firmAuditTime',
key: 'firmAuditTime',
ellipsis: true,
width: 150
},
{
title: '协会审核状态',
dataIndex: 'associationAuditStatus',
key: 'associationAuditStatus',
ellipsis: true,
width: 120,
align: 'center',
customRender: ({ text }) => {
const status = Object.values(REVIEW_ENUM).find(item => item.value === text);
return status ? status.desc : text;
},
},
{
title: '协会审核意见',
dataIndex: 'associationAuditOpinion',
key: 'associationAuditOpinion',
ellipsis: true,
width: 150
},
{
title: '协会审核人',
dataIndex: 'associationAuditUserName',
key: 'associationAuditUserName',
ellipsis: true,
width: 120
},
{
title: '协会审核时间',
dataIndex: 'associationAuditTime',
key: 'associationAuditTime',
ellipsis: true,
width: 150
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
fixed: 'right',
width: 150,
align: 'center'
}
]);
</script>
<style scoped>
.service-applications-detail {
padding: 16px;
}
.detail-header {
border-bottom: 1px solid #f0f0f0;
padding-bottom: 16px;
margin-bottom: 16px;
}
</style>

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

@ -0,0 +1,217 @@
<!--
* 律所统计概览组件CEO专用
*
* @Author: Assistant
* @Date: 2026-01-11
* @Description: CEO角色查看各律所服务申报统计概览
-->
<template>
<div class="law-firm-statistics">
<!-- 律所统计概览 -->
<a-card title="律所服务申报统计" class="statistics-card">
<!-- 查询条件 -->
<div class="query-section">
<a-form :model="queryForm" layout="inline">
<a-form-item label="年度">
<a-select v-model:value="queryForm.year" placeholder="请选择年度" style="width: 120px">
<a-select-option v-for="year in yearOptions" :key="year" :value="year">{{ year }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="季度">
<a-select v-model:value="queryForm.quarter" placeholder="请选择季度" style="width: 120px">
<a-select-option v-for="quarter in quarterOptions" :key="quarter.value" :value="quarter.value">
{{ quarter.label }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="律所名称">
<DepartmentTreeSelect
v-model:value="queryForm.firmId"
placeholder="请选择律所"
style="width: 300px"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleQuery" :loading="loading">
<SearchOutlined />
查询
</a-button>
<a-button @click="handleReset" style="margin-left: 8px">
<ReloadOutlined />
重置
</a-button>
</a-form-item>
</a-form>
</div>
<!-- 统计表格 -->
<div class="statistics-table">
<a-table
:dataSource="tableData"
:columns="columns"
:loading="loading"
:pagination="pagination"
rowKey="firmId"
size="middle"
@change="handleTableChange"
>
<template #bodyCell="{ column, record }">
<!-- 查看详情按钮 -->
<template v-if="column.dataIndex === 'action'">
<a-button type="link" @click="handleViewDetail(record)">查看详情</a-button>
</template>
<!-- 服务时长 -->
<template v-else-if="column.dataIndex === 'totalDuration'">
{{ record.totalDuration || 0 }} 小时
</template>
</template>
</a-table>
</div>
</a-card>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { serviceApplicationsApi } from '/@/api/business/service-applications/service-applications-api';
import DepartmentTreeSelect from '/@/components/system/department-tree-select/index.vue';
import { router } from '/@/router'; // router
//
const emit = defineEmits(['firm-selected']);
//
const loading = ref(false);
const tableData = ref([]);
//
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ['10', '20', '50', '100']
});
//
const yearOptions = ref([]);
const quarterOptions = ref([
{ value: 1, label: '第一季度' },
{ value: 2, label: '第二季度' },
{ value: 3, label: '第三季度' },
{ value: 4, label: '第四季度' }
]);
//
const columns = ref([
{ title: '律所名称', dataIndex: 'firmName', key: 'firmName', width: 200 },
{ title: '律师人数', dataIndex: 'lawyerCount', key: 'lawyerCount', width: 120 },
{ title: '申报总数', dataIndex: 'reportCount', key: 'reportCount', width: 120 },
{ title: '已通过数', dataIndex: 'approvedCount', key: 'approvedCount', width: 120 },
{ title: '服务时长(小时)', dataIndex: 'totalServiceDuration', key: 'totalServiceDuration', width: 120 },
{ title: '操作', dataIndex: 'action', key: 'action', width: 120, fixed: 'right' }
]);
//
const queryForm = reactive({
year: new Date().getFullYear(),
quarter: null,
firmId: null
});
//
function initYearOptions() {
const currentYear = new Date().getFullYear();
const years = [];
for (let i = currentYear - 5; i <= currentYear + 1; i++) {
years.push(i);
}
yearOptions.value = years;
}
//
async function handleQuery() {
try {
loading.value = true;
const params = {
year: queryForm.year,
quarter: queryForm.quarter,
firmId: queryForm.firmId,
pageNum: pagination.value.current,
pageSize: pagination.value.pageSize
};
const res = await serviceApplicationsApi.reportStatistics(params);
tableData.value = res.data.list || [];
pagination.value.total = res.data.total || 0;
} catch (error) {
message.error('查询失败');
} finally {
loading.value = false;
}
}
//
function handleReset() {
queryForm.year = new Date().getFullYear();
queryForm.quarter = null;
queryForm.firmId = null;
pagination.value.current = 1;
handleQuery();
}
//
function handleTableChange(paginationConfig) {
pagination.value = paginationConfig;
handleQuery();
}
//
function handleViewDetail(record) {
// id
// 使Vue Router/erp/service/list
//
const queryParams = {};
if (record.firmId) {
queryParams.firmId = record.firmId;
}
// 使Vue Router
router.push({
path: '/erp/service/list',
query: queryParams
});
}
//
onMounted(() => {
initYearOptions();
handleQuery();
});
</script>
<style scoped>
.law-firm-statistics {
padding: 16px;
background: #fff;
min-height: 600px;
}
.statistics-card {
margin-bottom: 16px;
}
.query-section {
margin-bottom: 16px;
background: #fafafa;
padding: 16px;
border-radius: 4px;
}
.statistics-table {
margin-top: 16px;
}
</style>
Loading…
Cancel
Save