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,
|
immediate: false,
|
||||||
shallow: false as any,
|
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">
|
<script setup lang="ts">
|
||||||
import CardPanel from "@/components/CardPanel.vue";
|
import CardPanel from "@/components/CardPanel.vue";
|
||||||
import type { LayoutCard } from "@/types/index";
|
import type { LayoutCard } from "@/types/index";
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted, computed } from "vue";
|
||||||
import { t } from "@/lang/i18n";
|
import { t } from "@/lang/i18n";
|
||||||
import {
|
import {
|
||||||
SearchOutlined,
|
SearchOutlined,
|
||||||
@ -17,6 +17,10 @@ import type { NodeStatus } from "../types/index";
|
|||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
import { computeNodeName } from "../tools/nodes";
|
import { computeNodeName } from "../tools/nodes";
|
||||||
import Loading from "@/components/Loading.vue";
|
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<{
|
const props = defineProps<{
|
||||||
card: LayoutCard;
|
card: LayoutCard;
|
||||||
@ -33,6 +37,15 @@ const currentRemoteNode = ref<NodeStatus>();
|
|||||||
const { execute: getNodes, state: nodes } = remoteNodeList();
|
const { execute: getNodes, state: nodes } = remoteNodeList();
|
||||||
const { execute: getInstances, state: instances, isLoading } = remoteInstances();
|
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 () => {
|
const initNodes = async () => {
|
||||||
await getNodes();
|
await getNodes();
|
||||||
if (!nodes.value?.length) {
|
if (!nodes.value?.length) {
|
||||||
@ -133,7 +146,7 @@ const handleChangeNode = () => {};
|
|||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<template v-if="!isLoading">
|
<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
|
<CardPanel
|
||||||
class="instance-card"
|
class="instance-card"
|
||||||
style="height: 100%"
|
style="height: 100%"
|
||||||
@ -144,11 +157,21 @@ const handleChangeNode = () => {};
|
|||||||
<a-typography-paragraph>
|
<a-typography-paragraph>
|
||||||
<div>
|
<div>
|
||||||
{{ t("状态:") }}
|
{{ 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>
|
||||||
<div>
|
<div>
|
||||||
{{ t("类型:") }}
|
{{ t("类型:") }}
|
||||||
{{ item.config.type }}
|
{{ item.moreInfo?.instanceTypeText }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ t("启动时间:") }}
|
{{ t("启动时间:") }}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { LayoutCard } from "@/types";
|
import type { LayoutCard } from "@/types";
|
||||||
import { useLayoutCardTools } from "../../hooks/useCardTools";
|
import { useLayoutCardTools } from "../../hooks/useCardTools";
|
||||||
import { getInstanceInfo } from "@/services/apis/instance";
|
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import { t } from "@/lang/i18n";
|
import { t } from "@/lang/i18n";
|
||||||
|
import { useInstanceInfo } from "@/hooks/useInstance";
|
||||||
|
import { CheckCircleOutlined, ExclamationCircleOutlined } from "@ant-design/icons-vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
card: LayoutCard;
|
card: LayoutCard;
|
||||||
@ -14,7 +15,12 @@ const { getMetaOrRouteValue } = useLayoutCardTools(props.card);
|
|||||||
const instanceId = getMetaOrRouteValue("instanceId");
|
const instanceId = getMetaOrRouteValue("instanceId");
|
||||||
const daemonId = getMetaOrRouteValue("daemonId");
|
const daemonId = getMetaOrRouteValue("daemonId");
|
||||||
|
|
||||||
const { execute, state: instanceInfo } = getInstanceInfo();
|
const { statusText, isRunning, isStopped, instanceTypeText, instanceInfo, execute } =
|
||||||
|
useInstanceInfo({
|
||||||
|
instanceId,
|
||||||
|
daemonId,
|
||||||
|
autoRefresh: true
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (instanceId && daemonId) {
|
if (instanceId && daemonId) {
|
||||||
@ -37,10 +43,21 @@ onMounted(async () => {
|
|||||||
<a-typography-paragraph>
|
<a-typography-paragraph>
|
||||||
{{ t("名称:") }}{{ instanceInfo?.config.nickname }}
|
{{ t("名称:") }}{{ instanceInfo?.config.nickname }}
|
||||||
</a-typography-paragraph>
|
</a-typography-paragraph>
|
||||||
|
<a-typography-paragraph> {{ t("类型:") }}{{ instanceTypeText }} </a-typography-paragraph>
|
||||||
<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>
|
||||||
<a-typography-paragraph> {{ t("状态:") }}{{ instanceInfo?.status }} </a-typography-paragraph>
|
|
||||||
<a-typography-paragraph>
|
<a-typography-paragraph>
|
||||||
{{ t("最后启动:") }}{{ instanceInfo?.config.lastDatetime }}
|
{{ t("最后启动:") }}{{ instanceInfo?.config.lastDatetime }}
|
||||||
</a-typography-paragraph>
|
</a-typography-paragraph>
|
||||||
|
@ -3,11 +3,12 @@ import CardPanel from "@/components/CardPanel.vue";
|
|||||||
import { t } from "@/lang/i18n";
|
import { t } from "@/lang/i18n";
|
||||||
import type { LayoutCard } from "@/types";
|
import type { LayoutCard } from "@/types";
|
||||||
import {
|
import {
|
||||||
|
CloudDownloadOutlined,
|
||||||
CodeOutlined,
|
CodeOutlined,
|
||||||
DownOutlined,
|
DownOutlined,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
PlayCircleOutlined,
|
PlayCircleOutlined,
|
||||||
PlaySquareOutlined
|
ReconciliationOutlined
|
||||||
} from "@ant-design/icons-vue";
|
} from "@ant-design/icons-vue";
|
||||||
import { arrayFilter } from "../../tools/array";
|
import { arrayFilter } from "../../tools/array";
|
||||||
import { useTerminal } from "../../hooks/useTerminal";
|
import { useTerminal } from "../../hooks/useTerminal";
|
||||||
@ -16,68 +17,77 @@ import { useLayoutCardTools } from "@/hooks/useCardTools";
|
|||||||
import { getRandomId } from "../../tools/randId";
|
import { getRandomId } from "../../tools/randId";
|
||||||
import IconBtn from "@/components/IconBtn.vue";
|
import IconBtn from "@/components/IconBtn.vue";
|
||||||
import { openInstance, stopInstance } from "@/services/apis/instance";
|
import { openInstance, stopInstance } from "@/services/apis/instance";
|
||||||
|
import { CloseOutlined } from "@ant-design/icons-vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
card: LayoutCard;
|
card: LayoutCard;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { getMetaOrRouteValue } = useLayoutCardTools(props.card);
|
const { getMetaOrRouteValue } = useLayoutCardTools(props.card);
|
||||||
|
const { execute, initTerminalWindow, sendCommand } = useTerminal();
|
||||||
|
|
||||||
const instanceId = getMetaOrRouteValue("instanceId");
|
const instanceId = getMetaOrRouteValue("instanceId");
|
||||||
const daemonId = getMetaOrRouteValue("daemonId");
|
const daemonId = getMetaOrRouteValue("daemonId");
|
||||||
const terminalDomId = computed(() => `terminal-window-${getRandomId()}`);
|
const terminalDomId = computed(() => `terminal-window-${getRandomId()}`);
|
||||||
const commandInputValue = ref("");
|
const commandInputValue = ref("");
|
||||||
|
|
||||||
const quickOperations = arrayFilter([
|
const quickOperations = computed(() =>
|
||||||
{
|
arrayFilter([
|
||||||
title: t("开启程序"),
|
{
|
||||||
icon: PlayCircleOutlined,
|
title: t("开启程序"),
|
||||||
click: () => {
|
icon: PlayCircleOutlined,
|
||||||
openInstance().execute({
|
click: () => {
|
||||||
params: {
|
openInstance().execute({
|
||||||
uuid: instanceId || "",
|
params: {
|
||||||
remote_uuid: daemonId || ""
|
uuid: instanceId || "",
|
||||||
}
|
remote_uuid: daemonId || ""
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
props: {}
|
||||||
},
|
},
|
||||||
props: {}
|
{
|
||||||
},
|
title: t("关闭程序"),
|
||||||
{
|
icon: PauseCircleOutlined,
|
||||||
title: t("关闭程序"),
|
click: () => {
|
||||||
icon: PauseCircleOutlined,
|
stopInstance().execute({
|
||||||
click: () => {
|
params: {
|
||||||
stopInstance().execute({
|
uuid: instanceId || "",
|
||||||
params: {
|
remote_uuid: daemonId || ""
|
||||||
uuid: instanceId || "",
|
}
|
||||||
remote_uuid: daemonId || ""
|
});
|
||||||
}
|
},
|
||||||
});
|
props: {
|
||||||
},
|
danger: true
|
||||||
props: {
|
}
|
||||||
danger: true
|
|
||||||
}
|
}
|
||||||
}
|
])
|
||||||
]);
|
);
|
||||||
|
|
||||||
const instanceOperations = arrayFilter([
|
const instanceOperations = arrayFilter([
|
||||||
{
|
{
|
||||||
title: t("重启"),
|
title: t("重启"),
|
||||||
icon: PlaySquareOutlined,
|
icon: ReconciliationOutlined,
|
||||||
|
click: () => {
|
||||||
|
console.log(3);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("终止"),
|
||||||
|
icon: CloseOutlined,
|
||||||
click: () => {
|
click: () => {
|
||||||
console.log(3);
|
console.log(3);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("更新"),
|
title: t("更新"),
|
||||||
icon: PlaySquareOutlined,
|
icon: CloudDownloadOutlined,
|
||||||
click: () => {
|
click: () => {
|
||||||
console.log(4);
|
console.log(4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { execute, initTerminalWindow, sendCommand } = useTerminal();
|
|
||||||
|
|
||||||
const handleSendCommand = () => {
|
const handleSendCommand = () => {
|
||||||
sendCommand(commandInputValue.value);
|
sendCommand(commandInputValue.value);
|
||||||
commandInputValue.value = "";
|
commandInputValue.value = "";
|
||||||
@ -88,6 +98,7 @@ const initTerminal = () => {
|
|||||||
if (dom) {
|
if (dom) {
|
||||||
initTerminalWindow(dom);
|
initTerminalWindow(dom);
|
||||||
}
|
}
|
||||||
|
throw new Error("init terminal failed");
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@ -97,7 +108,6 @@ onMounted(async () => {
|
|||||||
daemonId
|
daemonId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initTerminal();
|
initTerminal();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user