!132 fix(fixcontact): 反馈弹窗实际完成时间允许编辑

* fix(fixcontact): 反馈弹窗实际完成时间允许编辑
* feat(fixcontact): 反馈列表展示反馈人与反馈部门名称
* feat(fixcontact): 流程表单支持部门并行子流程
This commit is contained in:
wsm
2026-08-03 10:43:15 +00:00
parent 8c3827b59e
commit a2b7d24de9
4 changed files with 204 additions and 9 deletions
@@ -79,3 +79,19 @@ export const setBpmVariableLocal = (params) => {
export const withDrawFixContact = (params) => {
return defHttp.post({ url: '/bg/fixcontact/fixedContact20260730/withDrawFixContact', params }, { joinParamsToUrl: true, isTransformResponse: false });
};
export const saveSubApprove = (params) => {
return defHttp.post({ url: '/bg/fixcontact/fixedContact20260730/saveSubApprove', params }, { isTransformResponse: false });
};
export const setProcessVariable = (params) => {
return defHttp.post({ url: '/act/process/setProcessVariable', params }, { isTransformResponse: false });
};
/**
* 获取流程变量(支持局部变量查询),从task执行实例向上精准查找
* @param params { taskId, key, isLocal }
*/
export const getProcessVariable = (params: { taskId: string; key: string; isLocal?: boolean }) => {
return defHttp.get({ url: '/act/process/extActProcessNode/getVariable', params });
};
@@ -130,13 +130,13 @@ export const fixedContactFeedbackColumns: BasicColumn[] = [
{
title: '反馈人',
align: 'center',
dataIndex: 'feedbackUser_dictText',
dataIndex: 'feedbackUserName',
width: 110,
},
{
title: '反馈部门',
align: 'center',
dataIndex: 'feedbackDept_dictText',
dataIndex: 'feedbackDeptName',
width: 150,
},
];
@@ -44,7 +44,7 @@
</a-col>
<a-col :span="12">
<a-form-item label="实际完成时间" name="actualFinishTime">
<a-date-picker v-model:value="formData.actualFinishTime" value-format="YYYY-MM-DD" style="width: 100%" :disabled="isEdit" allow-clear />
<a-date-picker v-model:value="formData.actualFinishTime" value-format="YYYY-MM-DD" style="width: 100%" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
@@ -178,6 +178,23 @@
<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" @confirm="setDisplayNames" />
</a-form-item>
</a-col>
</a-row>
</a-form>
<TaskHandleInnerContent
:form-data="formData"
:claim="claim"
@@ -196,14 +213,17 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import { usePermission } from '/@/hooks/web/usePermission';
import { useUserStore } from '/@/store/modules/user';
import Icon from '/@/components/Icon/index';
import SzSelectDept from '/@/components/Form/src/jeecg/components/SzSelectDept.vue';
import SzSelectUser from '/@/components/semri/userComponent/SzUserSelect.vue';
import SzDeptUserModal from '/@/components/semri/deptUserComponent/SzDeptUserModal.vue';
import DeptRoleUserSelectDropDown from '/@/components/semri/userComponent/DeptRoleUserSelectDropDown.vue';
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
import FixedContact20260730BPMFeedbackModal from './FixedContact20260730BPMFeedbackModal.vue';
import { fixedContactFeedbackList, saveBpmForm, setBpmVariableLocal } from '../FixedContact20260730.api';
import { fixedContactFeedbackList, getProcessVariable, saveBpmForm, setBpmVariableLocal, saveSubApprove } from '../FixedContact20260730.api';
import { useSubApproveUserResolver } from '/@/composables/useSubApproveUserResolver';
import { PARTY_COMMITTEE_DEPT_ID, CHARGE_LEADER_ROLE_ID } from '/@/constant/supervision';
const { initBpmFormData } = usePermission();
@@ -219,6 +239,7 @@
const emit = defineEmits(['ok', 'success', 'claimSuccess']);
const { createMessage } = useMessage();
const userStore = useUserStore();
const queryByIdUrl = '/bg/fixcontact/fixedContact20260730/queryById';
const labelCol = { xs: { span: 24 }, sm: { span: 7 } };
@@ -229,10 +250,13 @@
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 feedbackAttachmentRefreshKey = ref(0);
const { usernames: subApproveUser, setDisplayNames, initFromData } = useSubApproveUserResolver();
const isEnd = ref(0);
const mainForm = reactive<Record<string, any>>({
id: '',
@@ -257,8 +281,8 @@
}
const feedbackBaseColumns = [
{ title: '反馈部门', dataIndex: 'feedbackDept_dictText', align: 'center', width: 160 },
{ title: '反馈人', dataIndex: 'feedbackUser_dictText', align: 'center', width: 120 },
{ title: '反馈部门', dataIndex: 'feedbackDeptName', align: 'center', width: 160 },
{ title: '反馈人', dataIndex: 'feedbackUserName', align: 'center', width: 120 },
{ title: '计划完成时间', dataIndex: 'planFinishTime', align: 'center', width: 130, customRender: ({ text }) => (!text ? '' : String(text).slice(0, 10)) },
{ title: '计划完成情况', dataIndex: 'planFinishStatus', align: 'left', width: 260, customRender: renderWrappedCell },
{ title: '处理意见、承办情况', dataIndex: 'handleOpinion', align: 'left', width: 260, customRender: renderWrappedCell },
@@ -319,6 +343,10 @@
return !!hasProcess;
});
const showSelnextUser = computed(() => String(props.formData?.extendUrlParams?.showSelnextUser ?? '') === '1');
const showSubApproveUser = computed(
() => String(props.formData?.extendUrlParams?.showSubApproveUser ?? props.formData?.extendUrlParams?.selectuser ?? '') === '1'
);
const showSubApproveUserSelector = computed(() => showSubApproveUser.value && String(isEnd.value) !== '1');
onMounted(() => {
initFormData();
@@ -326,6 +354,21 @@
watch([() => props.formData?.dataId, () => processInfo.value.processInstanceId], () => initFormData());
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 initFormData() {
const dataId = props.formData?.dataId;
if (!dataId) {
@@ -338,7 +381,7 @@
if (dataId !== props.formData?.dataId) {
return;
}
setMainForm(data || {});
await setMainForm(data || {});
if (dataId !== props.formData?.dataId) {
return;
}
@@ -348,10 +391,57 @@
}
}
function setMainForm(record: Record<string, any>) {
async function setMainForm(record: Record<string, any>) {
Object.keys(mainForm).forEach((key) => {
mainForm[key] = record[key] ?? '';
});
// 从 approveInfo JSON 初始化当前子流程部门的 isEnd 和部门经办人
const approveInfo = record.approveInfo;
const deptId = await getCurrentProcessDeptId();
if (approveInfo && deptId && approveInfo[deptId]) {
const detail = approveInfo[deptId];
isEnd.value = detail.isEnd ?? 0;
// 多选经办人存于 implUserNameList,单选经办人存于 implWorker
const workerNames = Array.isArray(detail.implUserNameList) && detail.implUserNameList.length
? detail.implUserNameList.join(',')
: detail.implWorker ?? '';
await initFromData(workerNames);
}
}
async function getCurrentProcessDeptId() {
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 deptId = String(value).replace('[', '').replace(']', '').trim();
if (deptId) {
return deptId;
}
}
} catch (e) {
// 局部变量读取失败,回退到当前用户部门
}
}
return getCurrentUserDeptId();
}
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() {
@@ -362,7 +452,7 @@
return;
}
const res = await fixedContactFeedbackList({ id: mainForm.id, bpmProcessId });
feedbackList.value = res?.records || res?.result?.records || [];
feedbackList.value = Array.isArray(res) ? res : res?.records || [];
feedbackAttachmentRefreshKey.value += 1;
}
@@ -410,9 +500,98 @@
return false;
}
}
if (showSubApproveUser.value) {
return saveSubApproveUserVariable({ showSuccessMessage: false });
}
return true;
}
async function saveSubApproveUserVariable(options: { showSuccessMessage?: boolean } = {}) {
const { showSuccessMessage = true } = options;
const processInstanceId = processInfo.value.processInstanceId;
const isEndValue = String(isEnd.value);
const usernameValue = normalizeUsernameList(subApproveUser.value);
const deptId = await getCurrentProcessDeptId();
if (!mainForm.id) {
createMessage.warning('主表ID不能为空');
return false;
}
if (!processInstanceId) {
createMessage.warning('流程实例ID不能为空');
return false;
}
if (!deptId) {
createMessage.warning('无法获取当前部门');
return false;
}
if (isEndValue !== '1' && !usernameValue) {
createMessage.warning('请选择部门经办人');
return false;
}
savingSubApproveUser.value = true;
try {
// 写 is_end 子流程局部变量(多实例间互不覆盖),供 ${fixContactFlow.getIsEnd(execution)} 网关路由使用
const isEndResult = await saveProcessVariableLocal('is_end', isEndValue);
if (!isEndResult?.success) {
createMessage.warning(isEndResult.message || '保存失败');
return false;
}
// 未办结时写部门经办人子流程局部变量,供子流程内经办人并行会签多实例 collection 使用
if (isEndValue !== '1') {
const subApproveUserResult = await saveProcessVariableLocal('sub_approve_user', usernameValue);
if (!subApproveUserResult?.success) {
createMessage.warning(subApproveUserResult.message || '保存失败');
return false;
}
}
// 同步落库 approve_info JSON,用于按部门回显
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;
}
}
// 局部变量写入(默认向上追溯 2 层父级执行实例),与 FixContactDeptLeaderApproveListener 存储目标保持一致
function saveProcessVariableLocal(varField: string, varVal: any) {
return setBpmVariableLocal({
processInstanceId: processInfo.value.processInstanceId,
taskId: processInfo.value.taskId,
parentLevel: 2,
varField,
varVal,
});
}
function normalizeUsernameList(value: string) {
return String(value || '')
.split(',')
.map((item) => item.trim())
.filter(Boolean)
.join(',');
}
function openFeedbackModal() {
if (!mainForm.id) {
createMessage.warning('主表数据不存在,无法新增反馈');