|
|
|
|
<!--
|
|
|
|
|
* 服务申报表
|
|
|
|
|
*
|
|
|
|
|
* @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>
|