Files
supervision-test-frontend-repo/src/views/bg/xispeak/components/BgXiSpeakBPMForm.vue
T
wsm 56538e9b97 !61 feat(dqinspecttask): 新增流程阶段筛选、调整状态功能,操作栏拆分为流程/表单两个下拉菜单
* feat(dqinspecttask): 新增流程阶段筛选、调整状态功能,操作栏拆分为流程/表单两个下拉菜单
* chore(xispeak): 更新 BPM_NODE_PERMISSIONS 节点权限配置,反馈流程补充默认审批意见
* fix(dqinspecttask): 发起流程表单去掉多余的抄送tab
* fix(dqinspecttask): 发起流程补充deadline和requireFinishDate字段
* feat(dqinspecttask): 发起流程和销号流程按钮按已完成阶段数控制可见性
* refactor(xispeak): 部门领导指派意见使用翻译后的真实姓名
* refactor(xispeak): 提升常用操作到行内,删除时同步清除关联流程
* refactor(xispeak): 替换牵头部门选部门组件为SzSelectDept
2026-06-26 07:58:06 +00:00

1077 lines
38 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<a-spin :spinning="loading">
<div class="bg-xispeak-bpm-form">
<a-form
class="main-form"
:class="{ 'main-form-readonly': mainDisabled }"
labelAlign="left"
:labelCol="labelCol"
:wrapperCol="wrapperCol"
:model="mainForm"
>
<div class="base-info-header section-toolbar">
<div class="section-title">基础信息</div>
<a-space class="section-actions">
<a-button v-if="!mainDisabled" class="base-save-button" type="link" size="small" :loading="savingMain" @click="submitForm">
<Icon icon="ant-design:save-outlined" />
保存
</a-button>
<a-button
class="section-action-button"
:type="showBaseInfoDetail ? 'default' : 'link'"
size="small"
@click="showBaseInfoDetail = !showBaseInfoDetail"
>
<Icon :icon="showBaseInfoDetail ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
{{ showBaseInfoDetail ? '收起基础信息' : '展开全部信息' }}
</a-button>
</a-space>
</div>
<a-row class="base-info-row" :gutter="[12, 4]">
<a-col :xs="24" :md="12">
<a-form-item label="重要讲话指示批示" name="speakStatus">
<a-textarea v-model:value="mainForm.speakStatus" :auto-size="{ minRows: 1, maxRows: 7 }" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<a-col :xs="24" :md="12">
<a-form-item label="学习传达研究部署" name="status">
<a-textarea v-model:value="mainForm.status" :auto-size="{ minRows: 1, maxRows: 7 }" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<template v-if="showBaseInfoDetail">
<a-divider />
<a-col :span="12">
<a-form-item label="时间" name="time">
<a-date-picker v-model:value="mainForm.time" value-format="YYYY-MM-DD" :disabled="mainDisabled" style="width: 100%" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="密级" name="secretLevel">
<j-dict-select-tag
v-model:value="mainForm.secretLevel"
dictCode="secret_level"
placeholder="请选择密级"
:disabled="mainDisabled"
allow-clear
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="责任人" name="responsiblePerson">
<j-select-user v-model:value="mainForm.responsiblePerson" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="企业负责同志批示情况" name="elmComment">
<a-input v-model:value="mainForm.elmComment" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="牵头部门" name="implDept">
<SzSelectDept v-model:value="mainForm.implDept" :disabled="mainDisabled" :multiple="true" checkStrictly allow-clear />
</a-form-item>
</a-col>
<!-- <a-col :span="12">-->
<!-- <a-form-item label="落实方案" name="implPlan">-->
<!-- <a-input v-model:value="mainForm.implPlan" :disabled="mainDisabled" allow-clear />-->
<!-- </a-form-item>-->
<!-- </a-col>-->
<a-col :span="12">
<a-form-item label="贯彻落实措施" name="implMeasures">
<a-input v-model:value="mainForm.implMeasures" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="会议决策数" name="numberOfResolutions">
<a-input-number v-model:value="mainForm.numberOfResolutions" :disabled="mainDisabled" style="width: 100%" />
</a-form-item>
</a-col>
<!-- <a-col :span="12">-->
<!-- <a-form-item label="落实条数" name="implCount">-->
<!-- <a-input-number v-model:value="mainForm.implCount" :disabled="mainDisabled" style="width: 100%" />-->
<!-- </a-form-item>-->
<!-- </a-col>-->
<!-- <a-col :span="12">-->
<!-- <a-form-item label="已闭环数量" name="closedCount">-->
<!-- <a-input-number v-model:value="mainForm.closedCount" :disabled="mainDisabled" style="width: 100%" />-->
<!-- </a-form-item>-->
<!-- </a-col>-->
<a-col :span="12">
<a-form-item label="完成状态" name="completionStatus">
<JDictSelectTag
v-model:value="mainForm.completionStatus"
dictCode="db_status"
placeholder="请选择完成状态"
type="radio"
:stringToNumber="true"
:disabled="mainDisabled"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="截止日期" name="deadline">
<a-date-picker v-model:value="mainForm.deadline" value-format="YYYY-MM-DD" :disabled="mainDisabled" style="width: 100%" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="报告反馈情况" name="reportFeedbackStatus">
<a-input v-model:value="mainForm.reportFeedbackStatus" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<!-- <a-col :span="12">-->
<!-- <a-form-item label="是否存在完成风险" name="isCompletionRisk">-->
<!-- <a-radio-group v-model:value="mainForm.isCompletionRisk" :disabled="mainDisabled">-->
<!-- <a-radio :value="0">不存在</a-radio>-->
<!-- <a-radio :value="1">存在</a-radio>-->
<!-- </a-radio-group>-->
<!-- </a-form-item>-->
<!-- </a-col>-->
<a-col :span="12">
<a-form-item label="是否需要督办领导" name="needSdwApproval">
<a-radio-group v-model:value="mainForm.needSdwApproval" :disabled="mainDisabled">
<a-radio :value="1"></a-radio>
<a-radio :value="0"></a-radio>
</a-radio-group>
</a-form-item>
</a-col>
<a-col v-if="mainForm.needSdwApproval === 1" :span="12">
<a-form-item label="督导领导列表" name="sdwLeaderList">
<DeptRoleUserSelectRadioGroup
v-model:value="mainForm.sdwLeaderList"
:deptId="PARTY_COMMITTEE_DEPT_ID"
:roleId="SDW_LEADER_ROLE_ID"
:disabled="mainDisabled"
/>
</a-form-item>
</a-col>
<!-- <a-col :span="12">-->
<!-- <a-form-item label="拖期风险应对措施" name="delayRiskMitigation">-->
<!-- <a-input v-model:value="mainForm.delayRiskMitigation" :disabled="mainDisabled" allow-clear />-->
<!-- </a-form-item>-->
<!-- </a-col>-->
<a-col :span="12">
<a-form-item label="督办次数" name="supervisionCount">
<a-input-number v-model:value="mainForm.supervisionCount" :disabled="mainDisabled" style="width: 100%" />
</a-form-item>
</a-col>
<!-- <a-col :span="12">-->
<!-- <a-form-item label="父级节点" name="pid">-->
<!-- <j-tree-select-->
<!-- v-model:value="mainForm.pid"-->
<!-- dict="bg_xi_speak,impl_status,id"-->
<!-- pidField="pid"-->
<!-- pidValue="0"-->
<!-- hasChildField="has_child"-->
<!-- :disabled="mainDisabled"-->
<!-- allow-clear-->
<!-- />-->
<!-- </a-form-item>-->
<!-- </a-col>-->
<!-- <a-col :span="12">-->
<!-- <a-form-item label="落实情况" name="implStatus">-->
<!-- <a-textarea v-model:value="mainForm.implStatus" :auto-size="{ minRows: 1, maxRows: 7 }" :disabled="mainDisabled" allow-clear />-->
<!-- </a-form-item>-->
<!-- </a-col>-->
</template>
</a-row>
</a-form>
<a-divider />
<div class="feedback-header section-toolbar" v-if="showFeedbackInfo">
<div class="section-title">反馈信息</div>
<a-space class="section-actions">
<a-button v-if="showAddFeedback" class="section-action-button feedback-add-button" type="primary" @click="openFeedbackModal">
<Icon icon="ant-design:plus-outlined" />
新增反馈
</a-button>
<a-button
class="section-action-button"
:type="showFeedbackDetail ? 'default' : 'link'"
size="small"
@click="showFeedbackDetail = !showFeedbackDetail"
>
<Icon :icon="showFeedbackDetail ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
{{ showFeedbackDetail ? '收起反馈信息' : '展开反馈信息' }}
</a-button>
</a-space>
</div>
<a-table
v-if="showFeedbackInfo && showFeedbackDetail"
rowKey="id"
size="small"
bordered
:columns="feedbackColumns"
:data-source="feedbackList"
:pagination="false"
:scroll="{ x: 1980 }"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'seq'">{{ index + 1 }}</template>
<template v-if="column.key === 'attachments'">
<SUploadFile v-if="record.id" class="feedback-attachment-list" :bussiness-id="record.id" :disabled="true" :multiple="true" />
<span v-else>-</span>
</template>
<template v-if="column.key === 'feedbackAction'">
<a-button type="link" size="small" @click="handleEditFeedback(record)">编辑</a-button>
<a-popconfirm title="确定删除该反馈吗?" @confirm="handleDeleteFeedback(record)">
<a-button type="link" size="small" danger>删除</a-button>
</a-popconfirm>
</template>
</template>
</a-table>
<BgXiSpeakBPMFeedbackModal
ref="feedbackModalRef"
:showJiJianFields="showJiJianFields"
:jiJianFieldsReadonly="jiJianFieldsReadonly"
:nonJiJianFieldsReadonly="nonJiJianFieldsReadonly"
@success="loadFeedbackList"
/>
<template v-if="showProcessHandle">
<a-divider />
<div class="process-header section-toolbar">
<div class="section-title">流程办理</div>
</div>
<a-form v-if="showSubApproveUser" class="process-variable-form" labelAlign="left" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="是否办结" :labelCol="wideLabelCol" :wrapperCol="wideWrapperCol">
<a-radio-group v-model:value="isEnd" :disabled="savingSubApproveUser">
<a-radio :value="0">分配部门经办人</a-radio>
<a-radio :value="1">办结</a-radio>
</a-radio-group>
</a-form-item>
</a-col>
<a-col v-if="showSubApproveUserSelector" :span="12">
<a-form-item label="部门经办人" :labelCol="wideLabelCol" :wrapperCol="wideWrapperCol">
<sz-dept-user-modal v-model:value="subApproveUser" :disabled="savingSubApproveUser" />
</a-form-item>
</a-col>
</a-row>
</a-form>
<TaskHandleInnerContent
:form-data="formData"
:claim="claim"
:showSelnextUser="showSelnextUser"
:beforeHandle="saveMainFormBeforeProcessHandle"
:defaultReason="defaultHandleReason"
@success="handleProcessSuccess"
@claimSuccess="handleClaimSuccess"
/>
</template>
</div>
</a-spin>
</template>
<script lang="ts" setup>
import { computed, onMounted, reactive, ref, watch } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import { useUserStore } from '/@/store/modules/user';
import SzSelectDept from '/@/components/Form/src/jeecg/components/SzSelectDept.vue';
import JSelectUser from '/@/components/Form/src/jeecg/components/JSelectUser.vue';
import SzDeptUserModal from '/@/components/semri/deptUserComponent/SzDeptUserModal.vue';
import JTreeSelect from '/@/components/Form/src/jeecg/components/JTreeSelect.vue';
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
import Icon from '/@/components/Icon/index';
import { bgXiSpeakFeedbackList, bgXiSpeakFeedbackListWithDept, bgXiSpeakFeedbackDelete, saveBpmForm, saveSubApprove } from '../BgXiSpeak.api';
import { getUserList } from '/@/api/common/api';
import { usePermission } from '/@/hooks/web/usePermission';
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
import BgXiSpeakBPMFeedbackModal from './BgXiSpeakBPMFeedbackModal.vue';
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
import DeptRoleUserSelectRadioGroup from '/@/components/semri/userComponent/DeptRoleUserSelectRadioGroup.vue';
import { PARTY_COMMITTEE_DEPT_ID, SDW_LEADER_ROLE_ID } from '/@/constant/supervision';
const { isDisabledAuth, hasPermission, initBpmFormData } = usePermission();
const userStore = useUserStore();
const props = defineProps({
formDisabled: { type: Boolean, default: false },
formData: { type: Object, default: () => ({}) },
formBpm: { type: Boolean, default: true },
claim: { type: Boolean, default: false },
});
initBpmFormData(props.formData);
console.log('@props.formData', props.formData);
console.log('@props', props);
const emit = defineEmits(['ok', 'success', 'claimSuccess']);
const { createMessage } = useMessage();
const queryByIdUrl = '/bg/xispeak/bgXiSpeak/queryById';
const labelCol = { xs: { span: 24 }, sm: { span: 7 } };
const wrapperCol = { xs: { span: 24 }, sm: { span: 17 } };
const wideLabelCol = { xs: { span: 24 }, sm: { span: 3 } };
const wideWrapperCol = { xs: { span: 24 }, sm: { span: 21 } };
const compactLabelCol = { xs: { span: 24 }, sm: { span: 5 } };
const compactWrapperCol = { xs: { span: 24 }, sm: { span: 19 } };
const feedbackModalRef = ref();
const loading = ref(false);
const savingMain = ref(false);
const savingSubApproveUser = ref(false);
const showBaseInfoDetail = ref(false);
const showFeedbackDetail = ref(true);
const feedbackList = ref<any[]>([]);
const subApproveUser = ref('');
const subApproveUserNameText = ref('');
const userResolveVersion = ref(0);
const isEnd = ref(0);
const mainForm = reactive<Record<string, any>>({
id: '',
pid: '',
speakStatus: '',
time: '',
elmComment: '',
status: '',
secretLevel: '',
responsiblePerson: '',
needSdwApproval: 0,
sdwLeaderList: '',
implPlan: '',
implMeasures: '',
numberOfResolutions: undefined,
implStatus: '',
implCount: undefined,
closedCount: undefined,
reportFeedbackStatus: '',
isCompletionRisk: undefined,
delayRiskMitigation: '',
completionStatus: undefined,
deadline: '',
implDept: '',
treeDepth: undefined,
sortOrder: undefined,
bpmStatus: '',
supervisionCount: undefined,
});
const feedbackColumns = computed(() => {
const cols: any[] = [
{ title: '序号', key: 'seq', align: 'center', width: 60 },
{ title: '反馈部门名称', dataIndex: 'responDeptname', align: 'center', width: 140 },
{ title: '反馈人姓名', dataIndex: 'responPersonname', align: 'center', width: 120 },
{ title: '具体措施', dataIndex: 'method', align: 'left', width: 200 },
{ title: '完成状态', dataIndex: 'completionStatus_dictText', align: 'center', width: 100 },
{ title: '是否有报送上级报告', dataIndex: 'isReportedToHigher_dictText', align: 'center', width: 140 },
{ title: '进展情况', dataIndex: 'progress', align: 'left', width: 200 },
// { title: '落实情况', dataIndex: 'implStatus', align: 'left', width: 200 },
{ title: '附件', dataIndex: 'id', key: 'attachments', align: 'left', width: 280 },
];
if (showJiJianFields.value) {
cols.push(
{ title: '检查情况(纪检)', dataIndex: 'checkStatus', align: 'left', width: 160 },
{ title: '监督意见(纪检)', dataIndex: 'inspectionAdvice', align: 'left', width: 160 },
{ title: '措施数量(纪检)', dataIndex: 'measureNum', align: 'center', width: 100 }
);
}
if (showEditFeedback.value) {
cols.push({ title: '操作', dataIndex: 'id', key: 'feedbackAction', align: 'center', width: 120, fixed: 'right' });
}
return cols;
});
const mainDisabled = computed(() => {
if (props.formBpm) {
return props.formData?.disabled !== false;
}
return props.formDisabled;
});
const showProcessHandle = computed(() => {
const explicit = props.formData?.extendUrlParams?.showProcessHandle;
if (explicit === '0') return false;
if (explicit === '1') return true;
return props.formBpm && !!props.formData?.taskId;
});
const showAddFeedback = computed(
() => String(props.formData?.extendUrlParams?.showAddFeedback ?? props.formData?.extendUrlParams?.addfeedback ?? '') === '1'
);
const showFeedbackInfo = computed(() => {
const p = props.formData?.extendUrlParams;
const explicit = p?.showFeedbackInfo ?? p?.showApproveInfo;
if (explicit === '0') return false;
if (explicit === '1') return true;
// 未显式设置时,任一子属性开启则自动显示反馈区域
return (
String(p?.showAddFeedback ?? p?.addfeedback ?? '') === '1' ||
String(p?.showEditFeedback ?? p?.editfeedback ?? '') === '1' ||
String(p?.showDeptFeedback ?? p?.deptfeedback ?? '') === '1'
);
});
const showEditFeedback = computed(
() => String(props.formData?.extendUrlParams?.showEditFeedback ?? props.formData?.extendUrlParams?.editfeedback ?? '') === '1'
);
const showDeptFeedback = computed(
() => String(props.formData?.extendUrlParams?.showDeptFeedback ?? props.formData?.extendUrlParams?.deptfeedback ?? '') === '1'
);
const showJiJianFields = computed(() => String(props.formData?.extendUrlParams?.showJiJianFields ?? '') === '1');
const jiJianFieldsReadonly = computed(() => String(props.formData?.extendUrlParams?.jiJianFieldsReadonly ?? '') === '1');
const nonJiJianFieldsReadonly = computed(() => String(props.formData?.extendUrlParams?.nonJiJianFieldsReadonly ?? '') === '1');
const showSelnextUser = computed(() => String(props.formData?.extendUrlParams?.showSelnextUser ?? '') === '1');
const showDeptFields = computed(() => String(props.formData?.extendUrlParams?.deptfields ?? '') === '1');
const showSubApproveUser = computed(
() => String(props.formData?.extendUrlParams?.showSubApproveUser ?? props.formData?.extendUrlParams?.selectuser ?? '') === '1'
);
const showSubApproveUserSelector = computed(() => showSubApproveUser.value && String(isEnd.value) !== '1');
const processInfo = computed(() => ({
taskId: props.formData?.taskId,
taskDefKey: props.formData?.taskDefKey,
processInstanceId:
props.formData?.procInsId ||
props.formData?.procInstId ||
props.formData?.processInstanceId ||
props.formData?.vars?.BPM_INST_ID ||
props.formData?.vars?.JG_LOCAL_PROCESS_ID ||
props.formData?.vars?.process_inst_id ||
props.formData?.vars?.processInstanceId,
processDataId: props.formData?.processDataId,
processTableName: props.formData?.processTableName,
}));
// ==================== 默认审批意见 ====================
const handleReasonTemplateMap: Record<string, () => string> = {
// TODO: 填写各节点的 taskDefKey
// 部门负责人审批 / 所党委审批 / 督办部门负责人审批 / 纪检负责人审批
// 'Task_': buildApproveReason,
// 部门经办人反馈完成情况 / 收集流程部门经办人填写 / 反馈流程部门经办人填写反馈
// 'Task_': buildDeptHandlerFeedbackReason,
// 部门负责人接受任务并分配
// 'Task_': buildDeptLeaderAssignReason,
// 部门负责人直接反馈
// 'Task_': buildDeptLeaderDirectFeedbackReason,
// 督办部门经办人确认全部反馈事项
// 'Task_': buildSuperviseConfirmAllReason,
// 督办经办人核查 / 反馈流程督办经办人核查
// 'Task_': buildSuperviseCheckReason,
// 纪检经办人监察
// 'Task_': buildJiJianCheckReason,
Task_15sm4w5: buildApproveReason,
Task_00i7rrp: buildApproveReason,
Task_00v3pcv: buildDeptHandlerFeedbackReason,
Task_04bazej: buildApproveReason,
Task_08zb04x: buildDeptHandlerFeedbackReason,
Task_0bj578x: buildApproveReason,
Task_0jrgrzs: buildDeptHandlerFeedbackReason,
Task_0mf32ge: buildApproveReason,
Task_0zmn6ik: buildApproveReason,
Task_133caii: buildApproveReason,
Task_15sm4w5: buildApproveReason,
Task_175afz2: buildApproveReason,
Task_1ogk921: buildApproveReason,
Task_1v9ce78: buildDeptLeaderAssignReason,
Task_1w4vxgc: buildApproveReason,
Task_1wfkrvj: buildApproveReason,
// 反馈流程节点
Task_005iiwi: buildApproveReason,
Task_06di2wf: buildApproveReason,
Task_0bahwr8: buildJiJianCheckReason,
Task_0sialna: buildDeptLeaderDirectFeedbackReason,
Task_12vdqzv: buildDeptHandlerFeedbackReason,
Task_13olnz1: buildSuperviseCheckReason,
Task_1l650tz: buildApproveReason,
};
const defaultHandleReason = computed(() => {
const builder = handleReasonTemplateMap[String(processInfo.value.taskDefKey || '')];
return builder ? builder() : '';
});
function buildApproveReason() {
return '同意。';
}
function buildDeptHandlerFeedbackReason() {
return '已完成反馈,请领导审批。';
}
function buildDeptLeaderAssignReason() {
const userNameText = normalizeDeptNames(subApproveUserNameText.value || subApproveUser.value);
return userNameText ? `请${userNameText}办理。` : '';
}
function buildDeptLeaderDirectFeedbackReason() {
return '已完成反馈。';
}
function buildSuperviseConfirmAllReason() {
return '已确认全部反馈事项。';
}
function buildSuperviseCheckReason() {
return '已核查。';
}
function buildJiJianCheckReason() {
return '已监察。';
}
onMounted(() => {
initFormData();
});
watch(
() => props.formData?.dataId,
() => initFormData()
);
watch(
() => processInfo.value.processInstanceId,
(newVal) => {
if (newVal) loadFeedbackList();
}
);
watch(showDeptFeedback, () => {
loadFeedbackList();
});
watch(
() => props.formData?.vars?.isEnd,
(value) => {
isEnd.value = String(value ?? '0') === '1' ? 1 : 0;
},
{ immediate: true }
);
watch(
subApproveUser,
(value) => {
resolveSubApproveUserName(value);
},
{ immediate: true }
);
async function initFormData() {
const dataId = props.formData?.dataId;
if (!dataId) {
return;
}
showBaseInfoDetail.value = false;
loading.value = true;
try {
const data = await defHttp.get({ url: queryByIdUrl, params: { id: dataId } });
setMainForm(data || {});
await loadFeedbackList();
} finally {
loading.value = false;
}
}
function setMainForm(record: Record<string, any>) {
Object.keys(mainForm).forEach((key) => {
mainForm[key] = record[key] ?? '';
});
// 从 approveInfo JSON 初始化当前部门的 isEnd 和 subApproveUser
const approveInfo = record.approveInfo;
const deptId = getCurrentUserDeptId();
if (approveInfo && deptId && approveInfo[deptId]) {
const detail = approveInfo[deptId];
isEnd.value = detail.isEnd ?? 0;
subApproveUser.value = detail.implWorker ?? '';
}
}
function getCurrentUserDeptId() {
const userInfo = (userStore.getUserInfo || {}) as Record<string, any>;
const loginInfo = (userStore.getLoginInfo || {}) as Record<string, any>;
const departs = Array.isArray(loginInfo.departs) ? loginInfo.departs : [];
if (userInfo.orgCode) {
const dept = departs.find((item: any) => item.orgCode === userInfo.orgCode);
return dept?.id || '';
}
return departs.length === 1 ? departs[0]?.id || '' : '';
}
function getCurrentAssignee() {
const userInfo = (userStore.getUserInfo || {}) as Record<string, any>;
return userInfo.username || '';
}
async function loadFeedbackList() {
const bpmProcessId = processInfo.value.processInstanceId;
if (!mainForm.id || !bpmProcessId) {
feedbackList.value = [];
return;
}
const apiFn = showDeptFeedback.value ? bgXiSpeakFeedbackListWithDept : bgXiSpeakFeedbackList;
const res = await apiFn({ mainId: mainForm.id, bpmProcessId, pageNo: 1, pageSize: 999 });
let list = res?.records || res?.result?.records || [];
if (list.length && showDeptFeedback.value) {
const deptId = getCurrentUserDeptId();
if (deptId) {
list = list.filter((item: any) => {
const deptIds = String(item.responDeptid || '')
.split(',')
.map((s: string) => s.trim());
return deptIds.includes(String(deptId));
});
}
}
feedbackList.value = list;
}
async function submitForm() {
return saveMainForm();
}
async function saveMainForm(options: { showMessage?: boolean; emitOk?: boolean } = {}) {
const { showMessage = true, emitOk = true } = options;
savingMain.value = true;
try {
const processInstanceId = processInfo.value.processInstanceId;
console.log('BgXiSpeak BPM process info', { ...processInfo.value, processInstanceId });
const res = await saveBpmForm({
processInstanceId,
varField: 'json_data',
formData: { ...mainForm },
});
if (res.success) {
if (showMessage) {
createMessage.success(res.message || '保存成功');
}
if (emitOk) {
emit('ok');
}
return true;
}
createMessage.warning(res.message || '保存失败');
return false;
} catch (error) {
createMessage.warning('保存失败');
return false;
} finally {
savingMain.value = false;
}
}
async function saveMainFormBeforeProcessHandle() {
if (!mainDisabled.value) {
const mainSaved = await saveMainForm({ showMessage: false, emitOk: false });
if (!mainSaved) {
return false;
}
}
if (showSubApproveUser.value) {
return saveSubApproveUserVariable({ showSuccessMessage: false });
}
return true;
}
function openFeedbackModal() {
if (!mainForm.id) {
createMessage.warning('主表数据不存在,无法新增反馈');
return;
}
const processInstanceId = processInfo.value.processInstanceId;
if (!processInstanceId) {
createMessage.warning('流程实例ID不能为空,无法新增反馈');
return;
}
feedbackModalRef.value?.open(mainForm.id, mainForm.secretLevel, processInstanceId);
}
function handleEditFeedback(record) {
feedbackModalRef.value?.openEdit(record);
}
async function handleDeleteFeedback(record) {
if (!record.id) {
createMessage.warning('反馈记录ID不存在');
return;
}
await bgXiSpeakFeedbackDelete({ id: record.id }, loadFeedbackList);
}
async function saveSubApproveUserVariable(options: { showSuccessMessage?: boolean } = {}) {
const { showSuccessMessage = true } = options;
const isEndValue = String(isEnd.value);
const usernameValue = normalizeUsernameList(subApproveUser.value);
const deptId = getCurrentUserDeptId();
if (!mainForm.id) {
createMessage.warning('主表ID不能为空');
return false;
}
if (!deptId) {
createMessage.warning('无法获取当前用户部门');
return false;
}
if (isEndValue !== '1' && !usernameValue) {
createMessage.warning('请选择部门经办人');
return false;
}
savingSubApproveUser.value = true;
try {
const params: Record<string, any> = {
mainId: mainForm.id,
deptId,
isEnd: Number(isEndValue),
};
if (isEndValue === '1') {
params.implWorker = getCurrentAssignee();
} else {
params.implUserNameList = usernameValue.split(',').filter(Boolean);
}
const res = await saveSubApprove(params);
if (!res?.success) {
createMessage.warning(res?.message || '保存失败');
return false;
}
if (showSuccessMessage) {
createMessage.success('保存成功');
}
return true;
} catch (error) {
createMessage.warning('保存失败');
return false;
} finally {
savingSubApproveUser.value = false;
}
}
function normalizeUsernameList(value: string) {
return String(value || '')
.split(',')
.map((item) => item.trim())
.filter(Boolean)
.join(',');
}
function normalizeDeptNames(value: any) {
return splitDeptNames(value).join('、');
}
function splitDeptNames(value: any) {
return String(value || '')
.split(/[,;;、]/)
.map((item) => item.trim())
.filter(Boolean);
}
async function resolveSubApproveUserName(value: any) {
const resolveVersion = ++userResolveVersion.value;
const usernames = normalizeUsernameList(value);
if (!usernames) {
subApproveUserNameText.value = '';
return;
}
subApproveUserNameText.value = normalizeDeptNames(usernames);
try {
const res = await getUserList({ username: usernames, isMultiTranslate: 'true', pageNo: 1, pageSize: 999 });
if (resolveVersion !== userResolveVersion.value) {
return;
}
const records = res?.records || res?.result?.records || res || [];
const userNameMap = Array.isArray(records)
? records.reduce((cache: Record<string, string>, item: any) => {
const username = String(item?.username || '').trim();
const realname = String(item?.realname || item?.name || '').trim();
if (username && realname) {
cache[username] = realname;
}
return cache;
}, {})
: {};
const names = usernames
.split(',')
.map((username) => userNameMap[username] || username)
.filter(Boolean);
subApproveUserNameText.value = normalizeDeptNames(names.join(','));
} catch {
if (resolveVersion === userResolveVersion.value) {
subApproveUserNameText.value = normalizeDeptNames(usernames);
}
}
}
function handleProcessSuccess() {
emit('success');
}
function handleClaimSuccess() {
emit('claimSuccess');
}
defineExpose({
submitForm,
initFormData,
loadFeedbackList,
});
</script>
<style lang="less" scoped>
.bg-xispeak-bpm-form {
padding: 8px 12px 14px;
}
.main-form {
background: #fff;
}
.main-form,
.process-variable-form {
:deep(.ant-form-item-label) {
text-align: left;
}
:deep(.ant-form-item-label > label) {
justify-content: flex-start;
color: #1f2937;
font-weight: 500;
}
:deep(.ant-form-item-control),
:deep(.ant-form-item-control-input),
:deep(.ant-form-item-control-input-content) {
text-align: left;
}
}
.section-title {
margin-bottom: 8px;
padding-left: 8px;
border-left: 3px solid #1677ff;
color: #1f2937;
font-size: 14px;
font-weight: 600;
line-height: 16px;
}
.section-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
padding: 6px 10px;
background: #f6fbff;
border: 1px solid #d6e8ff;
border-radius: 6px;
.section-title {
margin-bottom: 0;
}
.section-actions {
display: inline-flex;
align-items: center;
gap: 4px;
}
.section-action-button {
display: inline-flex;
align-items: center;
gap: 4px;
min-width: 58px;
justify-content: center;
font-weight: 500;
box-shadow: none;
}
.base-save-button {
display: inline-flex;
align-items: center;
gap: 4px;
height: 24px;
padding: 0 6px;
color: #64748b;
font-weight: 400;
}
}
.base-info-header {
margin-top: 0;
}
.base-info-row {
padding: 0 10px;
:deep(.ant-form-item) {
margin-bottom: 6px;
}
:deep(.ant-form-item-label) {
padding-bottom: 0;
}
:deep(.ant-form-item-label > label) {
width: 100%;
}
:deep(.ant-input),
:deep(.ant-input-number),
:deep(.ant-picker),
:deep(.ant-select-selector) {
min-height: 30px;
}
}
.main-form-readonly {
:deep(.ant-input[disabled]),
:deep(.ant-input-disabled),
:deep(.ant-input-affix-wrapper-disabled),
:deep(.ant-input-affix-wrapper-disabled.ant-input-outlined),
:deep(.ant-input-affix-wrapper-disabled:hover),
:deep(.ant-input-affix-wrapper-disabled:focus),
:deep(.ant-input-affix-wrapper-disabled-focused),
:deep(.ant-input-outlined[disabled]),
:deep(.ant-input-outlined.ant-input-disabled),
:deep(textarea.ant-input[disabled]),
:deep(.ant-input-number-disabled),
:deep(.ant-input-number-disabled:hover),
:deep(.ant-input-number-disabled:focus),
:deep(.ant-picker-disabled),
:deep(.ant-select-disabled .ant-select-selector) {
background: transparent !important;
border-color: transparent !important;
color: #1f2937 !important;
box-shadow: none !important;
cursor: default !important;
opacity: 1 !important;
}
:deep(.ant-input[disabled]),
:deep(.ant-input-disabled),
:deep(.ant-input-affix-wrapper-disabled),
:deep(.ant-input-number-disabled),
:deep(textarea.ant-input[disabled]) {
padding-left: 0 !important;
padding-right: 0 !important;
}
:deep(.ant-input-affix-wrapper-disabled .ant-input) {
background: transparent !important;
color: #1f2937 !important;
cursor: default !important;
}
:deep(.ant-input-number-disabled .ant-input-number-input) {
background: transparent !important;
color: #1f2937 !important;
-webkit-text-fill-color: #1f2937 !important;
cursor: default !important;
}
:deep(.ant-input[disabled]::placeholder),
:deep(.ant-input-disabled::placeholder),
:deep(.ant-input-affix-wrapper-disabled .ant-input::placeholder) {
color: #9ca3af !important;
}
:deep(.ant-picker-disabled input),
:deep(.ant-select-disabled .ant-select-selection-item),
:deep(.ant-select-disabled .ant-select-selection-item span),
:deep(.ant-select-disabled .ant-select-selection-placeholder),
:deep(.ant-select-disabled .ant-select-selection-placeholder span) {
color: #1f2937 !important;
-webkit-text-fill-color: #1f2937 !important;
cursor: default !important;
}
:deep(.ant-picker-disabled),
:deep(.ant-select-disabled .ant-select-selector) {
padding-left: 0 !important;
}
:deep(.ant-select-disabled),
:deep(.ant-select-disabled *),
:deep(.ant-select-disabled.ant-select-outlined),
:deep(.ant-select-disabled.ant-select-outlined .ant-select-selector) {
color: #1f2937 !important;
-webkit-text-fill-color: #1f2937 !important;
opacity: 1 !important;
}
:deep(.ant-select-disabled .ant-select-arrow),
:deep(.ant-picker-disabled .ant-picker-suffix),
:deep(.ant-input-disabled .ant-input-clear-icon),
:deep(.ant-input-affix-wrapper-disabled .ant-input-clear-icon),
:deep(.ant-input-number-disabled .ant-input-number-handler-wrap),
:deep(.ant-select-disabled .ant-select-clear) {
display: none;
}
:deep(.ant-radio-wrapper-disabled) {
color: #1f2937 !important;
cursor: default !important;
}
:deep(.ant-radio-wrapper-disabled:not(.ant-radio-wrapper-checked)) {
display: none;
}
:deep(.ant-radio-disabled .ant-radio-inner) {
display: none;
}
}
.feedback-header {
margin-bottom: 8px;
.feedback-add-button {
min-width: 104px;
}
}
.process-variable-form {
margin-bottom: 8px;
}
.sub-approve-user-row {
display: flex;
width: 100%;
:deep(.ant-input-affix-wrapper),
:deep(.ant-input) {
min-width: 280px;
}
}
:deep(.ant-divider-horizontal) {
margin: 10px 0;
}
.feedback-attachment-list {
min-width: 240px;
:deep(.file-list-wrapper) {
min-height: 36px;
max-height: 130px;
overflow-y: auto;
padding: 4px 8px;
}
:deep(.file-list-header) {
margin-bottom: 6px;
padding-bottom: 6px;
}
:deep(.file-list .file-item) {
padding: 8px;
margin-bottom: 6px;
}
:deep(.ant-empty) {
margin: 0;
padding: 4px 0;
.ant-empty-image {
display: none;
}
.ant-empty-description {
font-size: 12px;
color: #999;
padding: 0;
margin: 0;
}
}
}
</style>