律师系统前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1002 lines
37 KiB

<!--
* 服务申报表
*
* @Author: wzh
* @Date: 2025-12-20 14:44:06
* @Copyright 1.0
-->
<template>
<a-modal
:title="form.applicationId ? '编辑' : '申报'"
:width="1000"
:open="visibleFlag"
@cancel="onClose"
:maskClosable="false"
:destroyOnClose="true"
>
<a-form ref="formRef" :model="form" :rules="rules" layout="vertical" >
<!-- 基本信息 -->
<a-row :gutter="16">
<a-col :span="24">
<div style="font-weight: bold; font-size: 16px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 1px solid #d9d9d9;">
基础信息
</div>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="姓名" name="actualName">
<a-input style="width: 100%" v-model:value="form.actualName" placeholder="员工姓名" disabled />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="执业证号" name="certificateNumber">
<a-input style="width: 100%" v-model:value="form.certificateNumber" placeholder="执业证号" disabled />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="执业机构" name="firmId">
<DepartmentTreeSelect v-model:value="form.firmId" placeholder="执业机构名称" width="100%" disabled />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="职务" name="positionId" required>
<PositionSelect v-model:value="form.positionId" placeholder="请选择职务" width="100%" :disabled="readonlyMode" />
</a-form-item>
</a-col>
</a-row>
<!-- 服务信息 -->
<a-row :gutter="16">
<a-col :span="24">
<div style="font-weight: bold; font-size: 16px; margin: 24px 0 16px 0; padding-bottom: 8px; border-bottom: 1px solid #d9d9d9;">
服务信息
</div>
</a-col>
</a-row>
<!-- 活动信息 -->
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="活动类型" name="activityCategoryId">
<CategoryTree
v-model:value="form.activityCategoryId"
:category-type="CATEGORY_TYPE_ENUM.GOODS.value"
placeholder="请选择活动类型"
style="width: 100%"
:disabled="readonlyMode"
@change="onActivityCategoryChange"
/>
</a-form-item>
</a-col>
<a-col :span="16">
<a-form-item label="活动名称" name="activityNameId">
<a-select
v-model:value="form.activityNameId"
placeholder="请选择活动名称"
style="width: 100%"
:disabled="!form.activityCategoryId || readonlyMode"
:options="activityList"
show-search
:filter-option="filterActivityOption"
/>
</a-form-item>
</a-col>
</a-row>
<!-- 根据活动类型显示不同字段 -->
<template v-if="currentActivityType === 'DICT' || !currentActivityType">
<!-- dict类型:显示服务时长、服务开始时间和服务结束时间 -->
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="服务开始时间" name="serviceStart" label-align="left">
<a-date-picker show-time format="YYYY-MM-DD HH:mm:ss" valueFormat="YYYY-MM-DD HH:mm:ss" v-model:value="form.serviceStart" style="width: 100%" placeholder="服务开始时间" :disabled="readonlyMode" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="服务结束时间" name="serviceEnd" label-align="left">
<a-date-picker show-time format="YYYY-MM-DD HH:mm:ss" valueFormat="YYYY-MM-DD HH:mm:ss" v-model:value="form.serviceEnd" style="width: 100%" placeholder="服务结束时间" :disabled="readonlyMode" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item name="serviceDuration">
<template #label>
<span>服务时长(小时)</span>
<span style="color: #999; font-size: 12px; margin-left: 4px;">可通过时间选择也可手动填写</span>
</template>
<a-input-number
style="width: 100%"
v-model:value="form.serviceDuration"
placeholder="服务时长(小时)"
:disabled="readonlyMode"
:min="0"
:precision="1"
:parser="value => value.replace(/[^\d.]/g, '')"
@blur="handleServiceDurationBlur"
/>
</a-form-item>
<div style="font-size: 12px; color: #f00d0dff; margin-top: -12px; margin-bottom: 12px;">注:不足30分钟(含30分钟)的,按照0.5小时填报,超过30分钟不足1小时的(含1小时),按照1小时填报。</div>
</a-col>
</a-row>
</template>
<template v-else-if="currentActivityType === 'TIME'">
<!-- time类型:显示案件编号和服务时长在一行 -->
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="案件编号" name="recordNo" required>
<a-input style="width: 100%" v-model:value="form.recordNo" placeholder="请输入案件编号" :disabled="readonlyMode" />
</a-form-item>
</a-col>
<!-- 时间类型活动显示服务时长(如果需要用户填写) -->
<a-col :span="8" v-if="currentActivity.price && currentActivity.price.includes('-')">
<a-form-item name="serviceDuration">
<template #label>
<span>服务时长(小时)</span>
<span style="color: #999; font-size: 12px; margin-left: 4px;">请在{{ currentActivity.price }}范围内填写</span>
</template>
<a-input-number
style="width: 100%"
v-model:value="form.serviceDuration"
placeholder="服务时长(小时)"
:disabled="readonlyMode"
:min="0"
:precision="2"
:parser="value => value.replace(/[^\d.]/g, '')"
@blur="handleServiceDurationBlur"
/>
</a-form-item>
</a-col>
<!-- 时间类型活动显示固定服务时长 -->
<a-col :span="8" v-else-if="currentActivity.price && !currentActivity.price.includes('-')">
<a-form-item label="服务时长(小时)">
<a-input-number style="width: 100%" v-model:value="form.serviceDuration" :disabled="true" />
</a-form-item>
</a-col>
<!-- 时间类型活动显示默认服务时长(当活动没有price字段时) -->
<a-col :span="8" v-else>
<a-form-item label="服务时长(小时)">
<a-input-number style="width: 100%" v-model:value="form.serviceDuration" :disabled="true" />
</a-form-item>
</a-col>
</a-row>
</template>
<template v-else-if="currentActivityType === 'AMOUT'">
<!-- amount类型:显示金额输入框 -->
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="金额" name="workloadScore">
<a-input-number style="width: 100%" v-model:value="form.workloadScore" placeholder="请输入金额" :disabled="readonlyMode" :min="0" :precision="2" />
</a-form-item>
</a-col>
</a-row>
</template>
<!-- 服务统计信息 -->
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="参加人数(受益人数)" name="beneficiaryCount">
<a-input-number style="width: 100%" v-model:value="form.beneficiaryCount" placeholder="参加人数(受益人数)" :disabled="readonlyMode" />
</a-form-item>
</a-col>
<a-col :span="16">
<a-form-item label="组织单位名称" name="organizerName">
<a-input style="width: 100%" v-model:value="form.organizerName" placeholder="组织单位名称" :disabled="readonlyMode" />
</a-form-item>
</a-col>
</a-row>
<!-- 联系人信息 -->
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="服务对象负责人/联系人姓名" name="organizerContact">
<a-input style="width: 100%" v-model:value="form.organizerContact" placeholder="服务对象负责人/联系人姓名" :disabled="readonlyMode" />
</a-form-item>
</a-col>
<a-col :span="16">
<a-form-item label="联系方式" name="organizerPhone">
<a-input style="width: 100%" v-model:value="form.organizerPhone" placeholder="联系电话或邮箱" :disabled="readonlyMode" />
</a-form-item>
</a-col>
</a-row>
<!-- 详细描述和证明材料 -->
<a-form-item name="serviceContent">
<template #label>
<span>服务内容描述</span>
<span style="color: #f00d0dff; font-size: 12px; margin-left: 4px;">内容描述需要包括的内容主要是时间地点主题参与人员服务内容活动效果等</span>
</template>
<SmartWangeditor ref="serviceContentRef" v-model="form.serviceContent" :height="200" :readonly="readonlyMode" />
</a-form-item>
<a-form-item name="proofMaterials" required>
<template #label>
<span class="ant-form-item-required">证明材料</span>
<span style="font-size: 12px; color: #f00d0dff;"> 注:(请上传活动方案、活动记录、照片、新闻报道等材料)支持图片(JPG/PNG)、文档(PDF/Word/PPT)格式,单文件最大10MB,最多上传5个文件</span>
</template>
<template v-if="readonlyMode">
<div v-if="defaultFileList.length > 0">
<Upload
:defaultFileList="defaultFileList"
:maxUploadSize="5"
:maxSize="10"
:multiple="true"
accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx,.ppt,.pptx,.PNG,.JPG,.GIF"
:folder="FILE_FOLDER_TYPE_ENUM.COMMON.value"
buttonText=""
listType="text"
:showUploadBtn="false"
readonly
/>
</div>
<div v-else>无证明材料</div>
</template>
<Upload
v-else
:defaultFileList="defaultFileList"
:maxUploadSize="5"
:maxSize="10"
:multiple="true"
accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx,.ppt,.pptx,.PNG,.JPG,.GIF"
:folder="FILE_FOLDER_TYPE_ENUM.COMMON.value"
buttonText="上传证明材料"
listType="text"
extraMsg="活动方案、活动记录、照片、新闻报道等,支持图片(JPG/PNG)、文档(PDF/Word/PPT)格式,单文件最大10MB,最多上传5个文件"
@change="changeAttachment"
/>
</a-form-item>
</a-form>
<template #footer>
<a-space>
<a-button @click="onClose">取消</a-button>
<a-button v-if="!readonlyMode" type="primary" @click="onSave">保存</a-button>
</a-space>
</template>
</a-modal>
</template>
<script setup>
import { reactive, ref, nextTick, watch, onMounted } from 'vue';
import _ from 'lodash';
import { message } from 'ant-design-vue';
import dayjs from 'dayjs';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { serviceApplicationsApi } from '/@/api/business/service-applications/service-applications-api';
import { smartSentry } from '/@/lib/smart-sentry';
import SmartWangeditor from '/@/components/framework/wangeditor/index.vue';
import Upload from '/@/components/support/file-upload/index.vue';
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
import { fileApi } from '/@/api/support/file-api';
import DepartmentTreeSelect from '/@/components/system/department-tree-select/index.vue';
import EmployeeSelect from '/@/components/system/employee-select/index.vue';
import { loginApi } from '/@/api/system/login-api';
import { useUserStore } from '/@/store/modules/system/user';
import CategoryTree from '/@/components/business/category-tree-select/index.vue';
import { CATEGORY_TYPE_ENUM } from '/@/constants/business/erp/category-const';
import { goodsApi } from '/@/api/business/goods/goods-api';
import PositionSelect from '/@/components/system/position-select/index.vue';
// ------------------------ 事件 ------------------------
const emits = defineEmits(['reloadList']);
// ------------------------ 显示与隐藏 ------------------------
// 是否显示
const visibleFlag = ref(false);
// 只读模式(详情查看模式)
const readonlyMode = ref(false);
// 当前用户信息
let currentUserInfo = ref(null);
// 获取当前用户信息
async function getCurrentUserInfo() {
try {
const res = await loginApi.getLoginInfo();
currentUserInfo.value = res.data;
// 返回用户信息,不直接修改表单
return res.data;
} catch (error) {
smartSentry.captureError(error);
return null;
}
}
// 活动列表数据
const activityList = ref([]);
// 当前选中的活动和活动类型
const currentActivity = ref({});
const currentActivityType = ref('');
// 活动类型变化处理
async function onActivityCategoryChange(categoryId) {
// 检查是否是新增模式(没有applicationId)
const isNewMode = !form.applicationId;
// 只有在新增模式下才清空相关填写信息
if (isNewMode) {
form.activityNameId = undefined;
form.serviceStart = undefined;
form.serviceEnd = undefined;
form.serviceDuration = undefined;
form.recordNo = undefined;
form.workloadScore = undefined;
form.beneficiaryCount = undefined;
form.organizerName = undefined;
form.organizerContact = undefined;
form.organizerPhone = undefined;
form.serviceContent = undefined;
form.proofMaterials = undefined;
form.attachmentIds = undefined;
defaultFileList.value = [];
currentActivity.value = {};
currentActivityType.value = '';
}
if (!categoryId) {
activityList.value = [];
return;
}
try {
// 根据分类ID查询活动列表
const queryParams = {
categoryId: categoryId,
shelvesFlag: true, // 只查询上架的活动
pageNum: 1,
pageSize: 100, // 获取足够多的数据
};
const result = await goodsApi.queryGoodsList(queryParams);
if (result.data && result.data.list) {
// 将活动列表转换为下拉选项格式,保存完整的活动信息
activityList.value = result.data.list.map(item => ({
label: item.goodsName,
value: item.goodsId, // 传递活动ID而不是名称
goodsId: item.goodsId,
timeType: item.timeType,
...item
}));
} else {
activityList.value = [];
}
} catch (e) {
smartSentry.captureError(e);
activityList.value = [];
message.error('获取活动列表失败');
}
}
// 活动名称搜索过滤
function filterActivityOption(input, option) {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
}
async function show(rowData, isDetail = false) {
Object.assign(form, formDefault);
// 设置只读模式
readonlyMode.value = isDetail;
// 先获取当前用户信息
const userInfo = await getCurrentUserInfo();
if (rowData && !_.isEmpty(rowData)) {
// 编辑模式或详情模式:合并用户信息和现有数据
Object.assign(form, rowData);
// 确保用户信息不被覆盖
if (userInfo) {
form.userId = userInfo.employeeId;
form.actualName = userInfo.actualName || form.actualName;
form.certificateNumber = userInfo.licenseNumber || form.certificateNumber;
form.firmId = userInfo.departmentId || form.firmId;
form.departmentName = userInfo.departmentName || form.departmentName;
}
// 编辑模式或详情模式下,如果有attachmentIds,则获取文件列表
if (form.attachmentIds && form.attachmentIds.trim()) {
await getFileListByAttachmentIds(form.attachmentIds);
}
} else {
// 新增模式:自动填充当前用户信息
if (userInfo) {
form.userId = userInfo.employeeId;
form.actualName = userInfo.actualName || '';
form.certificateNumber = userInfo.licenseNumber || '';
form.firmId = userInfo.departmentId;
form.departmentName = userInfo.departmentName || '';
}
// 新增模式下清空文件列表
defaultFileList.value = [];
form.proofMaterials = [];
form.attachmentIds = '';
}
// 编辑模式或详情模式下,如果有活动类型ID,加载活动列表
if (form.activityCategoryId) {
await onActivityCategoryChange(form.activityCategoryId);
// 加载活动列表后,根据活动名称ID更新currentActivity和currentActivityType
if (form.activityNameId && activityList.value.length > 0) {
const selectedActivity = activityList.value.find(activity => activity.goodsId === form.activityNameId);
if (selectedActivity) {
currentActivity.value = selectedActivity;
currentActivityType.value = selectedActivity.timeType || '';
// 将timeType放入serviceType字段中传递给后端
form.serviceType = selectedActivity.timeType || '';
}
}
}
// 使用字典时把下面这注释修改成自己的字典字段 有多个字典字段就复制多份同理修改 不然打开表单时不显示字典初始值
// if (form.status && form.status.length > 0) {
// form.status = form.status.map((e) => e.valueCode);
// }
visibleFlag.value = true;
nextTick(() => {
formRef.value.clearValidate();
});
}
// 重置表单
function resetForm() {
Object.assign(form, formDefault);
nextTick(() => {
formRef.value.clearValidate();
});
}
// 根据attachmentIds获取文件列表
async function getFileListByAttachmentIds(attachmentIds) {
try {
if (!attachmentIds || !attachmentIds.trim()) {
defaultFileList.value = [];
return;
}
// 直接传递attachmentIds字符串,后端接口接收字符串参数
const result = await fileApi.getFileList(attachmentIds);
if (result.data && result.data.length > 0) {
// 将文件信息转换为Upload组件需要的格式,并清理URL
defaultFileList.value = result.data.map(file => {
const cleanUrl = file.fileUrl ? file.fileUrl.replace(/[`\s]/g, '') : '';
return {
fileId: file.fileId,
fileName: file.fileName, // 显示文件名
fileUrl: cleanUrl,
fileType: file.fileType,
fileKey: file.fileKey, // 用于文件下载
uid: file.fileId,
name: file.fileName,
status: 'done',
url: cleanUrl
};
});
// 同时更新表单中的proofMaterials字段
form.proofMaterials = result.data.map(file => ({
fileName: file.fileName,
fileUrl: file.fileUrl ? file.fileUrl.replace(/[`\s]/g, '') : '',
fileType: file.fileType
}));
console.log('文件列表已清理URL:', defaultFileList.value);
} else {
defaultFileList.value = [];
form.proofMaterials = [];
}
} catch (error) {
smartSentry.captureError(error);
defaultFileList.value = [];
form.proofMaterials = [];
message.error('获取文件列表失败');
}
}
// 处理附件上传
function changeAttachment(fileList) {
// 将文件信息保存到表单中
form.proofMaterials = fileList.map(file => {
return {
fileName: file.name,
fileUrl: file.url || file.response?.data?.fileUrl,
fileType: file.type
};
});
// 提取fileId并用逗号隔开
const fileIds = fileList.map(file => {
// 从文件对象中获取fileId,优先从response中获取
return file.response?.data?.fileId || file.fileId || '';
}).filter(id => id); // 过滤掉空值
form.attachmentIds = fileIds.join(',');
// 手动触发表单验证,确保验证及时更新
nextTick(() => {
if (formRef.value) {
formRef.value.validateFields(['proofMaterials']);
}
});
}
function onClose() {
Object.assign(form, formDefault);
// 清空附件数据
defaultFileList.value = [];
form.proofMaterials = [];
form.attachmentIds = '';
// 重置只读模式
readonlyMode.value = false;
visibleFlag.value = false;
}
// ------------------------ 表单 ------------------------
// 组件ref
const formRef = ref();
const serviceContentRef = ref();
// 证明材料上传
const defaultFileList = ref([]);
const formDefault = {
applicationId: undefined, //申报ID
userId: undefined, //姓名ID
actualName: undefined, //员工姓名
certificateNumber: undefined,
firmId: undefined, //执业机构ID
departmentName: undefined, //部门名称
positionId: undefined, //职务ID
serviceStart: undefined, //服务开始时间
serviceEnd: undefined, //服务结束时间
serviceDuration: undefined, //服务时长(小时)
activityCategoryId: undefined, //活动类型
activityNameId: undefined, //活动名称
recordNo: undefined, //案件编号
workloadScore: undefined, //金额
serviceType: undefined, //申报服务类型(使用timeType)
beneficiaryCount: undefined, //参加人数(受益人数)
organizerName: undefined, //组织单位名称
organizerContact: undefined, //负责人姓名
organizerPhone: undefined, //联系方式
serviceContent: undefined, //服务内容描述
proofMaterials: undefined, //证明材料
attachmentIds: undefined, //附件ID列表(逗号隔开)
firmAuditStatus: undefined, //执业机构审核状态:0-待审核,1-通过,2-退回
recordStatus: undefined, //备案状态:0-未备案,1-已备案
updateTime: undefined, //更新时间
createTime: undefined, //创建时间
};
let form = reactive({ ...formDefault });
// 服务时长格式化函数
function formatServiceDuration(minutes) {
if (minutes <= 30) {
return 0.5; // 30分钟及以下按0.5小时
} else if (minutes <= 60) {
return 1; // 超过30分钟但不超过60分钟按1小时
} else {
// 超过1小时的,按实际小时数计算,但遵循同样的分钟规则
const hours = Math.floor(minutes / 60);
const remainingMinutes = minutes % 60;
if (remainingMinutes === 0) {
return hours; // 整小时,直接返回小时数
} else if (remainingMinutes <= 30) {
return hours + 0.5; // 剩余30分钟及以下加0.5小时
} else {
return hours + 1; // 剩余超过30分钟加1小时
}
}
}
// 服务时长输入验证函数(用于手动输入)
function validateServiceDuration(hours) {
if (hours === undefined || hours === null || isNaN(hours) || hours < 0) {
return undefined;
}
// 将小时转换为分钟
const totalMinutes = Math.round(hours * 60);
// 使用格式化函数计算正确的值
return formatServiceDuration(totalMinutes);
}
// 监听服务开始时间和结束时间变化,自动计算服务时长
watch(
() => [form.serviceStart, form.serviceEnd],
([startTime, endTime]) => {
// 只有在非编辑模式下或当前活动类型为DICT时才自动计算服务时长
if ((!form.applicationId || currentActivityType.value === 'DICT') && (startTime && endTime)) {
const start = dayjs(startTime);
const end = dayjs(endTime);
if (end.isAfter(start)) {
// 计算时间差(分钟)
const durationMinutes = end.diff(start, 'minute');
// 根据规则计算服务时长
form.serviceDuration = formatServiceDuration(durationMinutes);
} else {
form.serviceDuration = undefined;
message.warning('服务结束时间必须晚于开始时间');
}
} else if (!form.applicationId && (!startTime || !endTime)) {
// 只有在非编辑模式下才清空服务时长
form.serviceDuration = undefined;
}
},
{ immediate: true }
);
// 监听手动输入的服务时长变化(仅用于清空处理)
watch(
() => form.serviceDuration,
(newValue, oldValue) => {
// 如果用户清空了服务时长(设置为undefined或null),允许清空
if (newValue === undefined || newValue === null) {
return;
}
// 其他验证逻辑通过事件处理函数处理,避免重复
},
{ immediate: true }
);
// 监听活动名称变化,更新活动类型和设置默认服务时长
watch(
() => form.activityNameId,
(activityNameId, oldActivityNameId) => {
if (activityNameId) {
const selectedActivity = activityList.value.find(activity => activity.goodsId === activityNameId);
if (selectedActivity) {
currentActivity.value = selectedActivity;
currentActivityType.value = selectedActivity.timeType || '';
// 将timeType放入serviceType字段中传递给后端
form.serviceType = selectedActivity.timeType || '';
// 根据活动类型设置默认服务时长
// 只有在非编辑模式下(即新建模式)才设置默认服务时长
if (!form.applicationId) {
if (selectedActivity.timeType === 'TIME') {
// 检查活动是否有固定小时数(使用price字段)
if (selectedActivity.price) {
// 如果价格包含范围(有-),让用户自己填写
if (selectedActivity.price.includes('-')) {
form.serviceDuration = undefined;
} else {
// 固定使用活动中的小时数
form.serviceDuration = parseFloat(selectedActivity.price);
}
} else {
// 默认值
form.serviceDuration = 25;
}
} else if (selectedActivity.timeType === 'AMOUT') {
form.serviceDuration = undefined;
} else if (selectedActivity.timeType === 'DICT') {
// DICT类型:清空服务时长,让用户通过选择时间或手动填写
form.serviceDuration = undefined;
}
}
}
} else {
currentActivity.value = {};
currentActivityType.value = '';
}
},
{ immediate: true }
);
const rules = {
actualName: [{ required: true, message: '姓名 必填' }],
certificateNumber: [{ required: true, message: '执业证号 必填' }],
firmId: [{ required: true, message: '执业机构 必填' }],
serviceStart: [
{
validator: async (rule, value) => {
// 只有dict类型需要验证服务开始时间
//if (currentActivityType.value === 'DICT' && !value) {
// return Promise.reject('服务开始时间 必填');
//}
return Promise.resolve();
}
}
],
serviceEnd: [
{
validator: async (rule, value) => {
// 只有dict类型需要验证服务结束时间
//if (currentActivityType.value === 'DICT' && !value) {
//return Promise.reject('服务结束时间 必填');
//}
return Promise.resolve();
}
}
],
serviceDuration: [
{
validator: async (rule, value) => {
// dict类型需要验证服务时长
if (currentActivityType.value === 'DICT' && !value) {
return Promise.reject('服务时长(小时) 必填');
}
// time类型需要验证服务时长
if (currentActivityType.value === 'TIME') {
if (!value) {
return Promise.reject('服务时长(小时) 必填');
}
// 如果活动有小时范围,验证用户填写的时长是否在范围内
if (currentActivity.value.price && currentActivity.value.price.includes('-')) {
const [min, max] = currentActivity.value.price.split('-').map(Number);
if (value < min || value > max) {
return Promise.reject(`你填写的此类公益活动时长超出该活动要求的${currentActivity.value.price}小时,请按规范要求填报具体时长`);
}
}
}
// amount类型需要验证服务时长范围
if (currentActivityType.value === 'AMOUT') {
if (!value) {
return Promise.reject('服务时长(小时) 必填');
}
if (value < 100 || value > 200) {
return Promise.reject('你填写的此类公益活动时长超出该活动要求的100-200小时,请按规范要求填报具体时长');
}
}
// 验证服务时长是否符合规则(适用于所有类型)
if (value !== undefined && value !== null) {
// 将小时转换为分钟进行验证
const inputMinutes = Math.round(value * 60);
const formattedDuration = formatServiceDuration(inputMinutes);
// 检查是否符合规则
if (Math.abs(formattedDuration - value) > 0.01) {
}
}
return Promise.resolve();
}
}
],
recordNo: [
{
validator: async (rule, value) => {
// time类型需要验证案件编号
if (currentActivityType.value === 'TIME' && !value) {
return Promise.reject('案件编号 必填');
}
return Promise.resolve();
}
}
],
workloadScore: [
{
validator: async (rule, value) => {
// amount类型需要验证金额
if (currentActivityType.value === 'AMOUT' && !value) {
return Promise.reject('金额 必填');
}
return Promise.resolve();
}
}
],
activityCategoryId: [{ required: true, message: '活动类型 必填' }],
activityNameId: [{ required: true, message: '活动名称 必填' }],
beneficiaryCount: [{ required: true, message: '参加人数(受益人数) 必填' }],
organizerName: [{ required: true, message: '组织单位名称 必填' }],
organizerContact: [{ required: true, message: '负责人姓名 必填' }],
organizerPhone: [
{ required: true, message: '联系方式 必填' },
{
pattern: /^(1[3-9]\d{9}|[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/,
message: '请输入正确的手机号或邮箱格式'
}
],
serviceContent: [{ required: true, message: '服务内容描述 必填' }],
proofMaterials: [
{
validator: async (rule, value) => {
// 检查是否有上传的文件
if (!form.proofMaterials || form.proofMaterials.length === 0) {
return Promise.reject('请上传证明材料');
}
return Promise.resolve();
}
}
],
};
// 点击保存,验证表单并保存
async function onSave() {
try {
// 验证富文本编辑器内容
const editorContent = serviceContentRef.value?.getHtml() || '';
const cleanContent = editorContent.replace(/<[^>]*>/g, '').trim();
if (!cleanContent) {
message.error('服务内容描述 必填');
return;
}
await formRef.value.validateFields();
save();
} catch (err) {
message.error('数据错误,请仔细填写表单数据!');
}
}
// 点击提交,验证表单并提交
async function onSubmit() {
try {
// 验证富文本编辑器内容
const editorContent = serviceContentRef.value?.getHtml() || '';
const cleanContent = editorContent.replace(/<[^>]*>/g, '').trim();
if (!cleanContent) {
message.error('服务内容描述 必填');
return;
}
await formRef.value.validateFields();
submit();
} catch (err) {
message.error('参数验证错误,请仔细填写表单数据!');
}
}
// 新建、编辑保存API
async function save() {
SmartLoading.show();
try {
// 案号重复检查
if (form.recordNo) {
try {
const checkResult = await serviceApplicationsApi.checkCaseNumber(form.recordNo, form.applicationId);
if (!checkResult.ok) {
message.error(`本案仅限一人申报,已由${checkResult.data.lawyerName}律师申报`);
return;
}
} catch (error) {
console.error('案号校验失败:', error);
}
}
// 准备提交数据
const submitData = { ...form };
// 确保attachmentIds字段存在且为字符串格式
if (!submitData.attachmentIds) {
submitData.attachmentIds = '';
}
if (form.applicationId) {
// 编辑保存:调用update接口
await serviceApplicationsApi.update(submitData);
} else {
// 新增保存:调用add接口
await serviceApplicationsApi.add(submitData);
}
message.success('保存成功');
emits('reloadList');
onClose();
// 保存成功后刷新页面
setTimeout(() => {
window.location.reload();
}, 1000);
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
}
}
// 新建、编辑提交API
async function submit() {
SmartLoading.show();
try {
// 案号重复检查
if (form.recordNo) {
try {
const checkResult = await serviceApplicationsApi.checkCaseNumber(form.recordNo, form.applicationId);
if (!checkResult.ok) {
message.error(`本案仅限一人申报,已由${checkResult.data.lawyerName}律师申报`);
return;
}
} catch (error) {
console.error('案号校验失败:', error);
}
}
// 准备提交数据
const submitData = { ...form };
// 确保attachmentIds字段存在且为字符串格式
if (!submitData.attachmentIds) {
submitData.attachmentIds = '';
}
// 调用submit接口,传递完整数据
await serviceApplicationsApi.submit(submitData);
message.success('提交成功');
emits('reloadList');
onClose();
} catch (err) {
smartSentry.captureError(err);
} finally {
SmartLoading.hide();
}
}
defineExpose({
show,
});
// 服务时长输入框blur事件处理
function handleServiceDurationBlur() {
const value = form.serviceDuration;
// 当用户删除输入时,value会是null或undefined,允许这种情况
if (value === null || value === undefined) {
return;
}
// 验证输入值是否为有效数字,防止异常输入
if (isNaN(value) || value < 0) {
// 无效输入,清空并提示
form.serviceDuration = undefined;
message.warning('请输入有效的服务时长');
return;
}
// 限制输入范围,防止异常值
if (value > 50) {
form.serviceDuration = 50;
message.warning('服务时长不能超过50小时');
return;
}
// 无论是否有时间选择,都对用户手动输入进行格式化验证
// 将小时转换为分钟进行验证
const inputMinutes = Math.round(value * 60);
// 使用相同的格式化规则
const formattedDuration = formatServiceDuration(inputMinutes);
// 如果格式化后的值与输入值不同,说明用户输入了不符合规则的值
if (Math.abs(formattedDuration - value) > 0.01) {
// 提示用户并自动修正为符合规则的值
message.info(`服务时长已自动调整为符合规则的值:${formattedDuration}小时`);
// 直接设置格式化后的值
form.serviceDuration = formattedDuration;
}
}
// 获取文件URL
function getFileUrl(file) {
const url = file.fileUrl;
return url;
}
// 获取文件名
function getFileName(file) {
return file.fileName;
}
</script>