mirror of
https://github.com/MCSManager/MCSManager.git
synced 2025-01-12 14:54:34 +08:00
Feat: useInstanceState hook
This commit is contained in:
parent
276dc729ad
commit
386dc0c7fb
@ -14,7 +14,8 @@ export function executeRequest<T>(config: RequestConfig) {
|
||||
{
|
||||
immediate: false,
|
||||
shallow: false as any,
|
||||
throwError: true
|
||||
throwError: true,
|
||||
resetOnExecute: false
|
||||
}
|
||||
);
|
||||
}
|
||||
|
138
frontend/src/hooks/useInstance.ts
Normal file
138
frontend/src/hooks/useInstance.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import type { InstanceDetail, MapData } from "@/types";
|
||||
import { t } from "@/lang/i18n";
|
||||
import { computed, onMounted, onUnmounted, ref, type Ref } from "vue";
|
||||
import { getInstanceInfo } from "@/services/apis/instance";
|
||||
|
||||
export const INSTANCE_STATUS_TEXT: MapData<string> = {
|
||||
"-1": t("维护中"),
|
||||
"0": t("未运行"),
|
||||
"1": t("停止中"),
|
||||
"2": t("启动中"),
|
||||
"3": t("运行中")
|
||||
};
|
||||
|
||||
export const TYPE_UNIVERSAL = "universal";
|
||||
export const TYPE_WEB_SHELL = "universal/web_shell";
|
||||
export const TYPE_MINECRAFT_MCDR = "universal/mcdr";
|
||||
export const TYPE_MINECRAFT_JAVA = "minecraft/java";
|
||||
export const TYPE_MINECRAFT_BUKKIT = "minecraft/java/bukkit";
|
||||
export const TYPE_MINECRAFT_SPIGOT = "minecraft/java/spigot";
|
||||
export const TYPE_MINECRAFT_PAPER = "minecraft/java/paper";
|
||||
export const TYPE_MINECRAFT_FORGE = "minecraft/java/forge";
|
||||
export const TYPE_MINECRAFT_FABRIC = "minecraft/java/fabric";
|
||||
export const TYPE_MINECRAFT_BUNGEECORD = "minecraft/java/bungeecord";
|
||||
export const TYPE_MINECRAFT_VELOCITY = "minecraft/java/velocity";
|
||||
export const TYPE_MINECRAFT_GEYSER = "minecraft/java/geyser";
|
||||
export const TYPE_MINECRAFT_SPONGE = "minecraft/java/sponge";
|
||||
export const TYPE_MINECRAFT_MOHIST = "minecraft/java/mohist";
|
||||
export const TYPE_MINECRAFT_BEDROCK = "minecraft/bedrock";
|
||||
export const TYPE_MINECRAFT_BDS = "minecraft/bedrock/bds";
|
||||
export const TYPE_MINECRAFT_NUKKIT = "minecraft/bedrock/nukkit";
|
||||
export const TYPE_STEAM_SERVER_UNIVERSAL = "steam/universal";
|
||||
|
||||
export const INSTANCE_TYPE_TRANSLATION: MapData<string> = {
|
||||
[TYPE_UNIVERSAL]: "General Console Application",
|
||||
[TYPE_STEAM_SERVER_UNIVERSAL]: "Steam Game Server",
|
||||
[TYPE_MINECRAFT_JAVA]: "MC Java Edition",
|
||||
[TYPE_MINECRAFT_BEDROCK]: "MC Bedrock Edition",
|
||||
[TYPE_MINECRAFT_SPIGOT]: "MC Spigot",
|
||||
[TYPE_MINECRAFT_PAPER]: "MC Paper",
|
||||
[TYPE_MINECRAFT_BUNGEECORD]: "MC BungeeCord",
|
||||
[TYPE_MINECRAFT_VELOCITY]: "MC Velocity",
|
||||
[TYPE_MINECRAFT_BDS]: "MC Bedrock",
|
||||
[TYPE_MINECRAFT_SPONGE]: "MC Sponge",
|
||||
[TYPE_MINECRAFT_FORGE]: "MC Forge",
|
||||
[TYPE_MINECRAFT_FABRIC]: "MC Fabric",
|
||||
[TYPE_MINECRAFT_BUKKIT]: "MC Bukkit",
|
||||
[TYPE_MINECRAFT_GEYSER]: "MC Geyser",
|
||||
[TYPE_MINECRAFT_MCDR]: "MC MCDR",
|
||||
[TYPE_WEB_SHELL]: "Web Shell"
|
||||
};
|
||||
|
||||
interface Params {
|
||||
instanceId?: string;
|
||||
daemonId?: string;
|
||||
autoRefresh?: boolean;
|
||||
instanceInfo?: Ref<InstanceDetail | undefined>;
|
||||
}
|
||||
|
||||
export interface InstanceMoreDetail extends InstanceDetail {
|
||||
moreInfo?: {
|
||||
isRunning: boolean;
|
||||
isStopped: boolean;
|
||||
instanceTypeText: string;
|
||||
statusText: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function useInstanceMoreDetail(info: InstanceMoreDetail): InstanceMoreDetail {
|
||||
const { statusText, isRunning, isStopped, instanceTypeText } = useInstanceInfo({
|
||||
instanceInfo: ref(info)
|
||||
});
|
||||
|
||||
info.moreInfo = {
|
||||
statusText: statusText.value,
|
||||
isRunning: isRunning.value,
|
||||
isStopped: isStopped.value,
|
||||
instanceTypeText: instanceTypeText.value
|
||||
};
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
export function useInstanceInfo(params: Params) {
|
||||
let task: NodeJS.Timer | undefined;
|
||||
const { daemonId, instanceId, instanceInfo, autoRefresh } = params;
|
||||
|
||||
const { execute, state, isLoading, isReady } = getInstanceInfo();
|
||||
|
||||
let finalState = state;
|
||||
if (instanceInfo) finalState = instanceInfo;
|
||||
|
||||
const isRunning = computed(() => finalState?.value?.status === 3);
|
||||
const isStopped = computed(() => finalState?.value?.status === 0);
|
||||
const instanceTypeText = computed(() => {
|
||||
return INSTANCE_TYPE_TRANSLATION[String(finalState?.value?.config.type)] || t("未知类型");
|
||||
});
|
||||
const statusText = computed(
|
||||
() => String(INSTANCE_STATUS_TEXT[String(finalState?.value?.status)]) || t("未知状态")
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
if (instanceId && daemonId) {
|
||||
await execute({
|
||||
params: {
|
||||
uuid: instanceId,
|
||||
remote_uuid: daemonId
|
||||
}
|
||||
});
|
||||
|
||||
if (autoRefresh) {
|
||||
task = setInterval(async () => {
|
||||
await execute({
|
||||
params: {
|
||||
uuid: instanceId,
|
||||
remote_uuid: daemonId
|
||||
}
|
||||
});
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (task) clearInterval(task);
|
||||
task = undefined;
|
||||
});
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
isReady,
|
||||
instanceInfo: state,
|
||||
execute,
|
||||
statusText,
|
||||
isRunning,
|
||||
isStopped,
|
||||
instanceTypeText
|
||||
};
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import CardPanel from "@/components/CardPanel.vue";
|
||||
import type { LayoutCard } from "@/types/index";
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import {
|
||||
SearchOutlined,
|
||||
@ -17,6 +17,10 @@ import type { NodeStatus } from "../types/index";
|
||||
import { message } from "ant-design-vue";
|
||||
import { computeNodeName } from "../tools/nodes";
|
||||
import Loading from "@/components/Loading.vue";
|
||||
import { useInstanceInfo } from "@/hooks/useInstance";
|
||||
import type { InstanceMoreDetail } from "../hooks/useInstance";
|
||||
import { CheckCircleOutlined, ExclamationCircleOutlined } from "@ant-design/icons-vue";
|
||||
import { useInstanceMoreDetail } from "../hooks/useInstance";
|
||||
|
||||
const props = defineProps<{
|
||||
card: LayoutCard;
|
||||
@ -33,6 +37,15 @@ const currentRemoteNode = ref<NodeStatus>();
|
||||
const { execute: getNodes, state: nodes } = remoteNodeList();
|
||||
const { execute: getInstances, state: instances, isLoading } = remoteInstances();
|
||||
|
||||
const instancesMoreInfo = computed(() => {
|
||||
const newInstances: InstanceMoreDetail[] = [];
|
||||
for (const instance of instances.value?.data || []) {
|
||||
const instanceMoreInfo = useInstanceMoreDetail(instance);
|
||||
newInstances.push(instanceMoreInfo);
|
||||
}
|
||||
return newInstances;
|
||||
});
|
||||
|
||||
const initNodes = async () => {
|
||||
await getNodes();
|
||||
if (!nodes.value?.length) {
|
||||
@ -133,7 +146,7 @@ const handleChangeNode = () => {};
|
||||
</div>
|
||||
</a-col>
|
||||
<template v-if="!isLoading">
|
||||
<a-col v-for="item in instances?.data" :key="item" :span="24" :md="6">
|
||||
<a-col v-for="item in instancesMoreInfo" :key="item" :span="24" :md="6">
|
||||
<CardPanel
|
||||
class="instance-card"
|
||||
style="height: 100%"
|
||||
@ -144,11 +157,21 @@ const handleChangeNode = () => {};
|
||||
<a-typography-paragraph>
|
||||
<div>
|
||||
{{ t("状态:") }}
|
||||
{{ item.status }}
|
||||
<span v-if="item.moreInfo?.isRunning" class="color-success">
|
||||
<CheckCircleOutlined />
|
||||
{{ item.moreInfo?.statusText }}
|
||||
</span>
|
||||
<span v-else-if="item.moreInfo?.isStopped" class="color-info">
|
||||
{{ item.moreInfo?.statusText }}
|
||||
</span>
|
||||
<span v-else>
|
||||
<ExclamationCircleOutlined />
|
||||
{{ item.moreInfo?.statusText }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ t("类型:") }}
|
||||
{{ item.config.type }}
|
||||
{{ item.moreInfo?.instanceTypeText }}
|
||||
</div>
|
||||
<div>
|
||||
{{ t("启动时间:") }}
|
||||
|
@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { LayoutCard } from "@/types";
|
||||
import { useLayoutCardTools } from "../../hooks/useCardTools";
|
||||
import { getInstanceInfo } from "@/services/apis/instance";
|
||||
import { onMounted } from "vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import { useInstanceInfo } from "@/hooks/useInstance";
|
||||
import { CheckCircleOutlined, ExclamationCircleOutlined } from "@ant-design/icons-vue";
|
||||
|
||||
const props = defineProps<{
|
||||
card: LayoutCard;
|
||||
@ -14,7 +15,12 @@ const { getMetaOrRouteValue } = useLayoutCardTools(props.card);
|
||||
const instanceId = getMetaOrRouteValue("instanceId");
|
||||
const daemonId = getMetaOrRouteValue("daemonId");
|
||||
|
||||
const { execute, state: instanceInfo } = getInstanceInfo();
|
||||
const { statusText, isRunning, isStopped, instanceTypeText, instanceInfo, execute } =
|
||||
useInstanceInfo({
|
||||
instanceId,
|
||||
daemonId,
|
||||
autoRefresh: true
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
if (instanceId && daemonId) {
|
||||
@ -37,10 +43,21 @@ onMounted(async () => {
|
||||
<a-typography-paragraph>
|
||||
{{ t("名称:") }}{{ instanceInfo?.config.nickname }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph> {{ t("类型:") }}{{ instanceTypeText }} </a-typography-paragraph>
|
||||
<a-typography-paragraph>
|
||||
{{ t("类型:") }}{{ instanceInfo?.config.type }}
|
||||
<span>{{ t("状态:") }}</span>
|
||||
<span v-if="isRunning" class="color-success">
|
||||
<CheckCircleOutlined />
|
||||
{{ statusText }}
|
||||
</span>
|
||||
<span v-else-if="isStopped" class="color-info">
|
||||
<ExclamationCircleOutlined />
|
||||
{{ statusText }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ statusText }}
|
||||
</span>
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph> {{ t("状态:") }}{{ instanceInfo?.status }} </a-typography-paragraph>
|
||||
<a-typography-paragraph>
|
||||
{{ t("最后启动:") }}{{ instanceInfo?.config.lastDatetime }}
|
||||
</a-typography-paragraph>
|
||||
|
@ -3,11 +3,12 @@ import CardPanel from "@/components/CardPanel.vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import type { LayoutCard } from "@/types";
|
||||
import {
|
||||
CloudDownloadOutlined,
|
||||
CodeOutlined,
|
||||
DownOutlined,
|
||||
PauseCircleOutlined,
|
||||
PlayCircleOutlined,
|
||||
PlaySquareOutlined
|
||||
ReconciliationOutlined
|
||||
} from "@ant-design/icons-vue";
|
||||
import { arrayFilter } from "../../tools/array";
|
||||
import { useTerminal } from "../../hooks/useTerminal";
|
||||
@ -16,68 +17,77 @@ import { useLayoutCardTools } from "@/hooks/useCardTools";
|
||||
import { getRandomId } from "../../tools/randId";
|
||||
import IconBtn from "@/components/IconBtn.vue";
|
||||
import { openInstance, stopInstance } from "@/services/apis/instance";
|
||||
import { CloseOutlined } from "@ant-design/icons-vue";
|
||||
|
||||
const props = defineProps<{
|
||||
card: LayoutCard;
|
||||
}>();
|
||||
|
||||
const { getMetaOrRouteValue } = useLayoutCardTools(props.card);
|
||||
const { execute, initTerminalWindow, sendCommand } = useTerminal();
|
||||
|
||||
const instanceId = getMetaOrRouteValue("instanceId");
|
||||
const daemonId = getMetaOrRouteValue("daemonId");
|
||||
const terminalDomId = computed(() => `terminal-window-${getRandomId()}`);
|
||||
const commandInputValue = ref("");
|
||||
|
||||
const quickOperations = arrayFilter([
|
||||
{
|
||||
title: t("开启程序"),
|
||||
icon: PlayCircleOutlined,
|
||||
click: () => {
|
||||
openInstance().execute({
|
||||
params: {
|
||||
uuid: instanceId || "",
|
||||
remote_uuid: daemonId || ""
|
||||
}
|
||||
});
|
||||
const quickOperations = computed(() =>
|
||||
arrayFilter([
|
||||
{
|
||||
title: t("开启程序"),
|
||||
icon: PlayCircleOutlined,
|
||||
click: () => {
|
||||
openInstance().execute({
|
||||
params: {
|
||||
uuid: instanceId || "",
|
||||
remote_uuid: daemonId || ""
|
||||
}
|
||||
});
|
||||
},
|
||||
props: {}
|
||||
},
|
||||
props: {}
|
||||
},
|
||||
{
|
||||
title: t("关闭程序"),
|
||||
icon: PauseCircleOutlined,
|
||||
click: () => {
|
||||
stopInstance().execute({
|
||||
params: {
|
||||
uuid: instanceId || "",
|
||||
remote_uuid: daemonId || ""
|
||||
}
|
||||
});
|
||||
},
|
||||
props: {
|
||||
danger: true
|
||||
{
|
||||
title: t("关闭程序"),
|
||||
icon: PauseCircleOutlined,
|
||||
click: () => {
|
||||
stopInstance().execute({
|
||||
params: {
|
||||
uuid: instanceId || "",
|
||||
remote_uuid: daemonId || ""
|
||||
}
|
||||
});
|
||||
},
|
||||
props: {
|
||||
danger: true
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
])
|
||||
);
|
||||
|
||||
const instanceOperations = arrayFilter([
|
||||
{
|
||||
title: t("重启"),
|
||||
icon: PlaySquareOutlined,
|
||||
icon: ReconciliationOutlined,
|
||||
click: () => {
|
||||
console.log(3);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: t("终止"),
|
||||
icon: CloseOutlined,
|
||||
click: () => {
|
||||
console.log(3);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: t("更新"),
|
||||
icon: PlaySquareOutlined,
|
||||
icon: CloudDownloadOutlined,
|
||||
click: () => {
|
||||
console.log(4);
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const { execute, initTerminalWindow, sendCommand } = useTerminal();
|
||||
|
||||
const handleSendCommand = () => {
|
||||
sendCommand(commandInputValue.value);
|
||||
commandInputValue.value = "";
|
||||
@ -88,6 +98,7 @@ const initTerminal = () => {
|
||||
if (dom) {
|
||||
initTerminalWindow(dom);
|
||||
}
|
||||
throw new Error("init terminal failed");
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
@ -97,7 +108,6 @@ onMounted(async () => {
|
||||
daemonId
|
||||
});
|
||||
}
|
||||
|
||||
initTerminal();
|
||||
});
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user