Files
supervision-test-frontend-repo/src/views/bg/xispeak/components/BgXiSpeakBPMForm.vue
T
wsm 2b939ed260 !159 fix(bqtakepulse): 筛选条件文案改为意见类型
* fix(bqtakepulse): 筛选条件文案改为意见类型
* fix(bgpartymatter): 反馈弹窗字段对齐
* fix(inspectProblem): 上级问题字段按问题种类动态显示必填星号
* fix(bqtakepulse): 督办次数改为数字输入
* fix(bqtakepulse): 反馈弹窗字段对齐并支持长label换行
* feat(bpm): 各流程表单默认抄送改用角色查询接口
* fix(bgpartymatter): 所办负责人审批改为单选并调整编辑表单布局
* fix(modal): 详情弹窗支持点击遮罩关闭
2026-08-18 02:58:09 +00:00

1104 lines
39 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)">{{
showJiJianFields && nonJiJianFieldsReadonly ? '督察' : '编辑'
}}</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 :xs="24" :md="{ span: 22, offset: 1 }">
<a-form-item label="是否办结" :labelCol="processLabelCol" :wrapperCol="processWideWrapperCol">
<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" :xs="24" :md="{ span: 22, offset: 1 }">
<a-form-item label="部门经办人" :labelCol="processLabelCol" :wrapperCol="processUserWrapperCol">
<sz-dept-user-modal
class="process-user-selector"
v-model:value="subApproveUser"
:disabled="savingSubApproveUser"
@confirm="setDisplayNames"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
<TaskHandleInnerContent
:form-data="formData"
:claim="claim"
:showSelnextUser="showSelnextUser"
:beforeHandle="saveMainFormBeforeProcessHandle"
:defaultReason="defaultHandleReason"
:defaultCc="defaultCcUsers"
@success="handleProcessSuccess"
@claimSuccess="handleClaimSuccess"
/>
</template>
</div>
</a-spin>
</template>
<script lang="ts" setup>
import { computed, onMounted, reactive, ref, watch, provide } 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, bgXiSpeakFeedbackDelete, saveBpmForm, saveSubApprove, getProcessVariable } from '../BgXiSpeak.api';
import { useSubApproveUserResolver } from '/@/composables/useSubApproveUserResolver';
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, CHARGE_LEADER_ROLE_ID } from '/@/constant/supervision';
import {
buildApproveReason,
buildDeptHandlerFeedbackReason,
buildDeptLeaderAssignReason,
buildDeptLeaderDirectFeedbackReason,
buildSuperviseConfirmAllReason,
buildSuperviseCheckReason,
buildJiJianCheckReason,
normalizeDeptNames,
} from '/@/composables/useDefaultApprovalReason';
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);
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: 7 } };
const wideWrapperCol = { xs: { span: 24 }, sm: { span: 17 } };
const compactLabelCol = { xs: { span: 24 }, sm: { span: 5 } };
const compactWrapperCol = { xs: { span: 24 }, sm: { span: 19 } };
// 流程变量区桌面端整体右移并缩短标签列,兼顾左侧留白与标签、控件间距。
const processLabelCol = { xs: { span: 24 }, sm: { span: 4 }, md: { span: 2 } };
const processWideWrapperCol = { xs: { span: 24 }, sm: { span: 20 }, md: { span: 22 } };
const processUserWrapperCol = { xs: { span: 24 }, sm: { span: 20 }, md: { span: 9 } };
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 { usernames: subApproveUser, displayNames: subApproveUserNameText, setDisplayNames, initFromData } = useSubApproveUserResolver();
const isEnd = ref(0);
const defaultCcUsers = ref<Array<{ username: string; realname: string }>>([]);
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 showDefaultCc = computed(() => String(props.formData?.extendUrlParams?.defaultCc ?? '') === '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> = {
// 部门负责人审批 / 所党委审批 / 督办部门负责人审批 / 纪检负责人审批
Task_15sm4w5: buildApproveReason,
Task_00i7rrp: buildApproveReason,
Task_04bazej: buildApproveReason,
Task_0bj578x: buildApproveReason,
Task_0mf32ge: buildApproveReason,
Task_0zmn6ik: buildApproveReason,
Task_133caii: buildApproveReason,
Task_175afz2: buildApproveReason,
Task_1ogk921: buildApproveReason,
Task_1w4vxgc: buildApproveReason,
Task_1wfkrvj: buildApproveReason,
// 反馈流程节点
Task_005iiwi: buildApproveReason,
Task_06di2wf: buildApproveReason,
Task_1l650tz: buildApproveReason,
// 部门经办人反馈完成情况 / 收集流程部门经办人填写 / 反馈流程部门经办人填写反馈
Task_00v3pcv: buildDeptHandlerFeedbackReason,
Task_08zb04x: buildDeptHandlerFeedbackReason,
Task_0jrgrzs: buildDeptHandlerFeedbackReason,
Task_12vdqzv: buildDeptHandlerFeedbackReason,
// 部门负责人接受任务并分配
Task_1v9ce78: () => {
if (isEnd.value === 1) return '';
return buildDeptLeaderAssignReason(normalizeDeptNames(subApproveUserNameText.value));
},
// 部门负责人直接反馈
Task_0sialna: buildDeptLeaderDirectFeedbackReason,
// 督办部门经办人确认全部反馈事项
// (暂无映射,预留 buildSuperviseConfirmAllReason)
// 督办经办人核查 / 反馈流程督办经办人核查
Task_13olnz1: buildSuperviseCheckReason,
// 纪检经办人监察
Task_0bahwr8: buildJiJianCheckReason,
};
const defaultHandleReason = computed(() => {
const builder = handleReasonTemplateMap[String(processInfo.value.taskDefKey || '')];
return builder ? builder() : '';
});
// 合并 mainForm + 表单局部变量,供占位符 ${变量名} 解析
const formScope = computed(() => ({
...mainForm,
isEnd: isEnd.value,
subApproveUser: subApproveUser.value,
subApproveUserNameText: subApproveUserNameText.value,
}));
provide('extraVars', formScope);
provide(
'flowVars',
computed(() => props.formData?.vars || {})
);
provide('formDefaultReason', defaultHandleReason);
provide('defaultCcUsers', defaultCcUsers);
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(isEnd, (val) => {
const vars = props.formData?.vars;
if (vars) {
vars.is_end = String(val);
vars.isEnd = String(val);
}
});
async function fetchDefaultCcUsers() {
try {
// 默认抄送按分管所领导角色跨部门查询,与 bgpartymatter/bqtakepulse 保持一致
const res = await defHttp.get({
url: '/sys/user/queryUserByRoleId',
params: { roleId: CHARGE_LEADER_ROLE_ID, searchSecurityLevel: 5, pageNo: 1, pageSize: 999 },
});
const records = res?.records || res?.result?.records || [];
defaultCcUsers.value = records.sort((a, b) => (a.sortno ?? Infinity) - (b.sortno ?? Infinity));
} catch {
defaultCcUsers.value = [];
}
}
watch(
() => showDefaultCc.value,
() => {
if (showDefaultCc.value) {
fetchDefaultCcUsers();
} else {
defaultCcUsers.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;
initFromData(detail.implWorker ?? '');
}
}
async function getProcessVarDeptIds(): Promise<string[]> {
const taskId = props.formData?.taskId;
if (taskId) {
try {
const res = await getProcessVariable({ taskId, key: 'impl_dept_id', isLocal: true });
const value = res?.result?.value ?? res?.value;
if (value) {
const result = String(value)
.split(',')
.map((s: string) => s.trim())
.filter(Boolean);
return result;
}
} catch (e) {
// 忽略局部变量读取失败,继续尝试流程级变量
}
}
// 局部变量未找到,尝试从流程级变量获取
const vars = (props.formData?.vars ?? {}) as Record<string, any>;
const value = vars.implDept ?? vars.pending_depts_id ?? vars.dept_id ?? vars.deptid;
if (value) {
const result = Array.isArray(value)
? value.map((v: any) => String(v).trim()).filter(Boolean)
: String(value)
.split(',')
.map((s: string) => s.trim())
.filter(Boolean);
return result;
}
// 最终 fallback 到主表已保存的牵头部门
if (mainForm.implDept) {
const result = String(mainForm.implDept)
.split(',')
.map((s: string) => s.trim())
.filter(Boolean);
return result;
}
return [];
}
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);
if (dept?.id) {
return dept.id;
}
}
// orgCode 缺失或与 departs 不匹配(多部门用户)时回退到第一个部门,避免返回空导致 saveSubApprove 报错
return departs.length > 0 ? 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 = 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 deptIds = await getProcessVarDeptIds();
if (deptIds.length) {
list = list.filter((item: any) => {
const responDeptIds = String(item.responDeptid || '')
.split(',')
.map((s: string) => s.trim());
return deptIds.some((id) => responDeptIds.includes(id));
});
}
}
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;
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 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;
}
// 限制经办人选人框与按钮的整体宽度,窄屏下仍可缩小到可用区域内。
.process-user-selector {
width: 300px;
max-width: 100%;
}
.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>