mirror of
https://github.com/MCSManager/MCSManager.git
synced 2024-12-03 07:20:16 +08:00
Merge pull request #1031 from MCSManager/abandon
Feat: optimize creation methods
This commit is contained in:
commit
314aec6661
2
frontend/components.d.ts
vendored
2
frontend/components.d.ts
vendored
@ -27,6 +27,7 @@ declare module 'vue' {
|
||||
APagination: typeof import('ant-design-vue/es')['Pagination']
|
||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||
AppHeader: typeof import('./src/components/AppHeader.vue')['default']
|
||||
AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
|
||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||
ARow: typeof import('ant-design-vue/es')['Row']
|
||||
@ -46,6 +47,7 @@ declare module 'vue' {
|
||||
ATypographyText: typeof import('ant-design-vue/es')['TypographyText']
|
||||
ATypographyTitle: typeof import('ant-design-vue/es')['TypographyTitle']
|
||||
AUpload: typeof import('ant-design-vue/es')['Upload']
|
||||
AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
|
||||
BetweenMenus: typeof import('./src/components/BetweenMenus.vue')['default']
|
||||
CardError: typeof import('./src/components/CardError.vue')['default']
|
||||
CardOperator: typeof import('./src/components/CardOperator.vue')['default']
|
||||
|
@ -11,6 +11,9 @@ import {
|
||||
IdcardTwoTone,
|
||||
NodeIndexOutlined,
|
||||
ShoppingCartOutlined,
|
||||
CloudUploadOutlined,
|
||||
FileZipOutlined,
|
||||
FileExcelOutlined,
|
||||
TransactionOutlined,
|
||||
SmileTwoTone,
|
||||
CodeOutlined,
|
||||
@ -29,6 +32,7 @@ export enum QUICKSTART_ACTION_TYPE {
|
||||
|
||||
export enum QUICKSTART_METHOD {
|
||||
FAST = "FAST",
|
||||
FILE = "FILE",
|
||||
IMPORT = "IMPORT",
|
||||
SELECT = "SELECT",
|
||||
EXIST = "EXIST"
|
||||
@ -111,20 +115,25 @@ export function useQuickStartFlow() {
|
||||
formData.appType === QUICKSTART_ACTION_TYPE.Minecraft ||
|
||||
formData.appType === QUICKSTART_ACTION_TYPE.Bedrock
|
||||
},
|
||||
{
|
||||
title: t("上传单个服务端软件"),
|
||||
key: QUICKSTART_METHOD.FILE,
|
||||
icon: CloudUploadOutlined
|
||||
},
|
||||
{
|
||||
title: t("上传服务端文件压缩包"),
|
||||
key: QUICKSTART_METHOD.IMPORT,
|
||||
icon: ShoppingCartOutlined
|
||||
icon: FileZipOutlined
|
||||
},
|
||||
{
|
||||
title: t("选择服务器现有目录"),
|
||||
key: QUICKSTART_METHOD.SELECT,
|
||||
icon: TransactionOutlined
|
||||
icon: FolderOpenOutlined
|
||||
},
|
||||
{
|
||||
title: t("无需额外文件"),
|
||||
key: QUICKSTART_METHOD.EXIST,
|
||||
icon: TransactionOutlined
|
||||
icon: FileExcelOutlined
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
187
frontend/src/services/apis/fileManager.ts
Normal file
187
frontend/src/services/apis/fileManager.ts
Normal file
@ -0,0 +1,187 @@
|
||||
import { useDefineApi } from "@/stores/useDefineApi";
|
||||
|
||||
// 获取文件列表
|
||||
export const getFileList = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
remote_uuid: string;
|
||||
uuid: string;
|
||||
target: string;
|
||||
page: number;
|
||||
page_size: number;
|
||||
file_name: string;
|
||||
};
|
||||
},
|
||||
{
|
||||
items: {
|
||||
name: string;
|
||||
size: number;
|
||||
time: string;
|
||||
type: number;
|
||||
mode: number;
|
||||
}[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
absolutePath: string;
|
||||
}
|
||||
>({
|
||||
url: "/api/files/list",
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
// 获取文件状态
|
||||
export const getFileStatus = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
},
|
||||
{
|
||||
instanceFileTask: number;
|
||||
globalFileTask: number;
|
||||
platform: string;
|
||||
isGlobalInstance: boolean;
|
||||
dist: string[];
|
||||
}
|
||||
>({
|
||||
url: "/api/files/status",
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
// 新建文件夹
|
||||
export const addFolder = useDefineApi<
|
||||
{
|
||||
data: {
|
||||
target: string;
|
||||
};
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/mkdir",
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
// 新建文件
|
||||
export const touchFile = useDefineApi<
|
||||
{
|
||||
data: {
|
||||
target: string;
|
||||
};
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/touch",
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
// 删除文件
|
||||
export const deleteFile = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
data: {
|
||||
targets: string[];
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files",
|
||||
method: "DELETE"
|
||||
});
|
||||
|
||||
// 复制文件
|
||||
export const copyFile = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
data: {
|
||||
targets: string[][];
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/copy",
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
// 移动文件
|
||||
export const moveFile = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
data: {
|
||||
targets: string[][];
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/move",
|
||||
method: "PUT"
|
||||
});
|
||||
|
||||
// 解压缩
|
||||
export const compressFile = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
data: {
|
||||
type: number;
|
||||
targets: string[] | string;
|
||||
source: string;
|
||||
code: string;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/compress",
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
// 获取文件上传地址
|
||||
export const uploadAddress = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
upload_dir: string;
|
||||
remote_uuid: string;
|
||||
uuid: string;
|
||||
};
|
||||
},
|
||||
{
|
||||
password: string;
|
||||
addr: string;
|
||||
}
|
||||
>({
|
||||
url: "/api/files/upload",
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
// 上传文件
|
||||
export const uploadFile = useDefineApi<
|
||||
{
|
||||
data: FormData;
|
||||
url: string;
|
||||
onUploadProgress: Function;
|
||||
},
|
||||
{}
|
||||
>({
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "multipart/form-data" }
|
||||
});
|
@ -215,159 +215,3 @@ export const connectNode = useDefineApi<
|
||||
url: "/api/service/link_remote_service",
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
// 文件管理
|
||||
// 获取文件列表
|
||||
export const getFileList = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
remote_uuid: string;
|
||||
uuid: string;
|
||||
target: string;
|
||||
page: number;
|
||||
page_size: number;
|
||||
file_name: string;
|
||||
};
|
||||
},
|
||||
{
|
||||
items: {
|
||||
name: string;
|
||||
size: number;
|
||||
time: string;
|
||||
type: number;
|
||||
mode: number;
|
||||
}[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
absolutePath: string;
|
||||
}
|
||||
>({
|
||||
url: "/api/files/list",
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
// 获取文件状态
|
||||
export const getFileStatus = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
},
|
||||
{
|
||||
instanceFileTask: number;
|
||||
globalFileTask: number;
|
||||
platform: string;
|
||||
isGlobalInstance: boolean;
|
||||
dist: string[];
|
||||
}
|
||||
>({
|
||||
url: "/api/files/status",
|
||||
method: "GET"
|
||||
});
|
||||
|
||||
// 新建文件夹
|
||||
export const addFolder = useDefineApi<
|
||||
{
|
||||
data: {
|
||||
target: string;
|
||||
};
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/mkdir",
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
// 新建文件
|
||||
export const touchFile = useDefineApi<
|
||||
{
|
||||
data: {
|
||||
target: string;
|
||||
};
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/touch",
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
// 删除文件
|
||||
export const deleteFile = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
data: {
|
||||
targets: string[];
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files",
|
||||
method: "DELETE"
|
||||
});
|
||||
|
||||
// 复制文件
|
||||
export const copyFile = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
data: {
|
||||
targets: string[][];
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/copy",
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
// 移动文件
|
||||
export const moveFile = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
data: {
|
||||
targets: string[][];
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/move",
|
||||
method: "PUT"
|
||||
});
|
||||
|
||||
// 解压缩
|
||||
export const compressFile = useDefineApi<
|
||||
{
|
||||
params: {
|
||||
uuid: string;
|
||||
remote_uuid: string;
|
||||
};
|
||||
data: {
|
||||
type: number;
|
||||
targets: string[] | string;
|
||||
source: string;
|
||||
code: string;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/compress",
|
||||
method: "POST"
|
||||
});
|
||||
|
@ -144,6 +144,7 @@ export const uploadInstanceFile = useDefineApi<
|
||||
unzip: number;
|
||||
code: string;
|
||||
};
|
||||
data: FormData;
|
||||
url: string;
|
||||
onUploadProgress: Function;
|
||||
},
|
||||
|
@ -1,3 +1,6 @@
|
||||
export function computeNodeName(ip: string, remark?: string) {
|
||||
return remark ? `${remark} - ${ip}` : ip;
|
||||
import { t } from "@/lang/i18n";
|
||||
|
||||
export function computeNodeName(ip: string, available: boolean, remark?: string) {
|
||||
const online = available ? "" : t("(离线)");
|
||||
return remark ? `${remark} - ${ip} ${online}` : `${ip} ${online}`;
|
||||
}
|
||||
|
@ -1,21 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import CardPanel from "@/components/CardPanel.vue";
|
||||
import type { LayoutCard } from "@/types/index";
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { ref, onMounted, computed, reactive } from "vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import {
|
||||
SearchOutlined,
|
||||
DownOutlined,
|
||||
FormOutlined,
|
||||
DatabaseOutlined,
|
||||
AppstoreOutlined
|
||||
AppstoreOutlined,
|
||||
FrownOutlined
|
||||
} from "@ant-design/icons-vue";
|
||||
import BetweenMenus from "@/components/BetweenMenus.vue";
|
||||
import { router } from "@/config/router";
|
||||
import { remoteInstances } from "@/services/apis";
|
||||
import { remoteNodeList } from "../services/apis";
|
||||
import type { NodeStatus } from "../types/index";
|
||||
import { message } from "ant-design-vue";
|
||||
import { message, type ItemType } from "ant-design-vue";
|
||||
import { computeNodeName } from "../tools/nodes";
|
||||
import Loading from "@/components/Loading.vue";
|
||||
import { useInstanceInfo } from "@/hooks/useInstance";
|
||||
@ -53,7 +54,9 @@ const initNodes = async () => {
|
||||
if (!nodes.value?.length) {
|
||||
return message.error(t("TXT_CODE_e3d96a26"));
|
||||
}
|
||||
if (nodes.value?.length > 0) {
|
||||
if (localStorage.getItem("pageSelectedRemote")) {
|
||||
currentRemoteNode.value = JSON.parse(localStorage.pageSelectedRemote);
|
||||
} else {
|
||||
currentRemoteNode.value = nodes.value[0];
|
||||
}
|
||||
};
|
||||
@ -62,6 +65,7 @@ const initInstancesData = async () => {
|
||||
if (!currentRemoteNode.value) {
|
||||
await initNodes();
|
||||
}
|
||||
try {
|
||||
await getInstances({
|
||||
params: {
|
||||
remote_uuid: currentRemoteNode.value?.uuid ?? "",
|
||||
@ -70,6 +74,9 @@ const initInstancesData = async () => {
|
||||
instance_name: operationForm.value.instanceName.trim()
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
return message.error(t("访问远程节点异常"));
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
@ -90,7 +97,15 @@ const toAppDetailPage = (daemonId: string, instanceId: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleChangeNode = () => {};
|
||||
const handleChangeNode = async (item: NodeStatus) => {
|
||||
try {
|
||||
currentRemoteNode.value = item;
|
||||
await initInstancesData();
|
||||
localStorage.setItem("pageSelectedRemote", JSON.stringify(item));
|
||||
} catch (err: any) {
|
||||
console.error(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const toCreateAppPage = () => {
|
||||
router.push({
|
||||
@ -113,10 +128,15 @@ const toCreateAppPage = () => {
|
||||
<template #right>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu @click="handleChangeNode">
|
||||
<a-menu-item v-for="item in nodes" :key="item.uuid">
|
||||
<DatabaseOutlined />
|
||||
{{ computeNodeName(item.ip, item.remarks) }}
|
||||
<a-menu>
|
||||
<a-menu-item
|
||||
v-for="item in nodes"
|
||||
:key="item.uuid"
|
||||
@click="handleChangeNode(item)"
|
||||
>
|
||||
<DatabaseOutlined v-if="item.available" />
|
||||
<FrownOutlined v-else />
|
||||
{{ computeNodeName(item.ip, item.available, item.remarks) }}
|
||||
</a-menu-item>
|
||||
<a-menu-divider />
|
||||
<a-menu-item key="toNodesPage">
|
||||
@ -126,7 +146,13 @@ const toCreateAppPage = () => {
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button class="mr-12">
|
||||
{{ computeNodeName(currentRemoteNode?.ip || "", currentRemoteNode?.remarks) }}
|
||||
{{
|
||||
computeNodeName(
|
||||
currentRemoteNode?.ip || "",
|
||||
currentRemoteNode?.available || true,
|
||||
currentRemoteNode?.remarks
|
||||
)
|
||||
}}
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
|
@ -3,7 +3,7 @@ import CardPanel from "@/components/CardPanel.vue";
|
||||
import type { LayoutCard } from "@/types/index";
|
||||
import { ref, computed, reactive, onMounted, watch, h, createVNode } from "vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import type { TableProps } from "ant-design-vue";
|
||||
import type { TableProps, UploadProps } from "ant-design-vue";
|
||||
import { convertFileSize } from "@/tools/fileSize";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
@ -12,7 +12,8 @@ import {
|
||||
FileOutlined,
|
||||
FolderOutlined,
|
||||
LoadingOutlined,
|
||||
ExclamationCircleOutlined
|
||||
ExclamationCircleOutlined,
|
||||
UploadOutlined
|
||||
} from "@ant-design/icons-vue";
|
||||
import BetweenMenus from "@/components/BetweenMenus.vue";
|
||||
import { useScreen } from "@/hooks/useScreen";
|
||||
@ -26,10 +27,13 @@ import {
|
||||
touchFile as touchFileApi,
|
||||
copyFile as copyFileApi,
|
||||
moveFile as moveFileApi,
|
||||
compressFile as compressFileApi
|
||||
} from "@/services/apis";
|
||||
compressFile as compressFileApi,
|
||||
uploadAddress,
|
||||
uploadFile as uploadFileApi
|
||||
} from "@/services/apis/fileManager";
|
||||
import { throttle } from "lodash";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { parseForwardAddress } from "@/tools/protocol";
|
||||
|
||||
const props = defineProps<{
|
||||
card: LayoutCard;
|
||||
@ -414,6 +418,44 @@ const unzipFile = async (name: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const beforeUpload: UploadProps["beforeUpload"] = async (file) => {
|
||||
await selectedFile(file);
|
||||
return false;
|
||||
};
|
||||
|
||||
const { state: cfg, execute: getCfg } = uploadAddress();
|
||||
const { execute: uploadFile } = uploadFileApi();
|
||||
const percentComplete = ref(0);
|
||||
const selectedFile = async (file: File) => {
|
||||
try {
|
||||
await getCfg({
|
||||
params: {
|
||||
upload_dir: breadcrumbs[breadcrumbs.length - 1].path,
|
||||
remote_uuid: daemonId!,
|
||||
uuid: instanceId!
|
||||
}
|
||||
});
|
||||
if (!cfg.value) throw new Error(t("获取上传地址失败"));
|
||||
|
||||
const uploadFormData = new FormData();
|
||||
uploadFormData.append("file", file);
|
||||
|
||||
await uploadFile({
|
||||
data: uploadFormData,
|
||||
url: `${parseForwardAddress(cfg.value.addr, "http")}/upload/${cfg.value.password}`,
|
||||
onUploadProgress: (progressEvent: any) => {
|
||||
percentComplete.value = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
||||
}
|
||||
});
|
||||
await getFileList();
|
||||
percentComplete.value = 0;
|
||||
return message.success(t("上传成功"));
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
return message.error(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const rowSelection: TableProps["rowSelection"] = {
|
||||
onChange: (selectedRowKeys: any, selectedRows: DataType[]) => {
|
||||
selectionData.value = selectedRows;
|
||||
@ -504,6 +546,22 @@ onMounted(() => {
|
||||
</a-typography-title>
|
||||
</template>
|
||||
<template #right>
|
||||
<a-upload
|
||||
v-if="!screen.isPhone.value"
|
||||
:before-upload="beforeUpload"
|
||||
:max-count="1"
|
||||
:disabled="percentComplete > 0"
|
||||
:show-upload-list="false"
|
||||
>
|
||||
<a-button class="mr-8" type="dashed" :loading="percentComplete > 0">
|
||||
<upload-outlined v-if="percentComplete === 0" />
|
||||
{{
|
||||
percentComplete > 0
|
||||
? t("正在上传:") + percentComplete + "%"
|
||||
: t("TXT_CODE_e00c858c")
|
||||
}}
|
||||
</a-button>
|
||||
</a-upload>
|
||||
<a-button
|
||||
v-if="clipboard?.value && clipboard.value.length > 0"
|
||||
type="dashed"
|
||||
@ -513,13 +571,26 @@ onMounted(() => {
|
||||
>
|
||||
{{ t("TXT_CODE_f0260e51") }}
|
||||
</a-button>
|
||||
<a-button v-else class="mr-8" @click="reloadList()">
|
||||
<a-button v-else class="mr-8" type="default" @click="reloadList()">
|
||||
{{ t("刷新列表") }}
|
||||
</a-button>
|
||||
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1">{{ t("TXT_CODE_e00c858c") }}</a-menu-item>
|
||||
<a-upload
|
||||
v-if="screen.isPhone.value"
|
||||
:before-upload="beforeUpload"
|
||||
:max-count="1"
|
||||
:disabled="percentComplete > 0"
|
||||
:show-upload-list="false"
|
||||
>
|
||||
<a-menu-item key="1" :disabled="percentComplete > 0">
|
||||
{{ percentComplete > 0 ? t("上传中...") : t("TXT_CODE_e00c858c") }}
|
||||
</a-menu-item>
|
||||
<template #itemRender=""></template>
|
||||
</a-upload>
|
||||
|
||||
<a-menu-item key="2" @click="touchFile(true)">
|
||||
{{ t("TXT_CODE_6215388a") }}
|
||||
</a-menu-item>
|
||||
@ -559,6 +630,15 @@ onMounted(() => {
|
||||
<a-col :span="24">
|
||||
<CardPanel style="height: 100%">
|
||||
<template #body>
|
||||
<a-progress
|
||||
v-if="percentComplete > 0"
|
||||
:stroke-color="{
|
||||
'0%': '#49b3ff',
|
||||
'100%': '#25f5b9'
|
||||
}"
|
||||
:percent="percentComplete"
|
||||
class="mb-20"
|
||||
/>
|
||||
<div class="file-breadcrumbs mb-20">
|
||||
<a-breadcrumb separator=">">
|
||||
<a-breadcrumb-item v-for="item in breadcrumbs" :key="item.path">
|
||||
@ -586,6 +666,7 @@ onMounted(() => {
|
||||
:scroll="{
|
||||
x: 'max-content'
|
||||
}"
|
||||
size="small"
|
||||
:pagination="{
|
||||
current: operationForm.current,
|
||||
pageSize: operationForm.pageSize,
|
||||
|
@ -52,21 +52,24 @@ const formData = reactive<NewInstanceForm>({
|
||||
endTime: ""
|
||||
});
|
||||
|
||||
const isImportMode = props.createMethod === QUICKSTART_METHOD.IMPORT;
|
||||
const isFileMode = props.createMethod === QUICKSTART_METHOD.FILE;
|
||||
const needUpload = isImportMode || isFileMode;
|
||||
|
||||
if (props.appType === QUICKSTART_ACTION_TYPE.Minecraft) {
|
||||
formData.startCommand =
|
||||
props.createMethod === QUICKSTART_METHOD.IMPORT ? "" : "java -jar ${ProgramName}";
|
||||
formData.startCommand = isFileMode ? "java -jar ${ProgramName}" : "";
|
||||
formData.stopCommand = "stop";
|
||||
formData.type = TYPE_MINECRAFT_JAVA;
|
||||
}
|
||||
|
||||
if (props.appType === QUICKSTART_ACTION_TYPE.Bedrock) {
|
||||
formData.startCommand = props.createMethod === QUICKSTART_METHOD.IMPORT ? "" : "${ProgramName}";
|
||||
formData.startCommand = isFileMode ? "${ProgramName}" : "";
|
||||
formData.stopCommand = "stop";
|
||||
formData.type = TYPE_MINECRAFT_BEDROCK;
|
||||
}
|
||||
|
||||
if (props.appType === QUICKSTART_ACTION_TYPE.SteamGameServer) {
|
||||
formData.startCommand = "${ProgramName}";
|
||||
formData.startCommand = isFileMode ? "${ProgramName}" : "";
|
||||
formData.type = TYPE_STEAM_SERVER_UNIVERSAL;
|
||||
}
|
||||
|
||||
@ -88,9 +91,16 @@ const rules: Record<string, Rule[]> = {
|
||||
const uFile = ref<File>();
|
||||
|
||||
const beforeUpload: UploadProps["beforeUpload"] = async (file) => {
|
||||
if (file.type !== "application/x-zip-compressed") return message.error(t("只能上传zip压缩文件"));
|
||||
uFile.value = file;
|
||||
|
||||
if (isImportMode) {
|
||||
if (file.type !== "application/x-zip-compressed")
|
||||
return message.error(t("只能上传zip压缩文件"));
|
||||
selectUnzipCodeDialog.value?.openDialog();
|
||||
} else {
|
||||
finalConfirm();
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -105,18 +115,15 @@ const finalConfirm = async () => {
|
||||
const thisModal = Modal.confirm({
|
||||
title: t("最终确认"),
|
||||
icon: createVNode(InfoCircleOutlined),
|
||||
content:
|
||||
props.createMethod === QUICKSTART_METHOD.IMPORT
|
||||
content: needUpload
|
||||
? t("上传文件时将同时创建实例,此操作不可逆,是否继续?")
|
||||
: t("实例将创建,是否继续?"),
|
||||
okText: t("确定"),
|
||||
async onOk() {
|
||||
thisModal.destroy();
|
||||
try {
|
||||
await formRef.value?.validateFields();
|
||||
thisModal.destroy();
|
||||
props.createMethod === QUICKSTART_METHOD.IMPORT
|
||||
? await selectedFile()
|
||||
: await createInstance();
|
||||
needUpload ? await selectedFile() : await createInstance();
|
||||
} catch {
|
||||
return message.error(t("请先完善基本参数再进行上传文件操作"));
|
||||
}
|
||||
@ -125,13 +132,16 @@ const finalConfirm = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 通过上传压缩包创建实例
|
||||
// 通过上传单个程序或者压缩包创建实例
|
||||
const { state: cfg, execute: getCfg } = uploadAddress();
|
||||
const { execute: uploadFile } = uploadInstanceFile();
|
||||
const percentComplete = ref(0);
|
||||
const selectedFile = async () => {
|
||||
try {
|
||||
if (!formData.cwd) formData.cwd = ".";
|
||||
if (isFileMode) {
|
||||
formData.startCommand = formData.startCommand.replace("${ProgramName}", uFile.value!.name);
|
||||
}
|
||||
await getCfg({
|
||||
params: {
|
||||
upload_dir: ".",
|
||||
@ -139,14 +149,14 @@ const selectedFile = async () => {
|
||||
},
|
||||
data: formData
|
||||
});
|
||||
if (!cfg.value) return;
|
||||
if (!cfg.value) throw new Error(t("获取上传地址失败"));
|
||||
|
||||
const uploadFormData = new FormData();
|
||||
uploadFormData.append("file", uFile.value as any);
|
||||
|
||||
await uploadFile({
|
||||
params: {
|
||||
unzip: UNZIP.ON,
|
||||
unzip: isImportMode ? UNZIP.ON : UNZIP.OFF,
|
||||
code: zipCode.value
|
||||
},
|
||||
data: uploadFormData,
|
||||
@ -219,6 +229,7 @@ const createInstance = async () => {
|
||||
v-model:value="formData.startCommand"
|
||||
:rows="3"
|
||||
:placeholder="t('如 java -jar server.jar,cmd.exe 等等')"
|
||||
style="min-height: 40px"
|
||||
/>
|
||||
<a-button
|
||||
type="default"
|
||||
@ -241,7 +252,29 @@ const createInstance = async () => {
|
||||
<a-input v-model:value="formData.cwd" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="createMethod === QUICKSTART_METHOD.IMPORT">
|
||||
<a-form-item v-if="createMethod === QUICKSTART_METHOD.FILE">
|
||||
<a-typography-title :level="5" class="require-field">
|
||||
{{ t("上传单个服务端软件") }}
|
||||
</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("上传文件后实例将自动创建") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-upload
|
||||
:before-upload="beforeUpload"
|
||||
:max-count="1"
|
||||
:change="selectedFile"
|
||||
:disabled="percentComplete > 0"
|
||||
>
|
||||
<a-button type="primary" :loading="percentComplete > 0">
|
||||
<upload-outlined v-if="percentComplete === 0" />
|
||||
{{ percentComplete > 0 ? t("正在上传:") + percentComplete + "%" : t("选择文件") }}
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-else-if="createMethod === QUICKSTART_METHOD.IMPORT">
|
||||
<a-typography-title :level="5" class="require-field">
|
||||
{{ t("上传服务端压缩包") }}
|
||||
</a-typography-title>
|
||||
@ -253,7 +286,6 @@ const createInstance = async () => {
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-upload
|
||||
action="/api/instance/upload"
|
||||
:before-upload="beforeUpload"
|
||||
:max-count="1"
|
||||
:change="selectedFile"
|
||||
|
Loading…
Reference in New Issue
Block a user