This commit is contained in:
吴红兵
2025-12-02 10:37:49 +08:00
commit 1f645dad3e
1183 changed files with 147673 additions and 0 deletions

View File

@@ -0,0 +1,239 @@
<template>
<el-dialog :title="title" v-model="visible" width="60%"
:close-on-click-modal="false" draggable>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="140px" v-loading="loading" :disabled="operType === 'view'">
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item :label="$t('runNode.flowInstId')" prop="flowInstId" >
<el-input v-model="form.flowInstId" :placeholder="t('runNode.inputFlowInstIdTip')" disabled/>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.defFlowId')" prop="defFlowId">
<el-select v-model="form.defFlowId" :placeholder="t('runNode.inputDefFlowIdTip')" clearable filterable disabled>
<el-option v-for="(item, index) in cascadeDic.defFlowId" :key="index" :label="item.flowName" :value="item.defFlowId"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.nodeName')" prop="nodeName">
<el-input v-model="form.nodeName" :placeholder="t('runNode.inputNodeNameTip')"/>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.startTime')" prop="startTime">
<el-date-picker type="datetime" :placeholder="t('runNode.inputStartTimeTip')" v-model="form.startTime" :value-format="dateTimeStr"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.endTime')" prop="endTime">
<el-date-picker type="datetime" :placeholder="t('runNode.inputEndTimeTip')" v-model="form.endTime" :value-format="dateTimeStr"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.nodeType')" prop="nodeType">
<el-select v-model="form.nodeType" :placeholder="t('runNode.inputNodeTypeTip')" clearable filterable>
<el-option v-for="(item, index) in DIC_PROP.NODE_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.nodeApproveMethod')" prop="nodeApproveMethod">
<el-select v-model="form.nodeApproveMethod" :placeholder="t('runNode.inputNodeApproveMethodTip')">
<el-option v-for="(item, index) in DIC_PROP.NODE_APPROVE_METHOD" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.rejectType')" prop="rejectType">
<el-radio-group v-model="form.rejectType">
<el-radio v-for="(item, index) in DIC_PROP.REJECT_TYPE" :key="index" :label="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.timeout')" prop="timeout">
<el-input-number :min="1" :max="1000" v-model="form.timeout" :placeholder="t('runNode.inputTimeoutTip')"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.status')" prop="status">
<el-select disabled v-model="form.status" :placeholder="t('runNode.inputStatusTip')" clearable filterable>
<el-option v-for="(item, index) in DIC_PROP.NODE_STATUS" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.isAutoNext')" prop="isAutoNext">
<el-radio-group v-model="form.isAutoNext">
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value">
{{ getLabelByLanguage(item) }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.isContinue')" prop="isContinue">
<el-radio-group v-model="form.isContinue">
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value">
{{ getLabelByLanguage(item) }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.suspension')" prop="suspension">
<el-radio-group v-model="form.suspension">
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value">
{{ getLabelByLanguage(item) }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item :label="t('runNode.sort')" prop="sort">
<el-input-number :min="1" :max="1000" v-model="form.sort" :placeholder="t('runNode.inputSortTip')"></el-input-number>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer v-if="operType !== 'view'">
<span class="dialog-footer">
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="RunNodeDialog">
import { useMessage } from "/@/hooks/message";
import { getObj, addObj, putObj } from '/@/api/jsonflow/run-node'
import { useI18n } from "vue-i18n"
import { rule } from '/@/utils/validate';
import {onCascadeChange, onLoadDicUrl} from "/@/flow/components/convert-name/convert";
const emit = defineEmits(['refresh']);
const { t } = useI18n();
// 定义变量内容
const dataFormRef = ref();
const visible = ref(false);
const loading = ref(false);
const operType = ref(false);
const title = ref('');
// 定义字典
const cascadeDic = reactive({});
const onCascade = onCascadeChange(cascadeDic, {key: "flowInstId", cascades: ["defFlowId"]});
function cascadeChange(key, cascades){
onCascade(form, {key: key, cascades: cascades});
}
// 提交表单数据
const form = reactive({
sort: 0,
defFlowId: '',
flowNodeId: '',
startTime: '',
endTime: '',
nodeType: '',
nodeApproveMethod: '',
rejectType: '',
timeout: 0,
status: '',
isAutoNext: '',
isContinue: '',
suspension: '',
});
// 定义校验规则
const dataRules = ref({
defFlowId: [{required: true, message: '流程名称不能为空', trigger: 'blur'}],
flowNodeId: [{required: true, message: '节点名称不能为空', trigger: 'blur'}],
nodeType: [{required: true, message: '节点类型不能为空', trigger: 'blur'}],
nodeApproveMethod: [{required: true, message: '多节点审批方式不能为空', trigger: 'blur'}],
rejectType: [{required: true, message: '被驳回后再次提交时不能为空', trigger: 'blur'}],
status: [{required: true, message: '节点状态不能为空', trigger: 'blur'}],
isAutoNext: [{required: true, message: '是否自动流转下一节点不能为空', trigger: 'blur'}],
isContinue: [{required: true, message: '是否继续下一节点不能为空', trigger: 'blur'}],
})
// 打开弹窗
const openDialog = (type: string, id: string) => {
visible.value = true
operType.value = type;
form.id = ''
if (type === 'add') {
title.value = t('common.addBtn');
} else if (type === 'edit') {
title.value = t('common.editBtn');
} else if (type === 'view') {
title.value = t('common.viewBtn');
}
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields();
});
// 获取RunNode信息
if (id) {
form.id = id
getRunNodeData(id)
}
};
// 提交
const onSubmit = async () => {
const valid = await dataFormRef.value.validate().catch(() => {});
if (!valid) return false;
try {
loading.value = true;
form.id ? await putObj(form) : await addObj(form);
useMessage().success(t(form.id ? 'common.editSuccessText' : 'common.addSuccessText'));
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg);
} finally {
loading.value = false;
}
};
// 初始化表单数据
const getRunNodeData = (id: string) => {
// 获取数据
loading.value = true
getObj(id).then((res: any) => {
Object.assign(form, res.data)
onCascade(form);
}).finally(() => {
loading.value = false
})
};
// 暴露变量
defineExpose({
openDialog
});
</script>

View File

@@ -0,0 +1,50 @@
export default {
runNode: {
index: '#',
importrunNodeTip: 'import RunNode',
id: 'id',
sort: 'sort',
defFlowId: 'defFlowId',
flowNodeId: 'flowNodeId',
startTime: 'startTime',
endTime: 'endTime',
nodeType: 'nodeType',
nodeName: 'nodeName',
subDefFlowId: 'subDefFlowId',
subFlowVersion: 'subFlowVersion',
nodeApproveMethod: 'nodeApproveMethod',
rejectType: 'rejectType',
timeout: 'timeout',
status: 'status',
isAutoNext: 'isAutoNext',
isContinue: 'isContinue',
suspension: 'suspension',
createUser: 'createUser',
createTime: 'createTime',
flowInstId: 'flowInstId',
flowKey: 'flowKey',
inputIdTip: 'input id',
inputSortTip: 'input sort',
inputDefFlowIdTip: 'input defFlowId',
inputFlowNodeIdTip: 'input flowNodeId',
inputStartTimeTip: 'input startTime',
inputEndTimeTip: 'input endTime',
inputNodeTypeTip: 'input nodeType',
inputNodeNameTip: 'input nodeName',
inputSubDefFlowIdTip: 'input subDefFlowId',
inputSubFlowVersionTip: 'input subFlowVersion',
inputNodeApproveMethodTip: 'input nodeApproveMethod',
inputRejectTypeTip: 'input rejectType',
inputTimeoutTip: 'input timeout',
inputStatusTip: 'input status',
inputIsAutoNextTip: 'input isAutoNext',
inputIsContinueTip: 'input isContinue',
inputSuspensionTip: 'input suspension',
inputCreateUserTip: 'input createUser',
inputCreateTimeTip: 'input createTime',
inputFlowInstIdTip: 'input flowInstId',
inputFlowKeyTip: 'input flowKey',
}
}

View File

@@ -0,0 +1,50 @@
export default {
runNode: {
index: '#',
importrunNodeTip: '导入运行节点管理',
id: '节点名称',
sort: '排序值',
defFlowId: '流程名称',
flowNodeId: '节点名称',
startTime: '开始时间',
endTime: '结束时间',
nodeType: '节点类型',
nodeName: '节点名称',
subDefFlowId: '关联子流程',
subFlowVersion: '子流程版本',
nodeApproveMethod: '多节点审批方式',
rejectType: '被驳回后再次提交时',
timeout: '节点时限(分钟)',
status: '节点状态',
isAutoNext: '是否自动流转下一节点',
isContinue: '是否继续下一节点',
suspension: '是否挂起',
createUser: '创建人',
createTime: '创建时间',
flowInstId: '流程实例ID',
flowKey: '业务KEY',
inputIdTip: '请输入节点名称',
inputSortTip: '请输入排序值',
inputDefFlowIdTip: '请输入流程名称',
inputFlowNodeIdTip: '请输入节点名称',
inputStartTimeTip: '请输入开始时间',
inputEndTimeTip: '请输入结束时间',
inputNodeTypeTip: '请输入节点类型',
inputNodeNameTip: '请输入节点名称',
inputSubDefFlowIdTip: '请输入关联子流程',
inputSubFlowVersionTip: '请输入子流程版本',
inputNodeApproveMethodTip: '请输入多节点审批方式',
inputRejectTypeTip: '请输入被驳回后再次提交时',
inputTimeoutTip: '请输入任务时限',
inputStatusTip: '请输入节点状态',
inputIsAutoNextTip: '请输入是否自动流转下一节点',
inputIsContinueTip: '请输入是否继续下一节点',
inputSuspensionTip: '请输入是否挂起',
inputCreateUserTip: '请输入创建人',
inputCreateTimeTip: '请输入创建时间',
inputFlowInstIdTip: '请输入流程实例ID',
inputFlowKeyTip: '请输入业务KEY',
}
}

View File

@@ -0,0 +1,247 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<el-row v-show="showSearch">
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
<el-form-item :label="$t('runNode.flowInstId')" prop="flowInstId" >
<el-input :placeholder="t('runNode.inputFlowInstIdTip')" v-model="state.queryForm.flowInstId"
@change="cascadeChange('flowInstId', ['runNodeId'])"
style="max-width: 180px" />
</el-form-item>
<el-form-item :label="$t('runNode.id')" prop="id" >
<el-select v-model="state.queryForm.id" :placeholder="t('runNode.inputIdTip')" clearable filterable style="max-width: 180px">
<el-option v-for="(item, index) in cascadeDic.runNodeId" :key="index" :label="item.nodeName" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('runNode.status')" prop="status" >
<el-select v-model="state.queryForm.status" :placeholder="t('runNode.inputStatusTip')" clearable filterable style="max-width: 180px">
<el-option v-for="(item, index) in DIC_PROP.NODE_STATUS" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button icon="search" type="primary" @click="getDataList">
{{ $t('common.queryBtn') }}
</el-button>
<el-button icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
</el-form-item>
</el-form>
</el-row>
<el-row>
<div class="mb8" style="width: 100%">
<el-tooltip placement="top">
<template #content>
{{ $t('common.addBtn') }}
</template>
<el-button icon="Plus" type="primary" class="ml10" @click="formDialogRef.openDialog('add')"
v-auth="'jsonflow_runnode_add'">
</el-button>
</el-tooltip>
<el-tooltip placement="top">
<template #content>
{{ $t('common.delBtn') }}
</template>
<el-button plain :disabled="multiple" icon="Delete" type="primary" class="ml10"
v-auth="'jsonflow_runnode_del'" @click="handleDelete(selectObjs)">
</el-button>
</el-tooltip>
<right-toolbar v-model:showSearch="showSearch" :export="'jsonflow_runnode_export'"
class="ml10" style="float: right;margin-right: 20px"
@queryTable="getDataList"></right-toolbar>
</div>
</el-row>
<el-table :data="state.dataList" v-loading="state.loading" style="width: 100%"
@selection-change="handleSelectionChange" @sort-change="sortChangeHandle">
<el-table-column type="selection" width="40" align="center" />
<el-table-column type="index" :label="t('runNode.index')" width="40" />
<el-table-column prop="sort" :label="t('runNode.sort')" show-overflow-tooltip/>
<el-table-column prop="flowInstId" :label="t('runNode.flowInstId')" show-overflow-tooltip/>
<el-table-column prop="defFlowId" :label="t('runNode.defFlowId')" show-overflow-tooltip>
<template #default="scope">
<convert-name :options="state.dicData.flowInstId" :value="scope.row.defFlowId"
:valueKey="'defFlowId'" :showKey="'flowName'"></convert-name>
</template>
</el-table-column>
<el-table-column prop="nodeName" :label="t('runNode.nodeName')" show-overflow-tooltip/>
<el-table-column prop="startTime" :label="t('runNode.startTime')" show-overflow-tooltip/>
<el-table-column prop="endTime" :label="t('runNode.endTime')" show-overflow-tooltip/>
<el-table-column prop="nodeType" :label="t('runNode.nodeType')" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="DIC_PROP.NODE_TYPE" :value="scope.row.nodeType"></dict-tag>
</template>
</el-table-column>
<el-table-column prop="subDefFlowId" :label="t('runNode.subDefFlowId')" show-overflow-tooltip>
<template #default="scope">
<convert-name :options="dicData.defFlowId" :value="scope.row.subDefFlowId"
:valueKey="'id'" :showKey="'flowName'"></convert-name>
</template>
</el-table-column>
<el-table-column prop="nodeApproveMethod" :label="t('runNode.nodeApproveMethod')" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="DIC_PROP.NODE_APPROVE_METHOD" :value="scope.row.nodeApproveMethod"></dict-tag>
</template>
</el-table-column>
<!-- <el-table-column prop="rejectType" :label="t('runNode.rejectType')" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="DIC_PROP.REJECT_TYPE" :value="scope.row.rejectType"></dict-tag>
</template>
</el-table-column>-->
<el-table-column prop="timeout" :label="t('runNode.timeout')" show-overflow-tooltip/>
<el-table-column prop="status" :label="t('runNode.status')" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="DIC_PROP.NODE_STATUS" :value="scope.row.status"></dict-tag>
</template>
</el-table-column>
<!-- <el-table-column prop="isAutoNext" :label="t('runNode.isAutoNext')" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="DIC_PROP.YES_OR_NO" :value="scope.row.isAutoNext"></dict-tag>
</template>
</el-table-column>
<el-table-column prop="isContinue" :label="t('runNode.isContinue')" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="DIC_PROP.YES_OR_NO" :value="scope.row.isContinue"></dict-tag>
</template>
</el-table-column>-->
<el-table-column prop="suspension" :label="t('runNode.suspension')" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="DIC_PROP.YES_OR_NO" :value="scope.row.suspension"></dict-tag>
</template>
</el-table-column>
<!-- <el-table-column prop="createUser" :label="t('runNode.createUser')" show-overflow-tooltip>
<template #default="scope">
<convert-name :options="state.dicData.createUser" :value="scope.row.createUser"
:valueKey="'userId'" :showKey="'name'"></convert-name>
</template>
</el-table-column>
<el-table-column prop="createTime" :label="t('runNode.createTime')" show-overflow-tooltip/>-->
<el-table-column :label="$t('common.action')" width="120">
<template #default="scope">
<el-tooltip placement="top">
<template #content>
{{ $t('common.viewBtn') }}
</template>
<el-button text type="primary" icon="view" @click="formDialogRef.openDialog('view', scope.row.id)">
</el-button>
</el-tooltip>
<el-tooltip placement="top">
<template #content>
{{ $t('common.editBtn') }}
</template>
<el-button icon="edit-pen" text type="primary" @click="formDialogRef.openDialog('edit', scope.row.id)">
</el-button>
</el-tooltip>
<el-tooltip placement="top">
<template #content>
{{ $t('common.delBtn') }}
</template>
<el-button icon="delete" text type="primary" @click="handleDelete([scope.row.id])">
</el-button>
</el-tooltip>
<el-tooltip placement="top" v-if="scope.row.status ==='0' || scope.row.status ==='9'">
<template #content>
催办节点
</template>
<el-button icon="Bell" text type="primary" @click="remind(scope.row, '0')">
</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
<!-- 编辑新增 -->
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
</div>
</template>
<script setup lang="ts" name="systemRunNode">
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObjs } from "/@/api/jsonflow/run-node";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { useI18n } from "vue-i18n";
import {onCascadeChange, onLoadDicUrl, onLoaded} from "/@/flow/components/convert-name/convert";
import * as runNode from "/@/api/jsonflow/run-node";
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const { t } = useI18n()
const $message = useMessage();
// 定义查询字典
const dicData = reactive({});
const cascadeDic = reactive({});
const onLoad = onLoadDicUrl({key: "defFlowId"});
const onCascade = onCascadeChange(cascadeDic, {key: "flowInstId", cascades: ["runNodeId"]});
onMounted(() => {
onLoad(dicData);
});
function cascadeChange(key, cascades){
onCascade(state.queryForm, {key: key, cascades: cascades});
}
// 定义变量内容
const formDialogRef = ref()
// 搜索变量
const queryRef = ref()
const showSearch = ref(true)
// 多选变量
const selectObjs = ref([]) as any
const multiple = ref(true)
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {},
pageList: fetchList,
onLoaded: onLoaded({key: "createUser"}, {key: "flowInstId"}),
descs: ["create_time"]
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
downBlobFile
} = useTable(state)
// 清空搜索条件
const resetQuery = () => {
// 清空搜索条件
queryRef.value?.resetFields()
// 清空多选
selectObjs.value = []
getDataList()
}
// 多选事件
const handleSelectionChange = (objs: any) => {
selectObjs.value = objs.map(({ id }) => id);
multiple.value = !objs.length;
};
// 删除操作
const handleDelete = async (ids: string[]) => {
try {
await useMessageBox().confirm(t('common.delConfirmText'));
} catch {
return;
}
try {
await delObjs(ids);
getDataList();
useMessage().success(t('common.delSuccessText'));
} catch (err: any) {
useMessage().error(err.msg);
}
};
function remind(row, type) {
row.remindType = type
runNode.remind(row).then(() => {
$message.success('催办成功')
})
}
</script>