Files
supervision-test-frontend-repo/src/views/dq/inspectTask/DqInspectTaskList.vue
T
wsm 9dfba2bca8 !149 chore(docs): 忽略 localDocs 本地文档目录
* chore(docs): 忽略 localDocs 本地文档目录
* fix(inspectTask): 发起销号流程入口常显并移除状态调整菜单
* fix(xispeak): 督办状态结合反馈闭环实时推导
2026-08-12 10:16:07 +00:00

650 lines
22 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">
<!--查询区域-->
<div class="jeecg-basic-table-form-container">
<a-form ref="formRef" @keyup.enter="reload" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row :gutter="24">
<a-col :xl="6" :lg="6" :md="8" :sm="12">
<a-form-item label="整改措施" name="improveMeasure">
<a-input v-model:value="queryParam.improveMeasure" placeholder="请输入整改措施" allow-clear @pressEnter="reload" @change="reload" />
</a-form-item>
</a-col>
<a-col :xl="6" :lg="6" :md="8" :sm="12">
<a-form-item label="分管所领导" name="chargeLeaderId">
<DeptRoleUserSelectDropDown v-model:value="queryParam.chargeLeaderId" :deptId="PARTY_COMMITTEE_DEPT_ID" :roleId="CHARGE_LEADER_ROLE_ID" placeholder="请选择分管所领导" allow-clear style="width: 100%" @change="reload" />
</a-form-item>
</a-col>
<a-col :xl="6" :lg="6" :md="8" :sm="12">
<a-form-item label="措施责任部门" name="measureResDept">
<SzSelectDept v-model:value="queryParam.measureResDept" :multiple="true" checkStrictly allow-clear @change="reload" />
</a-form-item>
</a-col>
<a-col :xl="6" :lg="6" :md="8" :sm="12">
<a-form-item name="bpmStatus" label="督办状态">
<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-form-item>
</a-col>
<a-col :xl="6" :lg="6" :md="8" :sm="12">
<span class="table-page-search-submitButtons">
<a-button type="primary" preIcon="ant-design:search-outlined" @click="reload">查询</a-button>
<a-button preIcon="ant-design:reload-outlined" style="margin-left: 8px" @click="searchReset">重置</a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection" @fetch-success="onFetchSuccess" :rowClassName="(r) => r.bpmStatus ? 'status-row-' + r.bpmStatus : ''">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" v-auth="'dqinspecttask:dq_inspect_task:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" v-auth="'dqinspecttask:dq_inspect_task:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">
导出</a-button
>
<j-upload-button
type="primary"
v-auth="'dqinspecttask:dq_inspect_task:importExcel'"
preIcon="ant-design:import-outlined"
@click="onImportXls"
>导入</j-upload-button
>
<a-button
type="primary"
v-auth="'dqinspecttask:dq_inspect_task:exportXls'"
preIcon="ant-design:download-outlined"
@click="handleDownloadTemplate"
>下载模板</a-button
>
<j-upload-button
type="primary"
v-auth="'dqinspecttask:dq_inspect_task:importExcel'"
preIcon="ant-design:check-outlined"
@click="handleCheckExcel"
>模板校验</j-upload-button
>
<j-upload-button
type="primary"
v-auth="'dqinspecttask:dq_inspect_task:importExcel'"
preIcon="ant-design:import-outlined"
@click="handleImportByTemplate"
>模板导入</j-upload-button
>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button v-auth="'dqinspecttask:dq_inspect_task:deleteBatch'"
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
<!-- 高级查询 -->
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
<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 === 'improveMeasure'">
<a-tooltip title="点击查看详情">
<a style="font-weight: 500" @click="handleDetail(record)">{{ text }}</a>
</a-tooltip>
</template>
</template>
</BasicTable>
<!-- 表单区域 -->
<DqInspectProgressFeedbackViewModal ref="feedbackViewModalRef" />
<DqInspectTaskModal @register="registerModal" @success="handleSuccess"></DqInspectTaskModal>
<!-- 审批记录 -->
<BpmPictureModal @register="registerBpmModal" />
<FlowScheduleStartModal ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
<BusinessProcessListModal ref="businessProcessListModalRef" />
</div>
</template>
<script lang="ts" name="dqinspecttask-dqInspectTask" setup>
import { ref, reactive, computed, unref, onMounted } from 'vue';
import { BasicTable, useTable } from '/@/components/Table';
import { Dropdown } from '/@/components/Dropdown';
import { useListPage } from '/@/hooks/system/useListPage';
import { useModal } from '/@/components/Modal';
import DqInspectTaskModal from './components/DqInspectTaskModal.vue';
import DqInspectProgressFeedbackViewModal from '../inspectProgress/components/DqInspectProgressFeedbackViewModal.vue';
import { ledgerColumns, superQuerySchema } from './DqInspectTask.data';
import {
ledgerList,
deleteOne,
batchDelete,
getImportUrl,
getExportUrl,
getImportExcelByEasyExcelUrl,
getCheckExcelByEasyExcelUrl,
getExportXlsHeadersUrl,
saveOrUpdate,
statusStats,
} from './DqInspectTask.api';
import { defHttp } from '/@/utils/http/axios';
import { showImportValidationErrors } from '/@/utils/helper/importError';
import { downloadFile } from '/@/utils/common/renderUtils';
import { useMessage } from '/@/hooks/web/useMessage';
import Icon from '/@/components/Icon/index';
import SzSelectDept from '/@/components/Form/src/jeecg/components/SzSelectDept.vue';
import DeptRoleUserSelectDropDown from '/@/components/semri/userComponent/DeptRoleUserSelectDropDown.vue';
import { PARTY_COMMITTEE_DEPT_ID, CHARGE_LEADER_ROLE_ID } from '/@/constant/supervision';
import { startProcessSchedules } from '/@/api/common/api';
import { dateUtil } from '/@/utils/dateUtil';
import FlowScheduleStartModal from './components/DqInspectTaskFlowScheduleStartModal.vue';
import BusinessProcessListModal from '/src/components/semri/process/BusinessProcessListModal.vue';
const [registerBpmModal, { openModal: bpmPicModal }] = useModal();
import { useUserStore } from '/@/store/modules/user';
const formRef = ref();
const queryParam = reactive<any>({});
const statsData = reactive({ notStarted: 0, inProgress: 0, overdue: 0, completed: 0 });
const checkedKeys = ref<Array<string | number>>([]);
//注册model
const [registerModal, { openModal }] = useModal();
const feedbackViewModalRef = ref();
const flowScheduleModalRef = ref();
const businessProcessListModalRef = ref();
const bpmStatusFilter = ref();
const userStore = useUserStore();
const { createMessage } = useMessage();
//注册table数据
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
title: 'dq_inspect_task',
api: ledgerList,
columns: ledgerColumns,
canResize: false,
useSearchForm: false,
pagination: {
current: 1,
pageSize: 12,
pageSizeOptions: ['12', '24', '36'],
},
actionColumn: {
width: 240,
fixed: 'right',
},
beforeFetch: async (params) => {
return Object.assign(params, queryParam);
},
afterFetch: async (items) => {
fetchStatusStats();
return calcLedgerRowSpan(items);
},
},
exportConfig: {
name: 'dq_inspect_task',
url: getExportUrl,
params: queryParam,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});
const [registerTable, { reload, setTableData, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
// 高级查询配置
const superQueryConfig = reactive(superQuerySchema);
/**
* 高级查询事件
*/
function handleSuperQuery(params) {
Object.keys(params).map((k) => {
queryParam[k] = params[k];
});
reload();
}
async function fetchStatusStats() {
try {
const params: Recordable = {};
Object.keys(queryParam).forEach((key) => {
if (queryParam[key] !== undefined && queryParam[key] !== null && queryParam[key] !== '') {
params[key] = queryParam[key];
}
});
const res = await statusStats(params);
if (res) {
Object.assign(statsData, res);
}
} catch (e) {
console.error('【巡视整改-状态统计】获取状态统计数据失败', e);
}
}
onMounted(() => {
fetchStatusStats();
});
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
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 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 });
}
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 });
}
/**
* 只在当前页内计算台账合并行,避免跨页合并导致表格分页行为不可控。
*/
function calcLedgerRowSpan(items) {
if (!items || items.length === 0) {
return items;
}
calcGroupRowSpan(items, (item) => item.categoryId || item.categoryName, '_categoryRowSpan');
calcGroupRowSpan(items, (item) => `${item.categoryId || item.categoryName}_${item.surfaceId || item.surfaceName}`, '_surfaceRowSpan');
calcGroupRowSpan(
items,
(item) => `${item.categoryId || item.categoryName}_${item.surfaceId || item.surfaceName}_${item.specificId || item.specificName}`,
'_specificRowSpan'
);
return items;
}
function calcGroupRowSpan(items, getKey, field) {
let start = 0;
while (start < items.length) {
const key = getKey(items[start]);
let end = start + 1;
while (end < items.length && getKey(items[end]) === key) {
end++;
}
const span = end - start;
items[start][field] = span;
for (let i = start + 1; i < end; i++) {
items[i][field] = 0;
}
start = end;
}
}
/**
* 流程操作菜单
*/
function getFlowMenuList(record) {
const list: any[] = [];
if ((record.bpmStatus == '1' || !record.bpmStatus) && record.completedStage === 0) {
list.push({ text: '发起巡视整改流程', icon: 'ant-design:play-circle-outlined', event: 'process', onClick: handleProcess.bind(null, record) });
}
list.push({ text: '发起销号流程', icon: 'ant-design:stop-outlined', event: 'deleteProcess', onClick: handleDeleteProcess.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:rollback-outlined',
// event: 'adj0',
// onClick: () => adjustCompletedStage(record, 0),
// });
// list.push({
// text: '调整为未发起销号流程状态',
// icon: 'ant-design:check-circle-outlined',
// event: 'adj2',
// onClick: () => adjustCompletedStage(record, 2),
// });
// list.push({
// text: '调整为已完成销号流程状态',
// icon: 'ant-design:check-circle-outlined',
// event: 'adj4',
// onClick: () => adjustCompletedStage(record, 4),
// });
// }
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:delete-outlined',
event: 'delete',
popConfirm: { title: '是否确认删除', confirm: handleDelete.bind(null, record), placement: 'topLeft' },
},
];
}
async function adjustCompletedStage(record, stage) {
await saveOrUpdate({ id: record.id, problemId: record.problemId, completedStage: stage }, true);
handleSuccess();
}
const FLOW_CODE = 'inspect_flow_create_01';
const FLOW_NAME = '巡视整改提升工作';
const BUSINESS_TABLE_NAME = 'dq_inspect_task';
const FORM_URL = 'dq/inspectTask/components/DqInspectTaskBPMForm?showFeedbackInfo=1';
const DELETE_FLOW_CODE = 'dq_delete_01';
const DELETE_FLOW_NAME = '巡视整改提升工作-销号';
const DELETE_FORM_URL = 'dq/inspectTask/components/DqInspectTaskBPMForm?showFeedbackInfo=1';
const currentScheduleFlowCode = ref('');
/**
* 发起流程
*/
function handleProcess(record) {
currentScheduleFlowCode.value = FLOW_CODE;
flowScheduleModalRef.value?.open({
title: '发起整改任务流程',
payload: { record },
deptOptions: getDeptOptions(record),
showSuoleaderFields: true,
isNeedAppro: record.isNeedAppro,
supDeptleaderid: record.supDeptleaderid,
supDeptleaderName: record.supDeptleaderid_dictText,
});
}
/**
* 发起销号流程
*/
function handleDeleteProcess(record) {
currentScheduleFlowCode.value = DELETE_FLOW_CODE;
flowScheduleModalRef.value?.open({
title: '发起销号流程',
payload: { record },
deptOptions: getDeptOptions(record),
showSuoleaderFields: true,
isNeedAppro: record.isNeedAppro,
supDeptleaderid: record.supDeptleaderid,
supDeptleaderName: record.supDeptleaderid_dictText,
});
}
/**
* 流程调度确认
*/
async function handleFlowScheduleConfirm({ payload, options }) {
const record = payload?.record || {};
const isDeleteFlow = currentScheduleFlowCode.value === DELETE_FLOW_CODE;
const flowCode = isDeleteFlow ? DELETE_FLOW_CODE : FLOW_CODE;
const flowName = isDeleteFlow ? DELETE_FLOW_NAME : FLOW_NAME;
const formUrl = isDeleteFlow ? DELETE_FORM_URL : FORM_URL;
const params = {
intervalType: options.intervalType,
startCount: options.startCount,
taskTask: {
triggerType: options.triggerType,
startTime: options.intervalStartTime,
flowCode,
formUrl,
flowName,
jsonData: { ...record },
businessTablename: BUSINESS_TABLE_NAME,
businessId: record.id,
title: record.improveMeasure || '',
content: record.improveMeasure || '',
deptId: options.deptId || record.measureResDept || '',
deptLeaderId: record.measureResLeader || '',
requireFinishDate: record.deadline || undefined,
deadline: record.deadline || undefined,
},
};
await startProcessSchedules(params);
const nextStage = (record.completedStage || 0) + 1;
await saveOrUpdate({ id: record.id, problemId: record.problemId, bpmStatus: '2', completedStage: nextStage }, true);
createMessage.success('发起成功');
handleSuccess();
}
/**
* 查询督办流程
*/
function handleQueryProcess(record) {
businessProcessListModalRef.value?.open({
title: '流程列表',
businessId: record.id,
columns: '',
});
}
/**
* 解析措施责任部门选项
*/
function getDeptOptions(record) {
const deptIds = String(record.measureResDept || '')
.split(/[,]/)
.map((s) => s.trim())
.filter(Boolean);
const deptNames = String(record.measureResDept_dictText || '')
.split(/[,]/)
.map((s) => s.trim())
.filter(Boolean);
return deptIds.map((deptId, index) => ({
value: deptId,
label: deptNames[index] || deptId,
}));
}
/**
* 查看反馈
*/
function handleViewFeedback(record) {
feedbackViewModalRef.value.open(record.id);
}
/**
* 审批进度
*/
async function handlePreviewPic(record) {
bpmPicModal(true, {
flowCode: 'inspect_flow_create_01',
dataId: record.id,
});
}
/* ----------------------以下为原生查询需要添加的-------------------------- */
const labelCol = reactive({
xs: 24,
sm: 4,
xl: 6,
xxl: 9,
});
const wrapperCol = reactive({
xs: 24,
sm: 20,
});
/**
* 重置
*/
function searchReset() {
formRef.value.resetFields();
selectedRowKeys.value = [];
bpmStatusFilter.value = undefined;
delete queryParam.bpmStatus;
//刷新数据
reload();
}
/**
* form点击事件(以逗号分割)
* @param key
* @param value
*/
function handleBpmStatusChange(value) {
if (value != null) {
queryParam.bpmStatus = value;
} else {
delete queryParam.bpmStatus;
}
reload();
}
async function onFetchSuccess(result) {
if (result?.items?.length > 0) {
setTableData([...getDataSource()]);
}
}
</script>
<style lang="less" scoped>
.jeecg-basic-table-form-container {
padding: 0;
.table-page-search-submitButtons {
display: block;
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(.status-row-1 td) {
background: #fffbe6 !important;
}
:deep(.status-row-2 td) {
background: #fff1f0 !important;
}
:deep(.status-row-3 td) {
background: #f6ffed !important;
}
</style>