mirror of
https://github.com/MCSManager/MCSManager.git
synced 2025-04-06 17:10:29 +08:00
commit
64a7a2727e
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@ -51,6 +51,7 @@ declare module 'vue' {
|
||||
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
||||
ATag: typeof import('ant-design-vue/es')['Tag']
|
||||
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
||||
ATimePicker: typeof import('ant-design-vue/es')['TimePicker']
|
||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||
ATypography: typeof import('ant-design-vue/es')['Typography']
|
||||
ATypographyParagraph: typeof import('ant-design-vue/es')['TypographyParagraph']
|
||||
|
@ -32,6 +32,7 @@ import UserStatusBlock from "@/widgets/UserStatusBlock.vue";
|
||||
import UserInstanceList from "@/widgets/UserInstanceList.vue";
|
||||
import ImageManager from "@/widgets/imageManager/index.vue";
|
||||
import NewImage from "@/widgets/imageManager/NewImage.vue";
|
||||
import Schedule from "@/widgets/instance/Schedule.vue";
|
||||
|
||||
import { NEW_CARD_TYPE } from "../types/index";
|
||||
import { ROLE } from "./router";
|
||||
@ -67,7 +68,8 @@ export const LAYOUT_CARD_TYPES: { [key: string]: any } = {
|
||||
UserStatusBlock,
|
||||
UserInstanceList,
|
||||
ImageManager,
|
||||
NewImage
|
||||
NewImage,
|
||||
Schedule
|
||||
};
|
||||
|
||||
export interface NewCardItem extends LayoutCard {
|
||||
|
@ -103,6 +103,14 @@ let originRouterConfig: RouterConfig[] = [
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/instances/schedule",
|
||||
name: t("计划任务"),
|
||||
component: LayoutContainer,
|
||||
meta: {
|
||||
permission: ROLE.USER
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { useDefineApi } from "@/stores/useDefineApi";
|
||||
import type { InstanceDetail, NewInstanceForm, QuickStartTemplate } from "@/types";
|
||||
import type {
|
||||
InstanceDetail,
|
||||
NewInstanceForm,
|
||||
QuickStartTemplate,
|
||||
Schedule,
|
||||
NewScheduleTask
|
||||
} from "@/types";
|
||||
import type { IGlobalInstanceConfig } from "../../../../common/global";
|
||||
import type { InstanceMoreDetail } from "@/hooks/useInstance";
|
||||
|
||||
@ -67,6 +73,53 @@ export const stopInstance = useDefineApi<
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
export const restartInstance = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
},
|
||||
{
|
||||
instanceUuid: string;
|
||||
}
|
||||
>({
|
||||
url: "/api/protected_instance/restart",
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
export const killInstance = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
},
|
||||
{
|
||||
instanceUuid: string;
|
||||
}
|
||||
>({
|
||||
url: "/api/protected_instance/kill",
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
export const updateInstance = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
task_name: string;
|
||||
};
|
||||
data: {
|
||||
time: number;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/protected_instance/asynchronous",
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
// 更新实例设置(普通用户)
|
||||
export const updateInstanceConfig = useDefineApi<
|
||||
{
|
||||
@ -343,3 +396,47 @@ export const batchDelete = useDefineApi<
|
||||
method: "DELETE",
|
||||
url: "/api/instance"
|
||||
});
|
||||
|
||||
// 获取计划任务
|
||||
export const scheduleList = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
remote_uuid: string;
|
||||
uuid: string;
|
||||
};
|
||||
},
|
||||
Schedule[]
|
||||
>({
|
||||
method: "GET",
|
||||
url: "/api/protected_schedule"
|
||||
});
|
||||
|
||||
// 删除计划任务
|
||||
export const scheduleDelete = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
remote_uuid: string;
|
||||
uuid: string;
|
||||
task_name: string;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
method: "DELETE",
|
||||
url: "/api/protected_schedule"
|
||||
});
|
||||
|
||||
// 创建计划任务
|
||||
export const scheduleCreate = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
remote_uuid: string;
|
||||
uuid: string;
|
||||
};
|
||||
data: NewScheduleTask;
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/protected_schedule",
|
||||
method: "POST"
|
||||
});
|
||||
|
@ -80,3 +80,23 @@ ENV LC_ALL=zh_CN.UTF-8
|
||||
ENV TZ=Asia/Shanghai
|
||||
WORKDIR /workspace
|
||||
`;
|
||||
|
||||
export const ScheduleAction = {
|
||||
command: t("发送命令"),
|
||||
stop: t("停止实例"),
|
||||
start: t("开启实例"),
|
||||
restart: t("重启实例"),
|
||||
kill: t("终止实例")
|
||||
};
|
||||
|
||||
export const ScheduleType = {
|
||||
1: t("间隔时间性任务"),
|
||||
2: t("周期时间性任务"),
|
||||
3: t("指定时间性任务")
|
||||
};
|
||||
|
||||
export enum ScheduleCreateType {
|
||||
INTERVAL = "1",
|
||||
CYCLE = "2",
|
||||
SPECIFY = "3"
|
||||
}
|
||||
|
@ -218,3 +218,21 @@ export interface MountComponent {
|
||||
destroyComponent: Function;
|
||||
emitResult: Function;
|
||||
}
|
||||
|
||||
export interface Schedule {
|
||||
instanceUuid: string;
|
||||
name: string;
|
||||
count: number;
|
||||
time: string;
|
||||
action: string;
|
||||
payload: string;
|
||||
type: number;
|
||||
}
|
||||
|
||||
export interface NewScheduleTask {
|
||||
name: String;
|
||||
count: String;
|
||||
time: String;
|
||||
action: String;
|
||||
type: String;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { ref, computed } from "vue";
|
||||
import type { LayoutCard } from "@/types";
|
||||
import { arrayFilter } from "../../tools/array";
|
||||
import { t } from "@/lang/i18n";
|
||||
import { ArrowRightOutlined, CloudServerOutlined } from "@ant-design/icons-vue";
|
||||
import { ArrowRightOutlined, CloudServerOutlined, FieldTimeOutlined } from "@ant-design/icons-vue";
|
||||
import InnerCard from "@/components/InnerCard.vue";
|
||||
import { LayoutCardHeight } from "../../config/originLayoutConfig";
|
||||
import { useAppRouters } from "@/hooks/useAppRouters";
|
||||
@ -80,9 +80,17 @@ const btns = computed(() =>
|
||||
},
|
||||
{
|
||||
title: t("TXT_CODE_b7d026f8"),
|
||||
icon: CloudServerOutlined,
|
||||
icon: FieldTimeOutlined,
|
||||
condition: () => !isGlobalTerminal.value,
|
||||
click: () => {}
|
||||
click: () => {
|
||||
toPage({
|
||||
path: "/instances/schedule",
|
||||
query: {
|
||||
instanceId,
|
||||
daemonId
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
// 暂时删除
|
||||
// {
|
||||
|
254
frontend/src/widgets/instance/Schedule.vue
Normal file
254
frontend/src/widgets/instance/Schedule.vue
Normal file
@ -0,0 +1,254 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import { message } from "ant-design-vue";
|
||||
import { DeleteOutlined, FieldTimeOutlined } from "@ant-design/icons-vue";
|
||||
import CardPanel from "@/components/CardPanel.vue";
|
||||
import BetweenMenus from "@/components/BetweenMenus.vue";
|
||||
import { useLayoutCardTools } from "@/hooks/useCardTools";
|
||||
import { useAppRouters } from "@/hooks/useAppRouters";
|
||||
import { scheduleList, scheduleDelete } from "@/services/apis/instance";
|
||||
import type { LayoutCard, Schedule } from "@/types/index";
|
||||
import { ScheduleAction, ScheduleType, ScheduleCreateType } from "@/types/const";
|
||||
import NewSchedule from "@/widgets/instance/dialogs/NewSchedule.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
card: LayoutCard;
|
||||
}>();
|
||||
|
||||
const { getMetaOrRouteValue } = useLayoutCardTools(props.card);
|
||||
const instanceId = getMetaOrRouteValue("instanceId");
|
||||
const daemonId = getMetaOrRouteValue("daemonId");
|
||||
const { toPage } = useAppRouters();
|
||||
const newScheduleDialog = ref<InstanceType<typeof NewSchedule>>();
|
||||
|
||||
const { state, execute, isLoading } = scheduleList();
|
||||
const getScheduleList = async () => {
|
||||
try {
|
||||
await execute({
|
||||
params: {
|
||||
remote_uuid: daemonId ?? "",
|
||||
uuid: instanceId ?? ""
|
||||
}
|
||||
});
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
message.error(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteSchedule = async (name: string) => {
|
||||
const { execute, state } = scheduleDelete();
|
||||
try {
|
||||
await execute({
|
||||
params: {
|
||||
remote_uuid: daemonId ?? "",
|
||||
uuid: instanceId ?? "",
|
||||
task_name: name
|
||||
}
|
||||
});
|
||||
if (state.value) {
|
||||
message.success(t("删除成功"));
|
||||
await getScheduleList();
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
message.error(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const rendTime = (text: string, schedule: Schedule) => {
|
||||
switch (schedule.type.toString()) {
|
||||
case ScheduleCreateType.INTERVAL: {
|
||||
const time = Number(text);
|
||||
let s = time;
|
||||
let m = 0;
|
||||
let h = 0;
|
||||
while (s >= 60) {
|
||||
s -= 60;
|
||||
m += 1;
|
||||
}
|
||||
while (m >= 60) {
|
||||
m -= 60;
|
||||
h += 1;
|
||||
}
|
||||
return `${t("每隔")} ${h} ${t("小时")} ${m} ${t("分钟")} ${s} ${t("秒")}`;
|
||||
}
|
||||
case ScheduleCreateType.CYCLE: {
|
||||
const time = text;
|
||||
const timeArr = time.split(" ");
|
||||
const h = timeArr[2];
|
||||
const m = timeArr[1];
|
||||
const s = timeArr[0];
|
||||
const w = timeArr[5];
|
||||
return `${t("每星期")} ${w} ${t("的")} ${h}:${m}:${s}`;
|
||||
}
|
||||
case ScheduleCreateType.SPECIFY: {
|
||||
const time = text;
|
||||
const timeArr = time.split(" ");
|
||||
const h = timeArr[2];
|
||||
const m = timeArr[1];
|
||||
const s = timeArr[0];
|
||||
const dd = timeArr[3];
|
||||
const mm = timeArr[4];
|
||||
return `${mm} ${t("月")} ${dd} ${t("日")} ${h}:${m}:${s}`;
|
||||
}
|
||||
default:
|
||||
return "Unknown Time";
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
align: "center",
|
||||
title: t("任务名"),
|
||||
dataIndex: "name",
|
||||
key: "name"
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
title: t("有效载荷"),
|
||||
dataIndex: "payload",
|
||||
key: "payload"
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
title: t("剩余次数"),
|
||||
dataIndex: "count",
|
||||
key: "count",
|
||||
minWidth: "80px",
|
||||
customRender: (e: { text: number }) => (e.text > 0 ? e.text : t("无限"))
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
title: t("动作"),
|
||||
dataIndex: "action",
|
||||
key: "action",
|
||||
minWidth: "180px",
|
||||
customRender: (e: { text: "command" | "stop" | "start" | "restart" | "kill" }) =>
|
||||
ScheduleAction[e.text]
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
title: t("类型"),
|
||||
dataIndex: "type",
|
||||
key: "type",
|
||||
minWidth: "180px",
|
||||
customRender: (e: { text: 1 | 2 | 3 }) => ScheduleType[e.text]
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
title: t("触发时间"),
|
||||
dataIndex: "time",
|
||||
key: "time",
|
||||
minWidth: "240px",
|
||||
customRender: (e: { text: string; record: Schedule }) => rendTime(e.text, e.record)
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
title: t("TXT_CODE_fe731dfc"),
|
||||
key: "actions",
|
||||
minWidth: "180px"
|
||||
}
|
||||
];
|
||||
|
||||
const refresh = async () => {
|
||||
await getScheduleList();
|
||||
message.success(t("已刷新"));
|
||||
};
|
||||
|
||||
const toConsole = () => {
|
||||
toPage({
|
||||
path: "/instances/terminal",
|
||||
query: {
|
||||
daemonId,
|
||||
instanceId
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
getScheduleList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="height: 100%" class="container">
|
||||
<a-row :gutter="[24, 24]" style="height: 100%">
|
||||
<a-col :span="24">
|
||||
<BetweenMenus>
|
||||
<template #left>
|
||||
<a-typography-title class="mb-0" :level="4">
|
||||
<FieldTimeOutlined />
|
||||
{{ card.title }}
|
||||
</a-typography-title>
|
||||
</template>
|
||||
<template #right>
|
||||
<a-button class="mr-10" @click="toConsole()">
|
||||
{{ t("返回") }}
|
||||
</a-button>
|
||||
<a-button class="mr-10" @click="refresh">
|
||||
{{ t("刷新") }}
|
||||
</a-button>
|
||||
<a-button type="primary" @click="newScheduleDialog?.openDialog()">
|
||||
{{ t("新增") }}
|
||||
</a-button>
|
||||
</template>
|
||||
</BetweenMenus>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<CardPanel style="height: 100%">
|
||||
<template #body>
|
||||
<a-spin :spinning="isLoading">
|
||||
<a-table
|
||||
:data-source="state"
|
||||
:columns="columns"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
:pagination="{
|
||||
pageSize: 15
|
||||
}"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'actions'">
|
||||
<a-popconfirm
|
||||
:title="t('你确定要删除这条计划任务吗?')"
|
||||
@confirm="deleteSchedule(record.name)"
|
||||
>
|
||||
<a-button size="">
|
||||
{{ t("删除") }}
|
||||
<DeleteOutlined />
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-spin>
|
||||
</template>
|
||||
</CardPanel>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<NewSchedule
|
||||
ref="newScheduleDialog"
|
||||
:daemon-id="daemonId ?? ''"
|
||||
:instance-id="instanceId ?? ''"
|
||||
@get-schedule-list="getScheduleList()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-input {
|
||||
transition: all 0.4s;
|
||||
text-align: center;
|
||||
width: 50%;
|
||||
|
||||
&:hover {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -18,7 +18,13 @@ import { onMounted, computed, ref } from "vue";
|
||||
import { useLayoutCardTools } from "@/hooks/useCardTools";
|
||||
import { getRandomId } from "../../tools/randId";
|
||||
import IconBtn from "@/components/IconBtn.vue";
|
||||
import { openInstance, stopInstance } from "@/services/apis/instance";
|
||||
import {
|
||||
openInstance,
|
||||
stopInstance,
|
||||
restartInstance,
|
||||
killInstance,
|
||||
updateInstance
|
||||
} from "@/services/apis/instance";
|
||||
import { CloseOutlined } from "@ant-design/icons-vue";
|
||||
import { GLOBAL_INSTANCE_NAME } from "../../config/const";
|
||||
import { INSTANCE_STATUS_TEXT } from "../../hooks/useInstance";
|
||||
@ -70,7 +76,8 @@ const quickOperations = computed(() =>
|
||||
}
|
||||
});
|
||||
},
|
||||
props: {}
|
||||
props: {},
|
||||
condition: () => isStopped.value
|
||||
},
|
||||
{
|
||||
title: t("TXT_CODE_b1dedda3"),
|
||||
@ -85,34 +92,59 @@ const quickOperations = computed(() =>
|
||||
},
|
||||
props: {
|
||||
danger: true
|
||||
}
|
||||
},
|
||||
condition: () => isRunning.value
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
const instanceOperations = arrayFilter([
|
||||
{
|
||||
title: t("TXT_CODE_47dcfa5"),
|
||||
icon: ReconciliationOutlined,
|
||||
click: () => {
|
||||
console.log(3);
|
||||
const instanceOperations = computed(() =>
|
||||
arrayFilter([
|
||||
{
|
||||
title: t("TXT_CODE_47dcfa5"),
|
||||
icon: ReconciliationOutlined,
|
||||
click: () => {
|
||||
restartInstance().execute({
|
||||
params: {
|
||||
uuid: instanceId || "",
|
||||
remote_uuid: daemonId || ""
|
||||
}
|
||||
});
|
||||
},
|
||||
condition: () => isRunning.value
|
||||
},
|
||||
{
|
||||
title: t("TXT_CODE_7b67813a"),
|
||||
icon: CloseOutlined,
|
||||
click: () => {
|
||||
killInstance().execute({
|
||||
params: {
|
||||
uuid: instanceId || "",
|
||||
remote_uuid: daemonId || ""
|
||||
}
|
||||
});
|
||||
},
|
||||
condition: () => isRunning.value
|
||||
},
|
||||
{
|
||||
title: t("TXT_CODE_40ca4f2"),
|
||||
icon: CloudDownloadOutlined,
|
||||
click: () => {
|
||||
updateInstance().execute({
|
||||
params: {
|
||||
uuid: instanceId || "",
|
||||
remote_uuid: daemonId || "",
|
||||
task_name: "update"
|
||||
},
|
||||
data: {
|
||||
time: new Date().getTime()
|
||||
}
|
||||
});
|
||||
},
|
||||
condition: () => isStopped.value
|
||||
}
|
||||
},
|
||||
{
|
||||
title: t("TXT_CODE_7b67813a"),
|
||||
icon: CloseOutlined,
|
||||
click: () => {
|
||||
console.log(3);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: t("TXT_CODE_40ca4f2"),
|
||||
icon: CloudDownloadOutlined,
|
||||
click: () => {
|
||||
console.log(4);
|
||||
}
|
||||
}
|
||||
]);
|
||||
])
|
||||
);
|
||||
|
||||
const handleSendCommand = () => {
|
||||
sendCommand(commandInputValue.value);
|
||||
@ -240,7 +272,7 @@ const innerTerminalType = viewType === "inner";
|
||||
</template>
|
||||
</BetweenMenus>
|
||||
</div>
|
||||
<a-spin :spinning="!isConnect" tip="正在连接终端中...">
|
||||
<a-spin :spinning="!isConnect" :tip="t('正在连接终端中...')">
|
||||
<div v-if="!containerState.isDesignMode" class="console-wrapper">
|
||||
<div class="terminal-wrapper global-card-container-shadow">
|
||||
<div class="terminal-container">
|
||||
|
246
frontend/src/widgets/instance/dialogs/NewSchedule.vue
Normal file
246
frontend/src/widgets/instance/dialogs/NewSchedule.vue
Normal file
@ -0,0 +1,246 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from "vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import { message, notification } from "ant-design-vue";
|
||||
import { ScheduleAction, ScheduleType, ScheduleCreateType } from "@/types/const";
|
||||
import type { NewScheduleTask } from "@/types";
|
||||
import { scheduleCreate } from "@/services/apis/instance";
|
||||
import { useScreen } from "@/hooks/useScreen";
|
||||
import type { Dayjs } from "dayjs";
|
||||
import _ from "lodash";
|
||||
|
||||
const props = defineProps<{
|
||||
daemonId: string;
|
||||
instanceId: string;
|
||||
}>();
|
||||
const emit = defineEmits(["getScheduleList"]);
|
||||
const open = ref(false);
|
||||
const openDialog = () => {
|
||||
open.value = true;
|
||||
};
|
||||
const { isPhone } = useScreen();
|
||||
|
||||
const weeks = [
|
||||
{ label: t("周一"), value: 1 },
|
||||
{ label: t("周二"), value: 2 },
|
||||
{ label: t("周三"), value: 3 },
|
||||
{ label: t("周四"), value: 4 },
|
||||
{ label: t("周五"), value: 5 },
|
||||
{ label: t("周六"), value: 6 },
|
||||
{ label: t("周日"), value: 7 }
|
||||
];
|
||||
|
||||
interface NewTask extends NewScheduleTask {
|
||||
payload: string;
|
||||
weekend: string[];
|
||||
cycle: string[];
|
||||
objTime: string;
|
||||
}
|
||||
|
||||
const newTaskOrigin: NewTask = {
|
||||
name: "",
|
||||
action: "command",
|
||||
count: "",
|
||||
type: ScheduleCreateType.INTERVAL,
|
||||
time: "",
|
||||
payload: "",
|
||||
weekend: [],
|
||||
cycle: ["0", "0", "0"],
|
||||
objTime: ""
|
||||
};
|
||||
|
||||
let newTask = reactive<NewTask>(_.cloneDeep(newTaskOrigin));
|
||||
|
||||
const createTaskTypeInterval = async () => {
|
||||
const arr = newTask.cycle;
|
||||
let ps = Number(arr[0]);
|
||||
let pm = Number(arr[1]);
|
||||
let ph = Number(arr[2]);
|
||||
const rs = ps + pm * 60 + ph * 60 * 60;
|
||||
newTask.time = rs.toString();
|
||||
await createRequest();
|
||||
};
|
||||
|
||||
const createTaskTypeCycle = async () => {
|
||||
const weekend = newTask.weekend;
|
||||
if (newTask.objTime === "") throw new Error(t("请选择时间"));
|
||||
if (weekend.length === 0) throw new Error(t("请选择星期"));
|
||||
const time = new Date(newTask.objTime);
|
||||
const h = time.getHours();
|
||||
const m = time.getMinutes();
|
||||
const s = time.getSeconds();
|
||||
newTask.time = `${s} ${m} ${h} * * ${weekend.join(",")}`;
|
||||
await createRequest();
|
||||
};
|
||||
|
||||
const createTaskTypeSpecify = async () => {
|
||||
if (newTask.objTime === "") throw new Error(t("请选择时间"));
|
||||
const time = newTask.objTime as unknown as Dayjs;
|
||||
const mm = time.month() + 1;
|
||||
const dd = time.date();
|
||||
const h = time.hour();
|
||||
const m = time.minute();
|
||||
const s = time.second();
|
||||
newTask.time = `${s} ${m} ${h} ${dd} ${mm} *`;
|
||||
await createRequest();
|
||||
};
|
||||
|
||||
const { state, isLoading, execute } = scheduleCreate();
|
||||
const createRequest = async () => {
|
||||
try {
|
||||
if (!newTask.count) newTask.count = "-1";
|
||||
await execute({
|
||||
params: {
|
||||
remote_uuid: props.daemonId,
|
||||
uuid: props.instanceId
|
||||
},
|
||||
data: newTask
|
||||
});
|
||||
if (state.value) {
|
||||
emit("getScheduleList");
|
||||
notification.success({
|
||||
message: t("创建成功")
|
||||
});
|
||||
newTask = reactive(_.cloneDeep(newTaskOrigin));
|
||||
open.value = false;
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
message.error(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
try {
|
||||
if (newTask.type === ScheduleCreateType.INTERVAL) await createTaskTypeInterval();
|
||||
if (newTask.type === ScheduleCreateType.CYCLE) await createTaskTypeCycle();
|
||||
if (newTask.type === ScheduleCreateType.SPECIFY) await createTaskTypeSpecify();
|
||||
} catch (err: any) {
|
||||
return message.error(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="open"
|
||||
centered
|
||||
:mask-closable="false"
|
||||
:title="t('新增计划任务')"
|
||||
:confirm-loading="isLoading"
|
||||
:destroy-on-close="true"
|
||||
:ok-text="t('保存')"
|
||||
@ok="submit"
|
||||
>
|
||||
<a-form-item>
|
||||
<a-typography-title :level="5">{{ t("计划任务名字") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("必填,且必须唯一") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-input v-model:value="newTask.name" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-typography-title :level="5">{{ t("任务动作 / 类型") }}</a-typography-title>
|
||||
<a-row :gutter="20">
|
||||
<a-col :xs="24" :md="12" :offset="0" :class="{ 'mb-10': isPhone }">
|
||||
<a-select
|
||||
v-model:value="newTask.action"
|
||||
:placeholder="t('请选择')"
|
||||
:dropdown-match-select-width="false"
|
||||
>
|
||||
<a-select-option v-for="(action, i) in ScheduleAction" :key="i" :value="i">
|
||||
{{ action }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :xs="24" :md="12" :offset="0">
|
||||
<a-select
|
||||
v-model:value="newTask.type"
|
||||
:placeholder="t('请选择')"
|
||||
:dropdown-match-select-width="false"
|
||||
>
|
||||
<a-select-option v-for="(type, i) in ScheduleType" :key="i" :value="i">
|
||||
{{ type }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="newTask.type === ScheduleCreateType.INTERVAL">
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text>
|
||||
{{ t("每隔一定时间将执行一次,具体间隔可以仔细设置") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-row :gutter="20">
|
||||
<a-col :xs="24" :md="6" :offset="0" :class="{ 'mb-10': isPhone }">
|
||||
<a-input
|
||||
v-model:value="newTask.cycle[2]"
|
||||
:placeholder="t('不可为空,请写 0 代表每隔 0 时')"
|
||||
:addon-after="t('时')"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :xs="24" :md="6" :offset="0" :class="{ 'mb-10': isPhone }">
|
||||
<a-input
|
||||
v-model:value="newTask.cycle[1]"
|
||||
:placeholder="t('不可为空,请写 0 代表每隔 0 时')"
|
||||
:addon-after="t('分')"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :xs="24" :md="6" :offset="0" :class="{ 'mb-10': isPhone }">
|
||||
<a-input
|
||||
v-model:value="newTask.cycle[0]"
|
||||
:placeholder="t('不可为空,请写 0 代表每隔 0 时')"
|
||||
:addon-after="t('秒')"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :xs="24" :md="6" :offset="0">
|
||||
<a-input v-model:value="newTask.count" :placeholder="t('执行次数,留空无限')" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
|
||||
<div v-if="newTask.type === ScheduleCreateType.CYCLE">
|
||||
<a-form-item>
|
||||
<a-typography-title :level="5">{{ t("触发时间") }}</a-typography-title>
|
||||
<a-time-picker
|
||||
v-model:value="newTask.objTime"
|
||||
size="large"
|
||||
:placeholder="$t('具体时间点')"
|
||||
class="w-100"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-checkbox-group v-model:value="newTask.weekend" :options="weeks" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-typography-title :level="5">{{ t("执行次数") }}</a-typography-title>
|
||||
<a-input v-model:value="newTask.count" :placeholder="t('留空无限')" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
<a-form-item v-if="newTask.type === ScheduleCreateType.SPECIFY">
|
||||
<a-typography-title :level="5">{{ t("请选择日期和时间") }}</a-typography-title>
|
||||
<a-date-picker v-model:value="newTask.objTime" show-time size="large" class="w-100" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-typography-title :level="5">{{ t("任务有效负载") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("比如命令,文件名或其他参数等") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-input v-model:value="newTask.payload" />
|
||||
</a-form-item>
|
||||
</a-modal>
|
||||
</template>
|
@ -256,6 +256,28 @@ function getDefaultFrontendLayoutConfig(): IPageLayoutConfig[] {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
page: "/instances/schedule",
|
||||
items: [
|
||||
{
|
||||
id: getRandomId(),
|
||||
meta: {},
|
||||
type: "Schedule",
|
||||
title: t("计划任务"),
|
||||
width: 12,
|
||||
height: LayoutCardHeight.AUTO,
|
||||
disableDelete: true
|
||||
},
|
||||
{
|
||||
id: getRandomId(),
|
||||
meta: {},
|
||||
type: "EmptyCard",
|
||||
title: "",
|
||||
width: 12,
|
||||
height: LayoutCardHeight.MINI
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
page: "/users",
|
||||
items: [
|
||||
|
Loading…
x
Reference in New Issue
Block a user