Files
EasyFlow/easyflow-ui-admin/app/src/views/system/sysJob/SysJobModal.vue
陈子默 d244a0404d fix: 收口管理端页面权限与工作流运行授权
- 页面选项接口改用所属页面权限并返回最小数据视图

- 统一校验工作流引用、租户、状态与定时任务执行主体

- 补充聊天记录权限迁移和权限隔离回归测试
2026-08-07 12:51:21 +08:00

306 lines
8.4 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.
<script setup lang="ts">
import type { FormInstance } from 'element-plus';
import { computed, onMounted, ref } from 'vue';
import { EasyFlowFormModal } from '@easyflow/common-ui';
import {
ElAlert,
ElForm,
ElFormItem,
ElInput,
ElMessage,
ElOption,
ElSelect,
} from 'element-plus';
import { api } from '#/api/request';
import CronPicker from '#/components/cron/CronPicker.vue';
import DictSelect from '#/components/dict/DictSelect.vue';
import { $t } from '#/locales';
import WorkflowFormItem from '#/views/ai/workflow/components/WorkflowFormItem.vue';
const emit = defineEmits(['reload']);
// vue
onMounted(() => {});
defineExpose({
openDialog,
});
const saveForm = ref<FormInstance>();
// variables
const dialogVisible = ref(false);
const isAdd = ref(true);
const initEntity = {
deptId: '',
jobName: '',
jobType: '',
jobParams: {
workflowParams: {},
},
cronExpression: '',
allowConcurrent: 0,
misfirePolicy: 3,
options: '',
status: '',
remark: '',
};
const entity = ref<any>(initEntity);
const btnLoading = ref(false);
const workflowOptions = ref<any[]>([]);
let workflowOptionsLoaded = false;
// 基础验证规则
const baseRules = ref({
jobName: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
jobType: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
cronExpression: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
allowConcurrent: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
misfirePolicy: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
});
const paramsLoading = ref(false);
const workflowParams = ref<any[]>([]);
const workflowParamsLoadError = ref('');
const rules = ref({ ...baseRules.value });
let workflowParamsRequestId = 0;
const workflowParamsSubmissionBlocked = computed(
() =>
entity.value.jobType === 1 &&
(paramsLoading.value || Boolean(workflowParamsLoadError.value)),
);
// functions
function openDialog(row: any) {
resetWorkflowParamsState();
if (row.id) {
entity.value = { ...row };
// 确保 jobParams 存在
if (!entity.value.jobParams) {
entity.value.jobParams = {};
}
if (entity.value.jobParams.workflowId) {
getWorkflowParams(entity.value.jobParams.workflowId);
}
isAdd.value = false;
} else {
entity.value = { ...initEntity };
}
void ensureWorkflowOptions();
dialogVisible.value = true;
}
async function ensureWorkflowOptions() {
if (workflowOptionsLoaded) {
return;
}
try {
const res = await api.get('/api/v1/sysJob/workflowOptions');
workflowOptions.value = Array.isArray(res.data) ? res.data : [];
workflowOptionsLoaded = true;
} catch {
workflowOptions.value = [];
ElMessage.error('工作流列表加载失败');
}
}
function save() {
if (btnLoading.value || workflowParamsSubmissionBlocked.value) {
return;
}
saveForm.value?.validate((valid) => {
if (!valid || btnLoading.value || workflowParamsSubmissionBlocked.value) {
return;
}
btnLoading.value = true;
api
.post(
isAdd.value ? 'api/v1/sysJob/save' : 'api/v1/sysJob/update',
entity.value,
)
.then((res) => {
btnLoading.value = false;
if (res.errorCode === 0) {
ElMessage.success(res.message);
emit('reload');
closeDialog();
}
})
.catch(() => {
btnLoading.value = false;
});
});
}
function closeDialog() {
saveForm.value?.resetFields();
isAdd.value = true;
entity.value = { ...initEntity };
resetWorkflowParamsState();
dialogVisible.value = false;
}
function jobTypeChange(v: any) {
resetWorkflowParamsState();
entity.value.jobParams = {
workflowParams: {},
};
if (v === 1) {
entity.value.jobParams.workflowParams = {};
}
}
function workflowChange(v: any) {
entity.value.jobParams.workflowParams = {};
getWorkflowParams(v);
}
function resetWorkflowParamsState() {
workflowParamsRequestId += 1;
paramsLoading.value = false;
workflowParams.value = [];
workflowParamsLoadError.value = '';
}
async function getWorkflowParams(v: any) {
const requestId = ++workflowParamsRequestId;
paramsLoading.value = true;
workflowParams.value = [];
workflowParamsLoadError.value = '';
try {
const res = await api.get('/api/v1/sysJob/workflowRunningParameters', {
params: { id: v },
});
if (requestId !== workflowParamsRequestId) {
return;
}
workflowParams.value = Array.isArray(res.data?.parameters)
? res.data.parameters
: [];
} catch {
if (requestId === workflowParamsRequestId) {
workflowParamsLoadError.value = '所选工作流已不可用,请重新选择';
}
} finally {
if (requestId === workflowParamsRequestId) {
paramsLoading.value = false;
}
}
}
const str = '"param"';
</script>
<template>
<EasyFlowFormModal
v-model:open="dialogVisible"
:closable="!btnLoading"
:title="isAdd ? $t('button.add') : $t('button.edit')"
:before-close="closeDialog"
:confirm-disabled="workflowParamsSubmissionBlocked"
:confirm-loading="btnLoading || paramsLoading"
:confirm-text="$t('button.save')"
:submitting="btnLoading"
@confirm="save"
>
<ElForm
v-loading="paramsLoading"
ref="saveForm"
:model="entity"
status-icon
:rules="rules"
label-position="top"
class="easyflow-modal-form easyflow-modal-form--compact"
>
<ElFormItem prop="jobName" :label="$t('sysJob.jobName')">
<ElInput v-model.trim="entity.jobName" />
</ElFormItem>
<ElFormItem prop="cronExpression" :label="$t('sysJob.cronExpression')">
<ElInput v-model.trim="entity.cronExpression" />
<CronPicker v-model="entity.cronExpression" />
</ElFormItem>
<ElFormItem prop="jobType" :label="$t('sysJob.jobType')">
<DictSelect
v-model="entity.jobType"
dict-code="jobType"
@change="jobTypeChange"
/>
</ElFormItem>
<ElFormItem
v-if="entity.jobType === 1"
:label="$t('sysJob.workflow')"
prop="jobParams.workflowId"
:rules="[
{ required: true, message: $t('message.required'), trigger: 'blur' },
]"
>
<ElSelect
v-model="entity.jobParams.workflowId"
clearable
filterable
@change="workflowChange"
>
<ElOption
v-for="workflow in workflowOptions"
:key="workflow.id"
:label="workflow.title"
:value="workflow.id"
/>
</ElSelect>
</ElFormItem>
<ElAlert
v-if="workflowParamsLoadError"
:title="workflowParamsLoadError"
type="error"
:closable="false"
show-icon
/>
<WorkflowFormItem
v-model:run-params="entity.jobParams.workflowParams"
:parameters="workflowParams"
prop-prefix="jobParams.workflowParams."
/>
<ElFormItem
v-if="entity.jobType === 2"
:label="$t('sysJob.beanMethod')"
prop="jobParams.beanMethod"
:rules="[
{ required: true, message: $t('message.required'), trigger: 'blur' },
]"
>
<ElInput v-model="entity.jobParams.beanMethod" />
<ElAlert
style="margin-top: 5px"
:title="`${$t('sysJob.example')}sysJobServiceImpl.testParam(${str},false,299)`"
/>
</ElFormItem>
<ElFormItem
v-if="entity.jobType === 3"
:label="$t('sysJob.javaMethod')"
prop="jobParams.javaMethod"
:rules="[
{ required: true, message: $t('message.required'), trigger: 'blur' },
]"
>
<ElInput v-model="entity.jobParams.javaMethod" />
<ElAlert
style="margin-top: 5px"
:title="`${$t('sysJob.example')}tech.easyflow.job.util.JobUtil.execTest(${str},1,0.52D,100L)`"
/>
</ElFormItem>
<ElFormItem prop="allowConcurrent" :label="$t('sysJob.allowConcurrent')">
<DictSelect v-model="entity.allowConcurrent" dict-code="yesOrNo" />
</ElFormItem>
<ElFormItem prop="misfirePolicy" :label="$t('sysJob.misfirePolicy')">
<DictSelect v-model="entity.misfirePolicy" dict-code="misfirePolicy" />
</ElFormItem>
<ElFormItem prop="remark" :label="$t('sysJob.remark')">
<ElInput v-model.trim="entity.remark" />
</ElFormItem>
</ElForm>
</EasyFlowFormModal>
</template>
<style scoped></style>