Files
supervision-test-frontend-repo/src/views/bg/xispeak/BgXiSpeakList.vue
T
wsm 0e848b8a02 !158 feat(dq): 整改任务查看详情改为事项信息+反馈信息双tab纯文本弹窗
* feat(dq): 整改任务查看详情改为事项信息+反馈信息双tab纯文本弹窗
* feat(xispeak): 查看详情改为事项信息+反馈信息双tab纯文本弹窗
* feat(bqtakepulse): 查看详情改为事项信息+反馈信息双tab纯文本弹窗
* style(bgpartymatter): 反馈表单输入框固定label宽度左对齐
* feat(bgpartymatter): 查看详情改为事项信息+反馈信息双tab纯文本弹窗
2026-08-17 07:15:31 +00:00

997 lines
32 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>
<div class="p-2 erpNativeList">
<!--查询区域-->
<div class="jeecg-basic-table-form-container">
<a-form ref="formRef" @keyup.enter="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row :gutter="24">
<a-col :xl="5" :lg="6" :md="8" :sm="12">
<a-form-item name="speakStatus">
<template #label>
<a-tooltip title="支持模糊查询,输入重要讲话指示批示情况关键词">习总书记重要讲话指示批示</a-tooltip>
</template>
<a-tooltip title="支持模糊查询,输入重要讲话指示批示情况关键词"><JInput v-model:value="queryParam.speakStatus" type="like" placeholder="请输入重要讲话指示批示情况" allow-clear trim @change="debouncedSearchQuery" /></a-tooltip>
</a-form-item>
</a-col>
<a-col :xl="6" :lg="6" :md="8" :sm="12">
<a-form-item name="implDept" :label-col="{ xs: 24, sm: 8, xl: 6 }" :wrapper-col="{ xs: 24, sm: 16, xl: 18 }">
<template #label>
<a-tooltip title="按牵头部门筛选">牵头部门</a-tooltip>
</template>
<a-tooltip title="按牵头部门筛选"><SzSelectDept v-model:value="selectedImplDept" :multiple="true" placeholder="选择牵头部门" checkStrictly allow-clear style="width: 100%" @change="handleImplDeptChange" /></a-tooltip>
</a-form-item>
</a-col>
<a-col :xl="4" :lg="6" :md="8" :sm="12">
<a-form-item name="responsiblePerson">
<template #label>
<a-tooltip title="按所党委责任领导筛选">所党委责任领导</a-tooltip>
</template>
<a-tooltip title="按所党委责任领导筛选"><DeptRoleUserSelectDropDown v-model:value="queryParam.responsiblePerson" :deptId="PARTY_COMMITTEE_DEPT_ID" :roleId="SDW_LEADER_ROLE_ID" placeholder="请选择所党委责任领导" allow-clear style="width: 100%" @change="searchQuery" /></a-tooltip>
</a-form-item>
</a-col>
<!-- <a-col :lg="4" :md="6" :sm="8">-->
<!-- <a-form-item label="完成状态" name="completionStatus">-->
<!-- <JDictSelectTag v-model:value="queryParam.completionStatus" dictCode="db_status" placeholder="请选择完成状态" allow-clear @change="searchQuery" />-->
<!-- </a-form-item>-->
<!-- </a-col>-->
<a-col :xl="4" :lg="6" :md="8" :sm="12">
<a-form-item name="bpmStatus">
<template #label>
<a-tooltip title="按督办状态筛选">督办状态</a-tooltip>
</template>
<a-tooltip title="按督办状态筛选"><a-select v-model:value="bpmStatusFilter" placeholder="请选择" allow-clear @change="handleBpmStatusChange">
<a-select-option value="1">未开始</a-select-option>
<a-select-option value="2">督办中</a-select-option>
<a-select-option value="3">已完成</a-select-option>
</a-select></a-tooltip>
</a-form-item>
</a-col>
<a-col :xl="5" :lg="6" :md="8" :sm="12">
<div class="table-page-search-submitButtons">
<a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery">查询</a-button>
<!-- 高级查询 -->
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
<a-button preIcon="ant-design:reload-outlined" @click="searchReset">重置</a-button>
</div>
</a-col>
</a-row>
</a-form>
</div>
<div class="content">
<BasicTable
@register="registerTable"
:rowSelection="rowSelection"
:expandedRowKeys="expandedRowKeys"
@expand="handleExpand"
@fetch-success="onFetchSuccess"
:rowClassName="(r) => r.bpmStatus ? 'status-row-' + r.bpmStatus : ''"
>
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:add'" @click="handleAdd" preIcon="ant-design:plus-outlined">新增</a-button>
<a-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">导出</a-button>
<!-- <j-upload-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls"-->
<!-- >导入</j-upload-button-->
<!-- >-->
<a-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:exportXls'" preIcon="ant-design:download-outlined" @click="handleDownloadTemplate"
>下载模板</a-button
>
<j-upload-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:importExcel'" preIcon="ant-design:check-outlined" @click="handleCheckExcel"
>模板校验</j-upload-button
>
<j-upload-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:importExcel'" preIcon="ant-design:import-outlined" @click="handleImportByTemplate"
>模板导入</j-upload-button
>
<a-button
v-if="selectedRowKeys.length > 0"
v-auth="'bg.xispeak:bg_xi_speak:deleteBatch'"
danger
@click="batchHandleDelete"
>
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-button>
<a-space style="float: right; font-size: 14px; font-weight: bold">
<a-tag color="warning" style="color: #8c6900">未开始 {{ statsData.notStarted }}</a-tag>
<a-tag color="error">督办中 {{ statsData.inProgress }}已逾期 {{ statsData.overdue }}</a-tag>
<a-tag color="success">已完成 {{ statsData.completed }}</a-tag>
</a-space>
</template>
<!--操作栏-->
<template #action="{ record }">
<Dropdown :trigger="['hover']" :dropMenuList="getFlowMenuList(record)" popconfirm>
<a-button type="link" size="small">流程操作 <Icon icon="mdi-light:chevron-down" /></a-button>
</Dropdown>
<Dropdown :trigger="['hover']" :dropMenuList="getFormMenuList(record)" popconfirm>
<a-button type="link" size="small">表单操作 <Icon icon="mdi-light:chevron-down" /></a-button>
</Dropdown>
</template>
<!--字段回显插槽-->
<template v-slot:bodyCell="{ column, record, index, text }">
<template v-if="column.dataIndex === 'speakStatus'">
<a-tooltip title="点击查看详情">
<a style="font-weight: 500" @click="handleDetail(record)">{{ text }}</a>
</a-tooltip>
</template>
</template>
</BasicTable>
</div>
<BgXiSpeakFeedbackViewModal ref="feedbackViewModalRef" />
<!-- tab 详情弹窗 -->
<BgXiSpeakDetailModal ref="detailModalRef" />
<!-- 表单区域 -->
<BgXiSpeakModal ref="registerModal" @success="handleSuccess"></BgXiSpeakModal>
<!-- 审批记录 -->
<BpmPictureModal @register="registerBpmModal" />
<FlowScheduleStartModal ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
<FeedbackRightNowModal ref="feedbackRightNowModalRef" @confirm="handleFeedbackRightNowConfirm" />
<BusinessProcessListModal ref="businessProcessListModalRef" />
</div>
</template>
<script lang="ts" name="bg.xispeak-bgXiSpeak" setup>
import { ref, reactive, unref, computed, provide, nextTick, onMounted } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import { BasicTable, useTable } from '/@/components/Table';
import { Dropdown } from '/@/components/Dropdown';
import { useListPage } from '/@/hooks/system/useListPage';
import { useModal } from '/@/components/Modal';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import { showImportValidationErrors } from '/@/utils/helper/importError';
import { startProcessSchedules } from '/@/api/common/api';
import { dateUtil } from '/@/utils/dateUtil';
import JInput from '/@/components/Form/src/jeecg/components/JInput.vue';
import SzSelectDept from '/@/components/Form/src/jeecg/components/SzSelectDept.vue';
import DeptRoleUserSelectDropDown from '/@/components/semri/userComponent/DeptRoleUserSelectDropDown.vue';
import { PARTY_COMMITTEE_DEPT_ID, SDW_LEADER_ROLE_ID } from '/@/constant/supervision';
import BgXiSpeakModal from './components/BgXiSpeakModal.vue';
import BgXiSpeakDetailModal from './components/BgXiSpeakDetailModal.vue';
import BusinessProcessListModal from '/src/components/semri/process/BusinessProcessListModal.vue';
import FlowScheduleStartModal from '/src/components/semri/process/FlowScheduleStartModal.vue';
import FeedbackRightNowModal from './components/FeedbackRightNowModal.vue';
import BgXiSpeakFeedbackViewModal from './components/BgXiSpeakFeedbackViewModal.vue';
import { columns as taskTaskColumns } from '/@/views/taskProcess/TaskTask.data';
import { columns, superQuerySchema } from './BgXiSpeak.data';
import {
list,
deleteBgXiSpeak,
batchDeleteBgXiSpeak,
getExportXlsByEasyExcelUrl,
getImportUrl,
getExportXlsHeadersUrl,
getImportExcelByEasyExcelUrl,
getCheckExcelByEasyExcelUrl,
getChildList,
getChildListBatch,
updateLaunchType,
saveOrUpdateDict,
withDrawXiSpeak,
queryFeedbackCounts,
statusStats,
} from './BgXiSpeak.api';
const FLOW_CODE = 'dev_bg_xi_speak_001';
const FLOW_NAME = '习总书记重要讲话指示批示';
const FLOW_CODE_JBR = 'bg_xi_speak_fb_01';
const BUSINESS_TABLE_NAME = 'bg_xi_speak';
const FORM_URL = 'bg/xispeak/components/BgXiSpeakBPMForm?showProcessHandle=0&showFeedbackInfo=1&showJiJianFields=1';
const [registerBpmModal, { openModal: bpmPicModal }] = useModal();
const { createMessage } = useMessage();
const formRef = ref();
const detailModalRef = ref();
const expandedRowKeys = ref([]);
const queryParam = ref<any>({});
const registerModal = ref();
const flowScheduleModalRef = ref();
const feedbackViewModalRef = ref();
const feedbackRightNowModalRef = ref();
const businessProcessListModalRef = ref();
const bpmStatusFilter = ref();
const selectedImplDept = ref([]);
const currentBusinessId = ref('');
const pendingFeedbackRightNow = ref(0);
const statsData = reactive({ notStarted: 0, inProgress: 0, overdue: 0, completed: 0 });
const processColumns = taskTaskColumns;
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
title: '习总书记重要讲话指示批示情况',
api: list,
columns,
canResize: true,
resizeHeightOffset: 56,
useSearchForm: false,
isTreeTable: true,
actionColumn: {
width: 240,
fixed: 'right',
},
beforeFetch: async (params) => {
params.hasQuery = 'true';
return Object.assign(params, queryParam.value);
},
},
exportConfig: {
name: '习总书记重要讲话指示批示情况',
url: getExportXlsByEasyExcelUrl,
},
importConfig: {
url: getImportUrl,
success: success,
},
});
const [
registerTable,
{ reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource, setTableData },
{ rowSelection, selectedRowKeys },
] = tableContext;
const mainId = computed(() => (unref(selectedRowKeys).length > 0 ? unref(selectedRowKeys)[0] : ''));
provide('mainId', mainId);
const labelCol = reactive({
xs: 24,
sm: 8,
xl: 10,
xxl: 14,
});
const wrapperCol = reactive({
xs: 24,
sm: 16,
xl: 14,
xxl: 10,
});
// 高级查询配置
const superQueryConfig = reactive(superQuerySchema);
/**
* 高级查询事件
*/
function handleSuperQuery(params) {
Object.keys(params).map((k) => {
queryParam.value[k] = params[k];
});
reload();
fetchStatusStats();
}
async function fetchStatusStats() {
try {
const params: Recordable = {};
Object.keys(queryParam.value).forEach((key) => {
if (queryParam.value[key] !== undefined && queryParam.value[key] !== null && queryParam.value[key] !== '') {
params[key] = queryParam.value[key];
}
});
const res = await statusStats(params);
if (res) {
Object.assign(statsData, res);
}
} catch (e) {
// stats fetch failure is non-critical
}
}
onMounted(() => {
fetchStatusStats();
});
/**
* 新增事件
*/
function handleAdd() {
registerModal.value.disableSubmit = false;
registerModal.value.add();
}
/**
* 编辑事件
*/
async function handleEdit(record) {
registerModal.value.disableSubmit = false;
registerModal.value.edit(record);
}
/**
* 详情
*/
function handleDetail(record) {
detailModalRef.value?.open(record);
}
/**
* 删除事件(含关联流程)
*/
async function handleDeleteWithProcess(record) {
await withDrawXiSpeak({ id: record.id });
await deleteBgXiSpeak({ id: record.id }, success);
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteBgXiSpeak({ id: record.id }, success);
}
async function handleWithDraw(record) {
const res = await withDrawXiSpeak({ id: record.id });
if (res.success) {
createMessage.success(res.message || '删除流程成功');
reload();
fetchStatusStats();
} else {
createMessage.error(res.message || '删除流程失败');
}
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
const ids = selectedRowKeys.value.filter((item) => !item.includes('loadChild'));
await batchDeleteBgXiSpeak({ id: ids }, success);
}
/**
* 成功回调刷新页面
*/
function success() {
selectedRowKeys.value = [];
reload();
fetchStatusStats();
}
/**
* 成功回调
*/
async function handleSuccess({ isUpdate, values, expandedArr, changeParent }) {
if (isUpdate) {
if (changeParent) {
reload();
} else {
let data = await list({ id: values.id, pageSize: 1, pageNo: 1, pid: values['pid'] });
if (data && data.records && data.records.length > 0) {
updateTableDataRecord(values.id, data.records[0]);
} else {
updateTableDataRecord(values.id, values);
}
}
} else {
if (!values['id'] || !values['pid']) {
reload();
} else {
expandedRowKeys.value = [];
for (let key of unref(expandedArr)) {
await expandTreeNode(key);
}
}
}
fetchStatusStats();
}
/**
* 树节点展开合并
*/
async function handleExpand(expanded, record) {
if (expanded) {
expandedRowKeys.value.push(record.id);
if (record.children && record.children.length > 0 && record.children[0].isLoading) {
let result = await getChildList({ pid: record.id });
result = result.records ? result.records : result;
if (result && result.length > 0) {
record.children = getDataByResult(result);
await refreshFeedbackCounts(record.children);
// 触发表格重新渲染
updateTableDataRecord(record.id, { ...record });
} else {
record.children = null;
record.hasChild = '0';
}
}
} else {
let keyIndex = expandedRowKeys.value.indexOf(record.id);
if (keyIndex >= 0) {
expandedRowKeys.value.splice(keyIndex, 1);
}
}
}
/**
* 处理数据集,标记有子节点的记录
*/
function getDataByResult(result) {
if (result && result.length > 0) {
return result.map((item) => {
if (item['hasChild'] == '1') {
let loadChild = { id: item.id + '_loadChild', name: 'loading...', isLoading: true };
item.children = [loadChild];
}
return item;
});
}
}
/**
* 递归收集所有 ID(含子节点)
*/
function collectAllIds(records) {
const ids: string[] = [];
const walk = (list) => {
if (!list) return;
for (const item of list) {
if (item.id) ids.push(item.id);
if (item.children) walk(item.children);
}
};
walk(records);
return ids;
}
/**
* 批量查询反馈统计数量并更新到 records 中
*/
async function refreshFeedbackCounts(records) {
const ids = collectAllIds(records);
if (ids.length === 0) return;
try {
// 后端返回 Map<mainId, {total, closed}>,直接作为 countsMap 使用
const countsMap: Record<string, { total: number; closed: number }> =
(await queryFeedbackCounts({ ids: ids.join(',') })) || {};
const updateFn = (list) => {
if (!list) return;
for (const item of list) {
const counts = countsMap[item.id];
if (counts) {
item.implCount = counts.total;
item.closedCount = counts.closed;
}
if (item.children) updateFn(item.children);
}
};
updateFn(records);
} catch (e) {
// 静默处理查询异常
}
}
/**
* 接口请求成功后回调
*/
async function onFetchSuccess(result) {
getDataByResult(result.items) && loadDataByExpandedRows();
if (result.items && result.items.length > 0) {
await refreshFeedbackCounts(result.items);
// 触发表格重新渲染以显示更新后的 implCount / closedCount
setTableData([...getDataSource()]);
}
}
/**
* 根据已展开的行查询数据
*/
async function loadDataByExpandedRows() {
if (unref(expandedRowKeys).length > 0) {
const res = await getChildListBatch({ parentIds: unref(expandedRowKeys).join(',') });
if (res.success && res.result.records.length > 0) {
let records = res.result.records;
const listMap = new Map();
for (let item of records) {
let pid = item['pid'];
if (unref(expandedRowKeys).includes(pid)) {
let mapList = listMap.get(pid);
if (mapList == null) {
mapList = [];
}
mapList.push(item);
listMap.set(pid, mapList);
}
}
let childrenMap = listMap;
let fn = (list) => {
if (list) {
list.forEach((data) => {
if (unref(expandedRowKeys).includes(data.id)) {
data.children = getDataByResult(childrenMap.get(data.id));
fn(data.children);
}
});
}
};
fn(getDataSource());
}
}
}
/**
* 操作表格后处理树节点展开合并
*/
async function expandTreeNode(key) {
let record = findTableDataRecord(key);
if (record) {
expandedRowKeys.value.push(key);
let result = await getChildList({ pid: key });
result = result.records ? result.records : result;
if (result && result.length > 0) {
record.children = getDataByResult(result);
} else {
record.children = null;
record.hasChild = '0';
}
updateTableDataRecord(key, record);
}
}
/**
* 添加下级
*/
function handleAddSub(record) {
registerModal.value.disableSubmit = false;
registerModal.value.add(record);
}
/**
* 流程操作菜单
*/
function getFlowMenuList(record) {
const list: any[] = [];
if (record.bpmStatus == '1' || !record.bpmStatus) {
list.push({
text: '发起落实措施收集流程',
icon: 'ant-design:play-circle-outlined',
event: 'process',
onClick: handleProcess.bind(null, record),
});
list.push({
text: '发起经办人执行反馈流程',
icon: 'ant-design:play-circle-outlined',
event: 'processJbr',
onClick: handleProcessJbr.bind(null, record),
});
}
if (record.bpmStatus == '3') {
list.push({
text: '发起经办人执行反馈流程',
icon: 'ant-design:play-circle-outlined',
event: 'processJbr',
onClick: handleProcessJbr.bind(null, record),
});
}
if (record.bpmStatus == '2') {
list.push({
text: '再次发起经办人执行反馈流程',
icon: 'ant-design:play-circle-outlined',
event: 'processJbrAgain',
onClick: handleProcessJbr.bind(null, record),
});
}
list.push({
text: '查询督办流程',
icon: 'ant-design:search-outlined',
event: 'queryProcess',
divider: true,
onClick: handleQueryProcess.bind(null, record),
});
if (record.bpmStatus && record.bpmStatus !== '1') {
list.push({
text: '删除关联流程',
icon: 'ant-design:delete-outlined',
event: 'withdraw',
popConfirm: {
title: '确定删除该记录关联的所有运行中流程吗?',
confirm: handleWithDraw.bind(null, record),
placement: 'topLeft',
},
});
}
return list;
}
/**
* 表单操作菜单
*/
function getFormMenuList(record) {
return [
{
text: '查看办理情况',
icon: 'ant-design:eye-outlined',
event: 'feedback',
divider: true,
onClick: handleViewFeedback.bind(null, record),
},
{
text: '编辑',
icon: 'ant-design:edit-outlined',
event: 'edit',
onClick: handleEdit.bind(null, record),
},
{
text: '详情',
icon: 'ant-design:file-text-outlined',
event: 'detail',
onClick: handleDetail.bind(null, record),
},
// {
// text: '添加下级',
// icon: 'ant-design:plus-outlined',
// event: 'addSub',
// onClick: handleAddSub.bind(null, { pid: record.id }),
// },
{
text: '删除',
icon: 'ant-design:delete-outlined',
event: 'delete',
popConfirm: {
title: '确定删除吗? 将同时删除该记录关联的所有流程信息',
confirm: handleDeleteWithProcess.bind(null, record),
placement: 'topLeft',
},
},
];
}
function splitDeptValue(value) {
if (Array.isArray(value)) {
return value.map((item) => String(item || '').trim()).filter(Boolean);
}
return String(value || '')
.split(/[,\uFF0C]/)
.map((item) => item.trim())
.filter(Boolean);
}
function getImplDeptOptions(record) {
const deptIds = splitDeptValue(record?.implDept);
const deptNames = splitDeptValue(record?.implDept_dictText);
return deptIds.map((deptId, index) => ({
value: deptId,
label: deptNames[index] || deptId,
}));
}
function getLeadDeptOptions(record) {
const deptIds = splitDeptValue(record?.implDept);
const deptNames = splitDeptValue(record?.implDept_dictText);
return deptIds.map((deptId, index) => ({
value: deptId,
label: deptNames[index] || deptId,
}));
}
function getNextSuperviseCount(value) {
const count = Number(value || 0);
return Number.isFinite(count) ? count + 1 : 1;
}
function handleProcess(record) {
feedbackRightNowModalRef.value?.open(record);
}
function handleFeedbackRightNowConfirm({ record, feedbackRightNow }) {
pendingFeedbackRightNow.value = feedbackRightNow;
nextTick(() => {
flowScheduleModalRef.value?.open({
title: '发起落实措施收集流程',
payload: { record },
deptOptions: getLeadDeptOptions(record),
showSuoleaderFields: true,
isNeedAppro: record?.needSdwApproval,
supDeptleaderid: record?.sdwLeaderList,
supDeptleaderName: record?.sdwLeaderList_dictText,
});
});
}
function handleProcessJbr(record) {
flowScheduleModalRef.value?.open({
title: '发起落实部门经办人执行反馈流程',
payload: { record, flowCode: FLOW_CODE_JBR },
deptOptions: getImplDeptOptions(record),
});
}
function handleQueryProcess(record) {
currentBusinessId.value = record.id;
businessProcessListModalRef.value?.open({
title: '流程列表',
businessId: record.id,
columns: '',
});
}
async function handleFlowScheduleConfirm({ payload, options }) {
const record = payload?.record || {};
const flowCode = payload?.flowCode || FLOW_CODE;
const formatDate = (value, format) => {
return value ? dateUtil(value).format(format) : undefined;
};
const launchType = options.triggerType === 2 ? 1 : 0;
if (flowCode === FLOW_CODE) {
await updateLaunchType({ id: record.id, launchType });
}
const nextSuperviseCount = getNextSuperviseCount(record.supervisionCount);
const flowJsonData = {
...record,
isNeedAppro: options.isNeedAppro ?? record.needSdwApproval ?? '',
supDeptleaderid: options.supDeptleaderid ?? record.sdwLeaderList ?? '',
supervisionCount: nextSuperviseCount,
feedback_right_now: flowCode === FLOW_CODE ? pendingFeedbackRightNow.value : undefined,
};
if (flowCode === FLOW_CODE_JBR) {
const selectedDeptIds = options.deptIds || [];
if (selectedDeptIds.length === 0) {
createMessage.warning('请选择落实部门');
return;
}
let successCount = 0;
const total = selectedDeptIds.length;
for (const deptId of selectedDeptIds) {
const deptDetail = record.approveInfo?.[deptId];
const implUserNameList: string[] = deptDetail?.implUserNameList || [];
const deptHandlerName = implUserNameList.join(',');
const params = {
intervalType: options.intervalType,
startCount: options.startCount,
taskTask: {
triggerType: options.triggerType,
startTime: options.intervalStartTime,
flowCode: flowCode,
formUrl: FORM_URL,
flowName: '习总书记重要讲话指示批示-部门反馈执行情况',
jsonData: {
...flowJsonData,
currentDeptId: deptId,
},
businessTablename: BUSINESS_TABLE_NAME,
businessId: record.id,
title: record.speakStatus,
content: record.implStatus,
urgencyLevel: record.completionStatus,
deptId: deptId,
deptLeaderId: deptDetail?.approverId || '',
deptHandlerName: deptHandlerName,
requireFinishDate: formatDate(record.time, 'YYYY-MM-DD'),
deadline: record.deadline || formatDate(record.time, 'YYYY-MM-DD'),
},
};
try {
await startProcessSchedules(params);
successCount++;
} catch (e) {
createMessage.error(`部门 ${deptId} 流程发起失败`);
}
}
// 部分/全部失败时也要保存状态并刷新列表,避免已成功发起的流程成为无主流程
if (successCount > 0) {
await saveOrUpdateDict({ id: record.id, bpmStatus: '2', supervisionCount: nextSuperviseCount, completionStatus: 0 }, true);
}
if (successCount === total) {
createMessage.success(`成功发起 ${total} 个经办人执行反馈流程`);
} else if (successCount > 0) {
createMessage.warning(`部分部门流程发起失败:成功 ${successCount}/${total} 个`);
} else {
createMessage.error(`所有部门流程发起失败(共 ${total} 个)`);
}
reload();
fetchStatusStats();
return;
}
const params = {
intervalType: options.intervalType,
startCount: options.startCount,
taskTask: {
triggerType: options.triggerType,
startTime: options.intervalStartTime,
flowCode: flowCode,
formUrl: FORM_URL,
flowName: FLOW_NAME,
jsonData: flowJsonData,
businessTablename: BUSINESS_TABLE_NAME,
businessId: record.id,
title: record.speakStatus,
content: record.implStatus,
urgencyLevel: record.completionStatus,
deptId: options.deptId || record.implDept,
deptLeaderId: record.responsiblePerson,
requireFinishDate: formatDate(record.time, 'YYYY-MM-DD'),
deadline: record.deadline || formatDate(record.time, 'YYYY-MM-DD'),
},
};
await startProcessSchedules(params);
await saveOrUpdateDict({ id: record.id, bpmStatus: '2', supervisionCount: nextSuperviseCount, completionStatus: 0 }, true);
createMessage.success('发起成功');
reload();
fetchStatusStats();
}
async function handlePreviewPic(record) {
bpmPicModal(true, {
flowCode: FLOW_CODE,
dataId: record.id,
});
}
async function handleViewFeedback(record: Recordable) {
feedbackViewModalRef.value.open(record);
}
async function handleDownloadTemplate() {
const data = await defHttp.get(
{ url: getExportXlsHeadersUrl, params: {}, responseType: 'blob', timeout: 60000 },
{ isTransformResponse: false },
);
if (!data) {
createMessage.warning('文件下载失败');
return;
}
const blob = new Blob([data]);
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = '习总书记重要讲话指示批示情况-导入模板.xls';
link.click();
URL.revokeObjectURL(link.href);
}
async function handleImportByTemplate(data) {
const isReturn = (fileInfo) => {
try {
if (fileInfo.code === 500 || fileInfo.code === 510) {
if (fileInfo.result && Array.isArray(fileInfo.result) && fileInfo.result.length > 0) {
showImportValidationErrors(fileInfo.message || `${data.file.name} 导入失败`, fileInfo.result);
} else {
createMessage.error(fileInfo.message || `${data.file.name} 导入失败`);
}
} else {
createMessage.success(fileInfo.message || `${data.file.name} 导入成功`);
}
} catch {
// ignore
} finally {
reload();
}
};
await defHttp.uploadFile({ url: getImportExcelByEasyExcelUrl }, { file: data.file }, { success: isReturn });
}
async function handleCheckExcel(data) {
const isReturn = (fileInfo) => {
try {
if (fileInfo.code === 500 || fileInfo.code === 510) {
if (fileInfo.result && Array.isArray(fileInfo.result) && fileInfo.result.length > 0) {
showImportValidationErrors(fileInfo.message || `${data.file.name} 校验失败`, fileInfo.result);
} else {
createMessage.error(fileInfo.message || `${data.file.name} 校验失败`);
}
} else {
createMessage.success(fileInfo.message || `${data.file.name} 校验通过`);
}
} catch {
// ignore
}
};
await defHttp.uploadFile({ url: getCheckExcelByEasyExcelUrl }, { file: data.file }, { success: isReturn });
}
function clearQueryParams() {
Object.keys(queryParam.value).forEach((key) => {
delete queryParam.value[key];
});
}
/**
* 查询
*/
function searchQuery() {
reload({ page: 1 });
fetchStatusStats();
}
// 输入框逐字触发的查询做防抖,查询按钮仍走未防抖的 searchQuery
const debouncedSearchQuery = useDebounceFn(searchQuery, 500);
/**
* 重置
*/
function searchReset() {
formRef.value?.resetFields();
clearQueryParams();
bpmStatusFilter.value = undefined;
selectedImplDept.value = [];
selectedRowKeys.value = [];
reload({ page: 1 });
fetchStatusStats();
}
function handleImplDeptChange(values) {
if (values && values.length > 0) {
queryParam.value.implDept = values.join(',');
} else {
delete queryParam.value.implDept;
}
reload({ page: 1 });
fetchStatusStats();
}
function handleBpmStatusChange(value) {
if (value != null) {
queryParam.value.bpmStatus = value;
} else {
delete queryParam.value.bpmStatus;
}
reload({ page: 1 });
fetchStatusStats();
}
</script>
<style lang="less" scoped>
.jeecg-basic-table-form-container {
padding: 0;
.table-page-search-submitButtons {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust{
min-width: 100px !important;
}
.query-group-split-cust{
width: 30px;
display: inline-block;
text-align: center;
}
.ant-form-item:not(.ant-form-item-with-help) {
margin-bottom: 16px;
height: 32px;
}
:deep(.ant-picker),
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-form-item-label > label) {
overflow: visible;
}
}
.erpNativeList {
height: 100%;
.content {
background-color: #fff;
// 内容区高度由表格自适应撑开,避免覆盖页面底部 footer。
height: auto;
}
}
:deep(.status-row-1 td) {
background: #fffbe6 !important;
}
:deep(.status-row-2 td) {
background: #fff1f0 !important;
}
:deep(.status-row-3 td) {
background: #f6ffed !important;
}
</style>