定时任务添加参数.

This commit is contained in:
lijiahang
2024-07-10 10:21:16 +08:00
parent a7f86bf62a
commit 873e910eb1
12 changed files with 279 additions and 68 deletions

View File

@@ -87,7 +87,7 @@ export const createDefaultOptions = (): Options => {
showFoldingControls: 'always', showFoldingControls: 'always',
renderLineHighlight: 'line', renderLineHighlight: 'line',
selectOnLineNumbers: true, selectOnLineNumbers: true,
lineNumbersMinChars: 2, lineNumbersMinChars: 3,
disableLayerHinting: true, disableLayerHinting: true,
minimap: { minimap: {
enabled: false, enabled: false,

View File

@@ -1,7 +1,7 @@
<template> <template>
<a-drawer v-model:visible="visible" <a-drawer v-model:visible="visible"
title="主机连接日志详情" title="主机连接日志详情"
:width="428" :width="442"
:mask-closable="false" :mask-closable="false"
:unmount-on-close="true" :unmount-on-close="true"
ok-text="关闭" ok-text="关闭"

View File

@@ -3,7 +3,7 @@
<a-card class="general-card table-search-card"> <a-card class="general-card table-search-card">
<query-header :model="formModel" <query-header :model="formModel"
label-align="left" label-align="left"
:itemOptions="{ 5: { span: 2 } }" :itemOptions="{ 6: { span: 2 } }"
@submit="fetchTableData" @submit="fetchTableData"
@reset="fetchTableData" @reset="fetchTableData"
@keyup.enter="() => fetchTableData()"> @keyup.enter="() => fetchTableData()">
@@ -30,6 +30,10 @@
<a-form-item field="hostAddress" label="主机地址"> <a-form-item field="hostAddress" label="主机地址">
<a-input v-model="formModel.hostAddress" placeholder="请输入主机地址" allow-clear /> <a-input v-model="formModel.hostAddress" placeholder="请输入主机地址" allow-clear />
</a-form-item> </a-form-item>
<!-- id -->
<a-form-item field="id" label="id">
<a-input-number v-model="formModel.id" placeholder="请输入日志id" allow-clear />
</a-form-item>
<!-- 类型 --> <!-- 类型 -->
<a-form-item field="type" label="类型"> <a-form-item field="type" label="类型">
<a-select v-model="formModel.type" <a-select v-model="formModel.type"
@@ -154,7 +158,7 @@
<!-- 详情 --> <!-- 详情 -->
<a-button type="text" <a-button type="text"
size="mini" size="mini"
@click="openDetail(record)"> @click="emits('openDetail', record)">
详情 详情
</a-button> </a-button>
<!-- 下线 --> <!-- 下线 -->
@@ -186,11 +190,6 @@
</template> </template>
</a-table> </a-table>
</a-card> </a-card>
<!-- 清空模态框 -->
<connect-log-clear-modal ref="clearModal"
@clear="fetchTableData" />
<!-- 详情模态框 -->
<connect-log-detail-drawer ref="detailModal" />
</template> </template>
<script lang="ts"> <script lang="ts">
@@ -213,20 +212,19 @@
import { dateFormat } from '@/utils'; import { dateFormat } from '@/utils';
import UserSelector from '@/components/user/user/selector/index.vue'; import UserSelector from '@/components/user/user/selector/index.vue';
import HostSelector from '@/components/asset/host/selector/index.vue'; import HostSelector from '@/components/asset/host/selector/index.vue';
import ConnectLogClearModal from './connect-log-clear-modal.vue';
import ConnectLogDetailDrawer from './connect-log-detail-drawer.vue';
const tableRenderData = ref<HostConnectLogQueryResponse[]>([]); const emits = defineEmits(['openClear', 'openDetail']);
const selectedKeys = ref<number[]>([]);
const clearModal = ref();
const detailModal = ref();
const pagination = usePagination(); const pagination = usePagination();
const rowSelection = useRowSelection(); const rowSelection = useRowSelection();
const { loading, setLoading } = useLoading(); const { loading, setLoading } = useLoading();
const { toOptions, getDictValue } = useDictStore(); const { toOptions, getDictValue } = useDictStore();
const tableRenderData = ref<Array<HostConnectLogQueryResponse>>([]);
const selectedKeys = ref<Array<number>>([]);
const formModel = reactive<HostConnectLogQueryRequest>({ const formModel = reactive<HostConnectLogQueryRequest>({
id: undefined,
userId: undefined, userId: undefined,
hostId: undefined, hostId: undefined,
hostAddress: undefined, hostAddress: undefined,
@@ -256,14 +254,11 @@
doFetchTableData({ page, limit, ...form }); doFetchTableData({ page, limit, ...form });
}; };
defineExpose({ fetchTableData });
// 打开清空 // 打开清空
const openClear = () => { const openClear = () => {
clearModal.value?.open({ ...formModel }); emits('openClear', { ...formModel, id: undefined });
};
// 打开详情
const openDetail = (record: HostConnectLogQueryResponse) => {
detailModal.value?.open(record);
}; };
// 强制下线 // 强制下线
@@ -297,13 +292,11 @@
}; };
// 删除当前行 // 删除当前行
const deleteRow = async ({ id }: { const deleteRow = async (record: HostConnectLogQueryResponse) => {
id: number
}) => {
try { try {
setLoading(true); setLoading(true);
// 调用删除接口 // 调用删除接口
await deleteHostConnectLog([id]); await deleteHostConnectLog([record.id]);
Message.success('删除成功'); Message.success('删除成功');
selectedKeys.value = []; selectedKeys.value = [];
// 重新加载数据 // 重新加载数据

View File

@@ -1,7 +1,14 @@
<template> <template>
<div class="layout-container" v-if="render"> <div class="layout-container" v-if="render">
<!-- 列表-表格 --> <!-- 列表-表格 -->
<connect-log-table /> <connect-log-table ref="table"
@open-clear="(s) => clearModal.open(s)"
@open-detail="(s) => detailModal.open(s)" />
<!-- 清空模态框 -->
<connect-log-clear-modal ref="clearModal"
@clear="() => table.fetchTableData()" />
<!-- 详情模态框 -->
<connect-log-detail-drawer ref="detailModal" />
</div> </div>
</template> </template>
@@ -16,8 +23,13 @@
import { useCacheStore, useDictStore } from '@/store'; import { useCacheStore, useDictStore } from '@/store';
import { dictKeys } from './types/const'; import { dictKeys } from './types/const';
import ConnectLogTable from './components/connect-log-table.vue'; import ConnectLogTable from './components/connect-log-table.vue';
import ConnectLogClearModal from './components/connect-log-clear-modal.vue';
import ConnectLogDetailDrawer from './components/connect-log-detail-drawer.vue';
const render = ref(false); const render = ref(false);
const table = ref();
const clearModal = ref();
const detailModal = ref();
// 加载字典配置 // 加载字典配置
onBeforeMount(async () => { onBeforeMount(async () => {

View File

@@ -16,7 +16,8 @@
</template> </template>
<a-spin :loading="loading" class="host-config-container"> <a-spin :loading="loading" class="host-config-container">
<!-- SSH 配置 --> <!-- SSH 配置 -->
<ssh-config-form :host-id="record.id" <ssh-config-form class="host-config-wrapper"
:host-id="record.id"
:content="config.ssh" :content="config.ssh"
@submitted="(e) => config.ssh = e" /> @submitted="(e) => config.ssh = e" />
</a-spin> </a-spin>
@@ -111,11 +112,12 @@
.host-config-container { .host-config-container {
width: 100%; width: 100%;
height: 100%; min-height: 100%;
background-color: var(--color-fill-2); background-color: var(--color-fill-2);
} }
.arco-card { .host-config-wrapper {
margin: 18px 18px 0 18px; margin: 18px;
} }
</style> </style>

View File

@@ -1,7 +1,7 @@
<template> <template>
<a-drawer v-model:visible="visible" <a-drawer v-model:visible="visible"
title="执行命令" title="执行命令"
width="66%" width="70%"
:esc-to-close="false" :esc-to-close="false"
:mask-closable="false" :mask-closable="false"
:unmount-on-close="true" :unmount-on-close="true"
@@ -275,7 +275,7 @@
.command-editor { .command-editor {
width: 100%; width: 100%;
height: 62vh; height: 56vh;
} }
</style> </style>

View File

@@ -1,6 +1,6 @@
<template> <template>
<a-drawer v-model:visible="visible" <a-drawer v-model:visible="visible"
width="66%" width="70%"
:title="title" :title="title"
:esc-to-close="false" :esc-to-close="false"
:mask-closable="false" :mask-closable="false"
@@ -103,18 +103,35 @@
:key="i" :key="i"
class="parameter-item" class="parameter-item"
:class="[ i === parameter.length - 1 ? 'parameter-item-last' : '' ]"> :class="[ i === parameter.length - 1 ? 'parameter-item-last' : '' ]">
<!-- 参数名 -->
<a-input class="parameter-item-name" <a-input class="parameter-item-name"
v-model="item.name" v-model="item.name"
placeholder="参数名称 (必填)" placeholder="必填"
allow-clear /> :max-length="24"
allow-clear>
<template #prepend>
<span>参数名</span>
</template>
</a-input>
<!-- 默认值 -->
<a-input class="parameter-item-default" <a-input class="parameter-item-default"
v-model="item.defaultValue" v-model="item.defaultValue"
placeholder="默认值 (非必填)" placeholder="非必填"
allow-clear /> allow-clear>
<template #prepend>
<span>默认值</span>
</template>
</a-input>
<!-- 描述 -->
<a-input class="parameter-item-description" <a-input class="parameter-item-description"
v-model="item.desc" v-model="item.desc"
placeholder="描述 (非必填)" placeholder="非必填"
allow-clear /> :max-length="64"
allow-clear>
<template #prepend>
<span>描述</span>
</template>
</a-input>
<span class="parameter-item-close click-icon-wrapper" <span class="parameter-item-close click-icon-wrapper"
title="移除" title="移除"
@click="removeParameter(i)"> @click="removeParameter(i)">

View File

@@ -1,7 +1,7 @@
<template> <template>
<a-drawer v-model:visible="visible" <a-drawer v-model:visible="visible"
title="计划任务详情" title="计划任务详情"
width="66%" width="70%"
:mask-closable="false" :mask-closable="false"
:unmount-on-close="true" :unmount-on-close="true"
ok-text="关闭" ok-text="关闭"
@@ -75,6 +75,17 @@
:readonly="true" :readonly="true"
:suggestions="false" /> :suggestions="false" />
</a-descriptions-item> </a-descriptions-item>
<!-- 执行参数 -->
<a-descriptions-item v-if="record.parameterSchema"
label="执行参数"
:span="3">
<editor v-model="record.parameterSchema"
language="json"
theme="vs-dark"
container-class="json-editor"
:readonly="true"
:suggestions="false" />
</a-descriptions-item>
</a-descriptions> </a-descriptions>
</a-drawer> </a-drawer>
</template> </template>
@@ -105,11 +116,20 @@
// 打开 // 打开
const open = async (id: any) => { const open = async (id: any) => {
// 查询计划任务
try { try {
// 查询计划任务
setLoading(true); setLoading(true);
const { data } = await getExecJob(id); const { data } = await getExecJob(id);
record.value = data; record.value = data;
// 设置参数值
if (data.parameterSchema) {
const value = JSON.parse(data.parameterSchema);
if (value?.length) {
data.parameterSchema = JSON.stringify(value, undefined, 4);
} else {
data.parameterSchema = undefined as unknown as string;
}
}
setVisible(true); setVisible(true);
} catch (e) { } catch (e) {
} finally { } finally {
@@ -146,7 +166,13 @@
} }
.command-editor { .command-editor {
height: calc(100vh - 384px); width: 100%;
height: calc(100vh - 396px);
}
.json-editor {
width: 100%;
height: 328px;
} }
</style> </style>

View File

@@ -1,7 +1,7 @@
<template> <template>
<a-drawer v-model:visible="visible" <a-drawer v-model:visible="visible"
:title="title" :title="title"
width="66%" width="70%"
:esc-to-close="false" :esc-to-close="false"
:mask-closable="false" :mask-closable="false"
:unmount-on-close="true" :unmount-on-close="true"
@@ -117,7 +117,66 @@
<exec-editor v-model="formModel.command" <exec-editor v-model="formModel.command"
container-class="command-editor" container-class="command-editor"
theme="vs-dark" theme="vs-dark"
:parameter="jobBuiltinParams" /> :parameter="[...jobBuiltinParams, ...parameter]" />
</a-form-item>
</a-col>
<!-- 命令参数 -->
<a-col :span="24">
<a-form-item field="parameter"
class="parameter-form-item"
label="命令参数">
<!-- label -->
<template #label>
<span class="span-blue pointer" @click="addParameter">添加参数</span>
</template>
<!-- 参数 -->
<template v-if="parameter.length">
<a-input-group v-for="(item, i) in parameter"
:key="i"
class="parameter-item"
:class="[ i === parameter.length - 1 ? 'parameter-item-last' : '' ]">
<!-- 参数名 -->
<a-input class="parameter-item-name"
v-model="item.name"
placeholder="必填"
:max-length="24"
allow-clear>
<template #prepend>
<span>参数名</span>
</template>
</a-input>
<!-- 参数值 -->
<a-input class="parameter-item-default"
v-model="item.defaultValue"
placeholder="必填"
allow-clear>
<template #prepend>
<span>参数值</span>
</template>
</a-input>
<!-- 描述 -->
<a-input class="parameter-item-description"
v-model="item.desc"
placeholder="非必填"
:max-length="64"
allow-clear>
<template #prepend>
<span>描述</span>
</template>
</a-input>
<span class="parameter-item-close click-icon-wrapper"
title="移除"
@click="removeParameter(i)">
<icon-close />
</span>
</a-input-group>
</template>
<!-- 无参数 -->
<template v-else>
<span class="no-parameter">
<icon-empty class="mr4" />无参数
</span>
</template>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
@@ -135,6 +194,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { ExecJobUpdateRequest } from '@/api/job/exec-job'; import type { ExecJobUpdateRequest } from '@/api/job/exec-job';
import type { ExecTemplateQueryResponse } from '@/api/exec/exec-template'; import type { ExecTemplateQueryResponse } from '@/api/exec/exec-template';
import type { TemplateParam } from '@/components/view/exec-editor/const';
import { onUnmounted, ref } from 'vue'; import { onUnmounted, ref } from 'vue';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible'; import useVisible from '@/hooks/visible';
@@ -157,6 +217,7 @@
const isAddHandle = ref<boolean>(true); const isAddHandle = ref<boolean>(true);
const formRef = ref<any>(); const formRef = ref<any>();
const formModel = ref<ExecJobUpdateRequest>({}); const formModel = ref<ExecJobUpdateRequest>({});
const parameter = ref<Array<TemplateParam>>([]);
const defaultForm = (): ExecJobUpdateRequest => { const defaultForm = (): ExecJobUpdateRequest => {
return { return {
@@ -208,6 +269,21 @@
parameterSchema: record.parameterSchema, parameterSchema: record.parameterSchema,
hostIdList: record.hostIdList, hostIdList: record.hostIdList,
}; };
if (record.parameterSchema) {
parameter.value = JSON.parse(record.parameterSchema);
} else {
parameter.value = [];
}
};
// 添加参数
const addParameter = () => {
parameter.value.push({});
};
// 移除参数
const removeParameter = (index: number) => {
parameter.value.splice(index, 1);
}; };
// 设置表达式 // 设置表达式
@@ -221,11 +297,11 @@
}; };
// 通过模板设置 // 通过模板设置
const setWithTemplate = async ({ id }: ExecTemplateQueryResponse) => { const setWithTemplate = async (record: ExecTemplateQueryResponse) => {
setLoading(true); setLoading(true);
try { try {
// 查询模板信息 // 查询模板信息
const { data } = await getExecTemplateWithAuthorized(id); const { data } = await getExecTemplateWithAuthorized(record.id);
formModel.value = { formModel.value = {
...formModel.value, ...formModel.value,
name: data.name, name: data.name,
@@ -235,6 +311,11 @@
parameterSchema: data.parameterSchema, parameterSchema: data.parameterSchema,
hostIdList: data.hostIdList, hostIdList: data.hostIdList,
}; };
if (record.parameterSchema) {
parameter.value = JSON.parse(record.parameterSchema);
} else {
parameter.value = [];
}
} catch (e) { } catch (e) {
} finally { } finally {
setLoading(false); setLoading(false);
@@ -257,6 +338,14 @@
if (error) { if (error) {
return false; return false;
} }
// 验证并设置命令参数
for (const p of parameter.value) {
if (!p.name || !p.defaultValue) {
Message.warning('请补全命令参数');
return false;
}
}
formModel.value.parameterSchema = JSON.stringify(parameter.value);
if (isAddHandle.value) { if (isAddHandle.value) {
// 新增 // 新增
await createExecJob(formModel.value); await createExecJob(formModel.value);
@@ -327,7 +416,7 @@
.command-editor { .command-editor {
width: 100%; width: 100%;
height: calc(100vh - 264px); height: calc(100vh - 318px);
} }
:deep(.arco-input-append) { :deep(.arco-input-append) {
@@ -352,4 +441,67 @@
} }
} }
.parameter-form-item {
user-select: none;
margin-top: 4px;
:deep(.arco-form-item-content) {
flex-direction: column;
}
.parameter-item-last {
margin-bottom: 0 !important;
}
.parameter-item {
width: 100%;
margin-bottom: 12px;
display: flex;
justify-content: space-between;
& > span {
border-radius: 2px;
border-right-color: transparent;
}
&-name {
width: 29%;
}
&-default {
width: 29%;
}
&-description {
width: calc(39% - 44px);
}
&-close {
cursor: pointer;
width: 32px;
height: 32px;
font-size: 16px;
background: var(--color-fill-2);
display: flex;
align-items: center;
justify-content: center;
&:hover {
background: var(--color-fill-3);
}
}
}
.no-parameter {
background: var(--color-fill-2);
width: 100%;
height: 32px;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
color: var(--color-text-2);
}
}
</style> </style>

View File

@@ -105,8 +105,8 @@
const emits = defineEmits(['clear']); const emits = defineEmits(['clear']);
// 打开 // 打开
const open = () => { const open = (record: OperatorLogQueryRequest) => {
renderForm({ ...defaultForm() }); renderForm({ ...defaultForm(), ...record });
setVisible(true); setVisible(true);
}; };

View File

@@ -98,7 +98,7 @@
<!-- 详情 --> <!-- 详情 -->
<a-button type="text" <a-button type="text"
size="mini" size="mini"
@click="openLogDetail(record)"> @click="emits('openDetail', record)">
详情 详情
</a-button> </a-button>
<!-- 删除 --> <!-- 删除 -->
@@ -117,11 +117,6 @@
</template> </template>
</a-table> </a-table>
</a-card> </a-card>
<!-- 清理模态框 -->
<operator-log-clear-modal ref="clearModal"
@clear="fetchTableData" />
<!-- json 查看器模态框 -->
<json-editor-modal ref="jsonView" />
</template> </template>
<script lang="ts"> <script lang="ts">
@@ -133,7 +128,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { OperatorLogQueryRequest, OperatorLogQueryResponse } from '@/api/user/operator-log'; import type { OperatorLogQueryRequest, OperatorLogQueryResponse } from '@/api/user/operator-log';
import { ref, reactive, onMounted } from 'vue'; import { ref, reactive, onMounted } from 'vue';
import { operatorLogModuleKey, operatorLogTypeKey, operatorRiskLevelKey, operatorLogResultKey, getLogDetail } from '../types/const'; import { operatorLogModuleKey, operatorLogTypeKey, operatorRiskLevelKey, operatorLogResultKey } from '../types/const';
import columns from '../types/table.columns'; import columns from '../types/table.columns';
import { copy } from '@/hooks/copy'; import { copy } from '@/hooks/copy';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
@@ -143,16 +138,14 @@
import { replaceHtmlTag, clearHtmlTag } from '@/utils'; import { replaceHtmlTag, clearHtmlTag } from '@/utils';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import OperatorLogQueryHeader from './operator-log-query-header.vue'; import OperatorLogQueryHeader from './operator-log-query-header.vue';
import OperatorLogClearModal from './operator-log-clear-modal.vue';
import JsonEditorModal from '@/components/view/json-editor/modal/index.vue'; const emits = defineEmits(['openClear', 'openDetail']);
const pagination = usePagination(); const pagination = usePagination();
const rowSelection = useRowSelection(); const rowSelection = useRowSelection();
const { loading, setLoading } = useLoading(); const { loading, setLoading } = useLoading();
const { getDictValue } = useDictStore(); const { getDictValue } = useDictStore();
const clearModal = ref();
const jsonView = ref();
const selectedKeys = ref<Array<number>>([]); const selectedKeys = ref<Array<number>>([]);
const tableRenderData = ref<Array<OperatorLogQueryResponse>>([]); const tableRenderData = ref<Array<OperatorLogQueryResponse>>([]);
const formModel = reactive<OperatorLogQueryRequest>({ const formModel = reactive<OperatorLogQueryRequest>({
@@ -163,14 +156,9 @@
startTimeRange: undefined, startTimeRange: undefined,
}); });
// 查看详情
const openLogDetail = (record: OperatorLogQueryResponse) => {
jsonView.value.open(getLogDetail(record));
};
// 打开清空 // 打开清空
const openClear = () => { const openClear = () => {
clearModal.value?.open(); emits('openClear', { ...formModel });
}; };
// 删除选中行 // 删除选中行
@@ -228,6 +216,8 @@
doFetchTableData({ page, limit, ...form }); doFetchTableData({ page, limit, ...form });
}; };
defineExpose({ fetchTableData });
// 初始化 // 初始化
onMounted(() => { onMounted(() => {
fetchTableData(); fetchTableData();

View File

@@ -1,6 +1,14 @@
<template> <template>
<div class="layout-container" v-if="render"> <div class="layout-container" v-if="render">
<operator-log-table /> <!-- 表格 -->
<operator-log-table ref="table"
@open-detail="openLogDetail"
@open-clear="(s) => clearModal.open(s)" />
<!-- 清理模态框 -->
<operator-log-clear-modal ref="clearModal"
@clear="() => table.fetchTableData()" />
<!-- json 查看器模态框 -->
<json-editor-modal ref="jsonView" />
</div> </div>
</template> </template>
@@ -11,14 +19,25 @@
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import type { OperatorLogQueryResponse } from '@/api/user/operator-log';
import { ref, onUnmounted, onBeforeMount } from 'vue'; import { ref, onUnmounted, onBeforeMount } from 'vue';
import { useCacheStore, useDictStore } from '@/store'; import { useCacheStore, useDictStore } from '@/store';
import { dictKeys } from './types/const'; import { dictKeys, getLogDetail } from './types/const';
import OperatorLogTable from './components/operator-log-table.vue'; import OperatorLogTable from './components/operator-log-table.vue';
import OperatorLogClearModal from './components/operator-log-clear-modal.vue';
import JsonEditorModal from '@/components/view/json-editor/modal/index.vue';
const cacheStore = useCacheStore(); const cacheStore = useCacheStore();
const render = ref(false); const render = ref(false);
const table = ref();
const clearModal = ref();
const jsonView = ref();
// 查看详情
const openLogDetail = (record: OperatorLogQueryResponse) => {
jsonView.value.open(getLogDetail(record));
};
// 加载字典值 // 加载字典值
onBeforeMount(async () => { onBeforeMount(async () => {