Files
supervision-test-frontend-repo/src/views/bg/bgpartymatter/components/BgPartymatterBPMForm.vue
T
ye1023 65ea58b9a4 yxk-20260519-
YHJY005-添加反馈表完成状态字典项
YHJY008-对于反馈信息中间状态不需要保留,每次只编辑不新增
2026-05-19 15:20:44 +08:00

733 lines
24 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-partymatter-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="title" :labelCol="compactLabelCol" :wrapperCol="compactWrapperCol">
<a-input v-model:value="mainForm.title" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<a-col :xs="24" :md="12">
<a-form-item label="会议决议" name="content" :labelCol="compactLabelCol" :wrapperCol="compactWrapperCol">
<a-textarea v-model:value="mainForm.content" :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="year">
<j-dict-select-tag v-model:value="mainForm.year" :disabled="mainDisabled" dictCode="super_year" placeholder="请选择年份" 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" :disabled="mainDisabled" dictCode="secret_level" placeholder="请选择密级" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="序号" name="number">
<a-input v-model:value="mainForm.number" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="会议时间" name="matterTime">
<a-date-picker v-model:value="mainForm.matterTime" 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="matterCode">
<a-input v-model:value="mainForm.matterCode" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="责任部门" name="responDeptid">
<j-select-dept v-model:value="mainForm.responDeptid" :disabled="mainDisabled" :multiple="true" checkStrictly allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="协办部门" name="assistDeptid">
<j-select-dept v-model:value="mainForm.assistDeptid" :disabled="mainDisabled" :multiple="true" checkStrictly allow-clear />
</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="urgencyLevel">
<j-dict-select-tag v-model:value="mainForm.urgencyLevel" :disabled="mainDisabled" dictCode="urgency_level" placeholder="请选择紧急程度" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="督办次数" name="superviseCount">
<a-input v-model:value="mainForm.superviseCount" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="完成状态" name="bpmStatus">
<a-input v-model:value="mainForm.bpmStatus" :disabled="mainDisabled" allow-clear />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="实际执行情况" name="actualExect" :labelCol="wideLabelCol" :wrapperCol="wideWrapperCol">
<a-textarea v-model:value="mainForm.actualExect" :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">
<div class="section-title">反馈信息</div>
<a-button v-if="showAddFeedback" class="section-action-button feedback-add-button" type="primary" @click="openFeedbackModal">
<Icon icon="ant-design:plus-outlined" />
新增反馈
</a-button>
</div>
<a-table
rowKey="id"
size="small"
bordered
:columns="feedbackColumns"
:data-source="feedbackList"
:pagination="false"
:scroll="{ x: 1340 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'attachments'">
<SUploadFile
v-if="record.id"
:key="getFeedbackAttachmentKey(record)"
class="feedback-attachment-list"
:bussiness-id="record.id"
:disabled="true"
:multiple="true"
/>
<span v-else>-</span>
</template>
<template v-else-if="column.key === 'action'">
<a-space>
<a-button type="link" size="small" @click="openFeedbackEditModal(record)">编辑</a-button>
<a-popconfirm title="是否确认删除" @confirm="handleFeedbackDelete(record)">
<a-button type="link" size="small" danger>删除</a-button>
</a-popconfirm>
</a-space>
</template>
</template>
</a-table>
<BgPartymatterBPMFeedbackModal ref="feedbackModalRef" @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">
<j-select-user
v-model:value="subApproveUser"
:disabled="savingSubApproveUser"
allow-clear
/>
</a-form-item>
</a-col>
<!-- <a-col :span="24">-->
<!-- <a-form-item :wrapperCol="wideActionWrapperCol">-->
<!-- <a-button type="primary" :loading="savingSubApproveUser" @click="saveSubApproveUserVariable()">保存</a-button>-->
<!-- </a-form-item>-->
<!-- </a-col>-->
</a-row>
</a-form>
<TaskHandleInnerContent
:form-data="formData"
:claim="claim"
:beforeHandle="saveMainFormBeforeProcessHandle"
@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 JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
import Icon from '/@/components/Icon/index';
import { bgPartymatterFeedbackDelete, bgPartymatterFeedbackList, saveBpmForm, setProcessVariable } from '../BgPartymatter.api';
import { usePermission } from '/@/hooks/web/usePermission';
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
import JDictSelectTag from "../../../../components/Form/src/jeecg/components/JDictSelectTag.vue";
import BgPartymatterBPMFeedbackModal from './BgPartymatterBPMFeedbackModal.vue';
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
import JSelectUser from "../../../../components/Form/src/jeecg/components/JSelectUser.vue";
const { isDisabledAuth, hasPermission, initBpmFormData} = usePermission();
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 = '/bgpartymatter/bgPartymatter/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 wideActionWrapperCol = { xs: { span: 24 }, sm: { span: 21, offset: 3 } };
const feedbackModalRef = ref();
const loading = ref(false);
const savingMain = ref(false);
const savingSubApproveUser = ref(false);
const showBaseInfoDetail = ref(false);
const feedbackList = ref<any[]>([]);
const feedbackAttachmentRefreshKey = ref(0);
const subApproveUser = ref('');
const isEnd = ref(0);
const mainForm = reactive<Record<string, any>>({
id: '',
year: '',
secretLevel: '',
number: '',
matterTime: '',
title: '',
content: '',
matterCode: '',
responDeptid: '',
assistDeptid: '',
actualExect: '',
bpmStatus: '',
superviseCount: '',
deadline: '',
urgencyLevel: '',
isNeedAppro: '',
supDeptleaderid: '',
isNeedSuoleader: '',
suoleaderid: '',
isNeedInterval: '',
startCount: '',
intervalType: '',
intervalStartTime: '',
});
const feedbackColumns = [
{ title: '责任部门名称', dataIndex: 'responDeptname', align: 'center', width: 160 },
{ title: '反馈人姓名', dataIndex: 'responPersonname', align: 'center', width: 140 },
{ title: '实际执行情况', dataIndex: 'actualExect', align: 'left', width: 280 },
{ title: '实际完成时间', dataIndex: 'actualTime', align: 'center', width: 180 },
{ title: '完成状态', dataIndex: 'bpmStatus', align: 'center', width: 120, customRender: ({ record }) => record?.bpmStatus_dictText || record?.bpmStatus },
{ title: '附件', dataIndex: 'id', key: 'attachments', align: 'left', width: 340 },
{ title: '操作', dataIndex: 'action', key: 'action', align: 'center', width: 120, fixed: 'right' },
];
const mainDisabled = computed(() => {
if (props.formBpm) {
return props.formData?.disabled !== false;
}
return props.formDisabled;
});
const showProcessHandle = computed(() => props.formBpm && !!props.formData?.taskId);
const showAddFeedback = computed(() => String(props.formData?.extendUrlParams?.addfeedback ?? '') === '1');
const showSubApproveUser = computed(() => String(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,
}));
onMounted(() => {
initFormData();
});
watch(
[() => props.formData?.dataId, () => processInfo.value.processInstanceId],
() => initFormData()
);
watch(
() => props.formData?.vars?.isEnd,
(value) => {
isEnd.value = String(value ?? '0') === '1' ? 1 : 0;
},
{ 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] ?? '';
});
}
async function loadFeedbackList() {
const bpmProcessId = processInfo.value.processInstanceId;
if (!mainForm.id || !bpmProcessId) {
feedbackList.value = [];
feedbackAttachmentRefreshKey.value += 1;
return;
}
const res = await bgPartymatterFeedbackList({ mainId: mainForm.id, bpmProcessId, pageNo: 1, pageSize: 999 });
feedbackList.value = res?.records || res?.result?.records || [];
feedbackAttachmentRefreshKey.value += 1;
}
function getFeedbackAttachmentKey(record: Recordable) {
return `${record?.id || ''}-${feedbackAttachmentRefreshKey.value}`;
}
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('BgPartymatter 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 openFeedbackEditModal(record: Recordable) {
if (!record?.id) {
createMessage.warning('反馈记录不存在,无法编辑');
return;
}
feedbackModalRef.value?.open(mainForm.id, mainForm.secretLevel, processInfo.value.processInstanceId, record);
}
async function handleFeedbackDelete(record: Recordable) {
if (!record?.id) {
createMessage.warning('反馈记录不存在,无法删除');
return;
}
try {
await bgPartymatterFeedbackDelete({ id: record.id }, () => undefined);
createMessage.success('删除成功');
await loadFeedbackList();
} catch (error) {
createMessage.warning('删除失败');
}
}
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);
if (!processInstanceId) {
createMessage.warning('流程实例ID不能为空');
return false;
}
if (isEndValue !== '1' && !usernameValue) {
createMessage.warning('请选择部门经办人');
return false;
}
savingSubApproveUser.value = true;
try {
const isEndResult = await saveProcessVariable('isEnd', isEndValue);
if (!isEndResult?.success) {
createMessage.warning(isEndResult.message || '保存失败');
return false;
}
if (isEndValue !== '1') {
const subApproveUserResult = await saveProcessVariable('subApproveUser', usernameValue);
if (!subApproveUserResult?.success) {
createMessage.warning(subApproveUserResult.message || '保存失败');
return false;
}
}
if (showSuccessMessage) {
createMessage.success('保存成功');
}
return true;
} catch (error) {
createMessage.warning('保存失败');
return false;
} finally {
savingSubApproveUser.value = false;
}
}
function saveProcessVariable(varField: string, varVal: any) {
return setProcessVariable({
processInstanceId: processInfo.value.processInstanceId,
taskId: processInfo.value.taskId,
local: true,
varField,
varVal,
});
}
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-partymatter-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-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-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(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[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-select-disabled .ant-select-clear) {
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: 300px;
:deep(.file-list-wrapper) {
min-height: auto;
padding: 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;
}
}
</style>