You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

614 lines
22 KiB
Vue

<template>
<div class="p-2">
<el-card shadow="never" style="margin-top: 0">
<!-- 审批按钮组件 -->
<approvalButton
@submitForm="submitForm"
@approvalVerifyOpen="approvalVerifyOpen"
@handleApprovalRecord="handleApprovalRecord"
:buttonLoading="buttonLoading"
:id="form.mailingApplyId"
:status="form.flowStatus"
:pageType="routeParams.type"
:mode="false"
>
<el-button
v-if="form.mailingApplyId && routeParams.type !== 'approval'"
v-hasPermi="['oa/crm:crmMailingApply:edit']"
type="success"
@click="openSignTimeDialog"
>更新签收时间</el-button
>
</approvalButton>
</el-card>
<el-card shadow="never" style="margin-top: 0">
<el-form ref="mailingApplyFormRef" :model="form" :loading="buttonLoading" :rules="rules" label-width="130px">
<el-divider content-position="left">基本信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="邮寄申请编号" prop="mailingApplyCode">
<el-input v-model="form.mailingApplyCode" placeholder="自动生成邮寄申请编号" readonly>
<template #append>
<el-button type="primary" @click="generateMailingApplyCode" :disabled="isCodeGenerated">生成邮寄申请编号</el-button>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="日期" prop="applicationDate">
<el-date-picker
clearable
v-model="form.applicationDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择日期"
:disabled="!isFormEditable"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="经手人" prop="handlerId">
<el-select
v-model="form.handlerId"
placeholder="选择经手人"
filterable
@change="onHandlerChange"
:disabled="!isFormEditable"
style="width: 100%"
>
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门" prop="deptId">
<el-select
v-model="form.deptId"
placeholder="请选择部门"
filterable
clearable
:disabled="!isFormEditable"
style="width: 100%"
>
<el-option v-for="item in deptList" :key="item.deptId" :label="item.deptName" :value="item.deptId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="省份" prop="province">
<el-input v-model="form.province" placeholder="例如目的地为山东省,请输入“山东”" :disabled="!isFormEditable" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="重量" prop="weight">
<el-input-number
v-model="form.weight"
:precision="2"
:step="0.1"
:min="0"
placeholder="请输入重量"
:disabled="!isFormEditable"
style="width: 100%"
>
<template #append>kg</template>
</el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="发起邮寄类型" prop="mailingType">
<el-select v-model="form.mailingType" placeholder="请选择邮寄类型" :disabled="!isFormEditable" style="width: 100%">
<el-option v-for="dict in mailing_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮寄费用" prop="mailingFee">
<el-input-number
v-model="form.mailingFee"
:precision="2"
:step="1"
:min="0"
placeholder="请输入邮寄费用"
:disabled="!isFormEditable"
style="width: 100%"
>
<template #append></template>
</el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="快递单号" prop="expressNo">
<el-input v-model="form.expressNo" placeholder="请输入快递单号" :disabled="!isFormEditable" />
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left">邮寄物品及事由</el-divider>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="邮寄物品信息及申请事由" prop="itemInfo" label-width="180px">
<el-input v-model="form.itemInfo" type="textarea" :rows="4" placeholder="请输入邮寄物品信息及申请事由" :disabled="!isFormEditable" />
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left">附件及关联项目</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="附件图片" prop="ossId">
<FileUpload
v-model="ossFileModel"
:limit="5"
:fileSize="20"
:fileType="['doc', 'docx', 'pdf', 'xls', 'xlsx', 'png', 'jpg', 'jpeg', 'gif']"
:disabled="!isFormEditable"
:isShowTip="true"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目名称" prop="projectId">
<el-input v-model="form.projectName" placeholder="点击选择项目" readonly @click="openProjectSelect">
<template #append>
<el-button icon="Search" @click="openProjectSelect" />
</template>
</el-input>
<el-button
v-if="form.projectId && !(routeParams.type === 'view' || routeParams.type === 'approval')"
link
type="danger"
@click="clearProjectSelect"
style="margin-left: 8px"
>
清空
</el-button>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目号" prop="projectCode">
<el-input v-model="form.projectCode" placeholder="自动填充" disabled />
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left">物流信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="物流状态" prop="logisticsStatus">
<el-select v-model="form.logisticsStatus" placeholder="请选择物流状态" :disabled="!isFormEditable" style="width: 100%">
<el-option v-for="dict in logistics_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮寄时间" prop="mailingTime">
<el-date-picker
clearable
v-model="form.mailingTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择邮寄时间"
:disabled="!isFormEditable"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="签收时间" prop="signTime">
<el-date-picker
clearable
v-model="form.signTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择签收时间"
:disabled="!isFormEditable"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" :disabled="!isFormEditable" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- 审批验证组件 -->
<SubmitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
<!-- 审批记录组件 -->
<ApprovalRecord ref="approvalRecordRef" />
<!-- 更新签收时间弹窗 -->
<el-dialog title="更新签收时间" v-model="signTimeDialog.visible" width="420px" append-to-body>
<el-form label-width="100px">
<el-form-item label="签收时间">
<el-date-picker
clearable
v-model="signTimeForm.signTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择签收时间"
style="width: 100%"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="signTimeDialog.visible = false">取消</el-button>
<el-button type="primary" :loading="signTimeDialog.loading" @click="submitSignTime"></el-button>
</div>
</template>
</el-dialog>
<!-- 项目选择弹窗 -->
<ProjectSelect ref="projectSelectRef" :multiple="false" @confirm-call-back="projectSelectCallback" />
</div>
</template>
<script setup name="CrmMailingApplyEdit" lang="ts">
import {
addCrmMailingApply,
getCrmMailingApply,
mailingApplySubmitAndFlowStart,
updateCrmMailingApply,
updateCrmMailingApplySignTime
} from '@/api/oa/crm/crmMailingApply';
import { CrmMailingApplyForm } from '@/api/oa/crm/crmMailingApply/types';
import { getUserList, listUser } from '@/api/system/user';
import { UserQuery } from '@/api/system/user/types';
import { allListDept } from '@/api/system/dept';
import ProjectSelect from '@/components/ProjectSelect/index.vue';
import { getRuleGenerateCode } from '@/api/system/codeRule';
import { CodeRuleEnum, FlowCodeEnum } from '@/enums/OAEnum';
import { getInfo } from '@/api/login';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import approvalButton from '@/components/Process/approvalButton.vue';
import { useUserStore } from '@/store/modules/user';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { mailing_type, logistics_status, mailing_apply_status, wf_business_status } = toRefs<any>(
proxy?.useDict('mailing_type', 'logistics_status', 'mailing_apply_status', 'wf_business_status')
);
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
const mailingApplyFormRef = ref<ElFormInstance>();
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const projectSelectRef = ref<InstanceType<typeof ProjectSelect>>();
const buttonLoading = ref(false);
const isCodeGenerated = ref(false);
// P0修复计算属性控制表单是否可编辑仅草稿状态且非update模式才可编辑
const isFormEditable = computed(() => {
// 查看模式或审批模式永远不可编辑
if (routeParams.value.type === 'view' || routeParams.value.type === 'approval') {
return false;
}
// 新增模式可编辑
if (routeParams.value.type === 'add' || !form.value.mailingApplyId) {
return true;
}
// 修改模式:仅草稿状态可编辑
return form.value.mailingApplyStatus === '1';
});
const signTimeDialog = reactive({ visible: false, loading: false });
const signTimeForm = reactive<{ signTime: string | undefined }>({ signTime: undefined });
// 路由参数统一使用query方式与projectInfo保持一致
const routeParams = ref<Record<string, any>>({});
// 任务变量
const taskVariables = ref<any>({});
// 用户列表
const userList = ref<any[]>([]);
// 部门列表
const deptList = ref<any[]>([]);
// OSS 上传内部 v-model 中转,避免直接绑定到 form 的字段类型导致TS报错
const ossFileModel = ref<string | string[] | undefined>(undefined);
const initFormData: CrmMailingApplyForm = {
mailingApplyId: undefined,
mailingApplyCode: undefined,
applicationDate: undefined,
handlerId: undefined,
handlerName: undefined,
deptId: undefined,
province: undefined,
weight: undefined,
mailingType: undefined,
mailingFee: undefined,
expressNo: undefined,
itemInfo: undefined,
projectId: undefined,
projectCode: undefined,
projectName: undefined,
ossId: undefined,
mailingApplyStatus: '1',
flowStatus: undefined,
logisticsStatus: undefined,
mailingTime: undefined,
signTime: undefined,
remark: undefined,
flowCode: undefined,
variables: undefined,
bizExt: undefined
};
const data = reactive<{ form: CrmMailingApplyForm; rules: any }>({
form: { ...initFormData },
rules: {
applicationDate: [{ required: true, message: '申请日期不能为空', trigger: 'blur' }],
handlerId: [{ required: true, message: '经手人不能为空', trigger: 'change' }],
province: [{ required: true, message: '省份不能为空', trigger: 'blur' }],
weight: [{ required: true, message: '重量不能为空', trigger: 'blur' }],
mailingType: [{ required: true, message: '邮寄类型不能为空', trigger: 'change' }],
mailingFee: [{ required: true, message: '邮寄费用不能为空', trigger: 'blur' }],
itemInfo: [{ required: true, message: '邮寄物品信息及申请事由不能为空', trigger: 'blur' }],
expressNo: [{ required: true, message: '快递单号不能为空', trigger: 'blur' }],
logisticsStatus: [{ required: true, message: '物流状态不能为空', trigger: 'change' }],
mailingTime: [{ required: true, message: '邮寄时间不能为空', trigger: 'change' }]
}
});
const { form, rules } = toRefs(data);
/** 加载用户列表 */
const loadUserList = async () => {
try {
const res = await getUserList({} as any);
userList.value = res.data;
} catch (e) {
userList.value = [];
}
};
/** 加载部门列表 */
const loadDeptList = async () => {
try {
const res = await allListDept({ deptCategory: '03' } as any);
deptList.value = res.data || [];
} catch (e) {
deptList.value = [];
}
};
/** 打开项目选择弹窗 */
const openProjectSelect = () => {
if (routeParams.value.type === 'view' || routeParams.value.type === 'approval') {
return;
}
projectSelectRef.value?.open();
};
/** 项目选择回调 */
const projectSelectCallback = (selectedProjects: any[]) => {
if (selectedProjects && selectedProjects.length > 0) {
const project = selectedProjects[0];
form.value.projectId = project.projectId;
form.value.projectCode = project.projectCode;
form.value.projectName = project.projectName;
}
};
/** 清空项目选择 */
const clearProjectSelect = () => {
if (routeParams.value.type === 'view' || routeParams.value.type === 'approval') {
return;
}
form.value.projectId = undefined;
form.value.projectCode = undefined;
form.value.projectName = undefined;
};
watch(
() => form.value.ossId,
(val) => {
ossFileModel.value = val as any;
},
{ immediate: true }
);
watch(
() => ossFileModel.value,
(val) => {
form.value.ossId = val as any;
}
);
/** 经手人变更时自动填充姓名和部门 */
const onHandlerChange = (userId: number) => {
const user = userList.value.find((u) => u.userId === userId);
if (user) {
form.value.handlerName = user.nickName;
form.value.deptId = user.deptId;
}
};
/** 生成邮寄申请编号 */
const generateMailingApplyCode = async () => {
if (isCodeGenerated.value) return;
try {
const params = { codeRuleCode: CodeRuleEnum.MAILING_APPLY } as any;
const res = await getRuleGenerateCode(params);
form.value.mailingApplyCode = res.msg;
if (form.value.mailingApplyCode) {
isCodeGenerated.value = true;
proxy?.$modal.msgSuccess('邮寄申请编号生成成功');
}
} catch (error) {
console.error('生成邮寄申请编号失败:', error);
proxy?.$modal.msgError('生成邮寄申请编号失败');
}
};
/** 加载表单数据 */
const loadFormData = async () => {
// 获取路由参数(兼容新旧路由)
const id = (route.query.id as string | number) || (route.params.mailingApplyId as string | number);
const type = (route.query.type as string) || (id && id !== '0' ? 'update' : 'add');
routeParams.value = { ...route.query, id, type } as any;
if (id && id !== '0' && (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval')) {
const res = await getCrmMailingApply(id);
Object.assign(form.value, res.data);
console.log('ossId:', form.value.ossId); //
if (form.value.mailingApplyCode) {
isCodeGenerated.value = true;
}
} else {
// 新增时生成编号并设置默认值
await generateMailingApplyCode();
form.value.applicationDate = new Date().toISOString().split('T')[0];
// 默认设置当前登录用户为经手人getUserList无权限时使用getInfo兜底部门信息
form.value.handlerId = userStore.userId;
form.value.handlerName = userStore.nickname as any;
try {
const userInfoRes = await getInfo();
const userVO = userInfoRes.data?.user;
if (userVO) {
form.value.handlerName = userVO.nickName;
form.value.deptId = userVO.deptId;
}
} catch (e) {
// 忽略异常:避免因权限/网络问题导致新增页不可用
}
onHandlerChange(userStore.userId as number);
}
};
/** 审批验证组件打开 */
const approvalVerifyOpen = () => {
submitVerifyRef.value?.openDialog(routeParams.value.taskId as any);
};
/** 审批记录 */
const handleApprovalRecord = () => {
approvalRecordRef.value?.init(form.value.mailingApplyId);
};
/** 审批回调 */
const submitCallback = () => {
proxy?.$modal.msgSuccess('操作成功');
proxy?.$tab.closePage();
router.go(-1);
};
const openSignTimeDialog = () => {
signTimeForm.signTime = form.value.signTime as any;
signTimeDialog.visible = true;
};
const submitSignTime = async () => {
if (!form.value.mailingApplyId) {
proxy?.$modal.msgError('请先保存单据');
return;
}
signTimeDialog.loading = true;
try {
const res = await updateCrmMailingApplySignTime({ mailingApplyId: form.value.mailingApplyId, signTime: signTimeForm.signTime } as any);
form.value = res.data;
proxy?.$modal.msgSuccess('签收时间更新成功');
signTimeDialog.visible = false;
} finally {
signTimeDialog.loading = false;
}
};
/** 提交表单 */
const submitForm = async (status: string, mode: boolean) => {
if (!mailingApplyFormRef.value) {
return;
}
if (routeParams.value.type !== 'view' && routeParams.value.type !== 'approval' && !form.value.mailingApplyCode) {
await generateMailingApplyCode();
if (!form.value.mailingApplyCode) {
proxy?.$modal.msgError('邮寄申请编号生成失败,请稍后重试');
return;
}
}
try {
if (status === 'draft') {
await mailingApplyFormRef.value.validateField(['applicationDate', 'handlerId', 'province', 'weight', 'mailingType', 'mailingFee', 'itemInfo']);
} else {
await mailingApplyFormRef.value.validate();
}
} catch (e) {
return;
}
buttonLoading.value = true;
try {
if (status !== 'draft') {
form.value.flowCode = FlowCodeEnum.MAILING_APPLY_CODE;
form.value.variables = {
mailingApplyId: form.value.mailingApplyId,
mailingApplyCode: form.value.mailingApplyCode,
applicationDate: form.value.applicationDate,
handlerId: form.value.handlerId,
handlerName: form.value.handlerName,
deptId: form.value.deptId,
province: form.value.province,
weight: form.value.weight,
mailingType: form.value.mailingType,
mailingFee: form.value.mailingFee,
expressNo: form.value.expressNo,
itemInfo: form.value.itemInfo,
projectId: form.value.projectId,
projectCode: form.value.projectCode,
projectName: form.value.projectName,
logisticsStatus: form.value.logisticsStatus,
mailingTime: form.value.mailingTime,
signTime: form.value.signTime,
remark: form.value.remark,
entity: { ...form.value }
};
form.value.bizExt = {
businessTitle: '邮寄申请',
businessCode: form.value.mailingApplyCode
};
const res = await mailingApplySubmitAndFlowStart(form.value);
form.value = res.data;
proxy?.$modal.msgSuccess('提交成功');
proxy?.$tab.closePage();
router.go(-1);
} else {
form.value.mailingApplyStatus = '1';
form.value.flowStatus = 'draft';
if (form.value.mailingApplyId) {
await updateCrmMailingApply(form.value);
} else {
const res = await addCrmMailingApply(form.value);
form.value = res.data;
}
proxy?.$modal.msgSuccess('暂存成功');
// P1修复暂存成功后关闭tab返回列表与projectInfo保持一致
proxy?.$tab.closePage();
router.go(-1);
}
} finally {
buttonLoading.value = false;
}
};
onMounted(async () => {
nextTick(async () => {
proxy?.$modal.loading('正在加载数据,请稍后...');
await loadUserList();
await loadDeptList();
await loadFormData();
proxy?.$modal.closeLoading();
});
});
</script>