mirror of
https://github.com/MCSManager/MCSManager.git
synced 2025-02-05 15:29:35 +08:00
Feat: add docker config dialog
This commit is contained in:
parent
d36361883f
commit
2d66ecd419
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@ -73,6 +73,7 @@ declare module 'vue' {
|
||||
InnerCard: typeof import('./src/components/InnerCard.vue')['default']
|
||||
InputDialogProvider: typeof import('./src/components/InputDialogProvider.vue')['default']
|
||||
InstanceConfigEditor: typeof import('./src/components/InstanceConfigEditor.vue')['default']
|
||||
KvOptionsDialog: typeof import('./src/components/fc/KvOptionsDialog.vue')['default']
|
||||
LayoutCard: typeof import('./src/components/LayoutCard.vue')['default']
|
||||
LeftMenuBtn: typeof import('./src/components/LeftMenuBtn.vue')['default']
|
||||
LeftMenusPanel: typeof import('./src/components/LeftMenusPanel.vue')['default']
|
||||
|
@ -182,14 +182,14 @@
|
||||
|
||||
.require-field {
|
||||
&::before {
|
||||
content: '*';
|
||||
color:red;
|
||||
content: "*";
|
||||
color: red;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-overflow-container {
|
||||
height: 80vh;
|
||||
max-height: 80vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
|
125
frontend/src/components/fc/KvOptionsDialog.vue
Normal file
125
frontend/src/components/fc/KvOptionsDialog.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
import type { MountComponent } from "@/types";
|
||||
import { t } from "@/lang/i18n";
|
||||
import type { AntColumnsType, AntTableCell } from "@/types/ant";
|
||||
import { MinusCircleOutlined, PlusCircleOutlined } from "@ant-design/icons-vue";
|
||||
import { h } from "vue";
|
||||
import _ from "lodash";
|
||||
|
||||
interface Props extends MountComponent {
|
||||
title: string;
|
||||
keyTitle: string;
|
||||
valueTitle: string;
|
||||
data: any[];
|
||||
columns?: AntColumnsType[];
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const dataSource = ref<any[]>(props.data instanceof Array ? _.cloneDeep(props.data) : []);
|
||||
|
||||
const open = ref(true);
|
||||
|
||||
const cancel = async () => {
|
||||
open.value = false;
|
||||
if (props.destroyComponent) props.destroyComponent();
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
if (props.emitResult) props.emitResult(dataSource.value);
|
||||
await cancel();
|
||||
};
|
||||
|
||||
const columns = computed<AntColumnsType[]>(() => {
|
||||
if (props.columns) {
|
||||
return [
|
||||
...props.columns,
|
||||
{
|
||||
align: "center",
|
||||
key: "operation",
|
||||
dataIndex: "operation",
|
||||
title: t("操作")
|
||||
}
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
align: "center",
|
||||
key: "k",
|
||||
dataIndex: "k",
|
||||
title: props.keyTitle
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
key: "v",
|
||||
dataIndex: "v",
|
||||
title: props.valueTitle
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
key: "operation",
|
||||
dataIndex: "operation",
|
||||
title: t("操作")
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
const operation = (type: "add" | "del", index = 0) => {
|
||||
if (type === "add") {
|
||||
const keys = columns.value.filter((v) => v.dataIndex).map((v) => String(v.dataIndex));
|
||||
const obj: any = {};
|
||||
for (const key of keys) {
|
||||
if (key === "operation") continue;
|
||||
obj[key] = "";
|
||||
}
|
||||
dataSource.value.push(obj);
|
||||
} else {
|
||||
dataSource.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="open"
|
||||
centered
|
||||
width="600px"
|
||||
:mask-closable="false"
|
||||
:title="props.title"
|
||||
:ok-text="t('确定')"
|
||||
:cancel-text="t('TXT_CODE_a0451c97')"
|
||||
@ok="submit"
|
||||
@cancel="cancel"
|
||||
>
|
||||
<div class="dialog-overflow-container">
|
||||
<div class="flex justify-end mb-20">
|
||||
<a-button :icon="h(PlusCircleOutlined)" @click="operation('add')">
|
||||
{{ t("新增配置") }}
|
||||
</a-button>
|
||||
</div>
|
||||
<a-table :data-source="dataSource" :columns="columns" :pagination="false">
|
||||
<template #bodyCell="{ column, record, index }: AntTableCell">
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<div class="flex-center flex-between">
|
||||
<div>
|
||||
<a-button
|
||||
:icon="h(MinusCircleOutlined)"
|
||||
danger
|
||||
@click="operation('del', index)"
|
||||
></a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-input
|
||||
v-model:value="record[String(column.dataIndex)]"
|
||||
:placeholder="t('请输入内容')"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -3,6 +3,15 @@ import type { UserInstance } from "@/types/user";
|
||||
|
||||
import SelectInstances from "@/components/fc/SelectInstances.vue";
|
||||
import CmdAssistantDialog from "@/components/fc/CmdAssistantDialog/index.vue";
|
||||
import KvOptionsDialogVue from "@/components/fc/KvOptionsDialog.vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import type { AntColumnsType } from "@/types/ant";
|
||||
|
||||
interface PortConfigItem {
|
||||
host: string;
|
||||
container: string;
|
||||
protocol: string;
|
||||
}
|
||||
|
||||
export async function useSelectInstances() {
|
||||
return await useMountComponent().mount<UserInstance[]>(SelectInstances);
|
||||
@ -11,3 +20,31 @@ export async function useSelectInstances() {
|
||||
export async function useCmdAssistantDialog() {
|
||||
return await useMountComponent().mount<string>(CmdAssistantDialog);
|
||||
}
|
||||
|
||||
export async function usePortEditDialog(data: PortConfigItem[] = []) {
|
||||
return (
|
||||
(await useMountComponent({
|
||||
data,
|
||||
title: t("容器端口映射配置"),
|
||||
keyTitle: t("主机端口"),
|
||||
valueTitle: t("容器端口"),
|
||||
columns: [
|
||||
{
|
||||
align: "center",
|
||||
dataIndex: "host",
|
||||
title: t("主机端口")
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
dataIndex: "container",
|
||||
title: t("容器端口")
|
||||
},
|
||||
{
|
||||
align: "center",
|
||||
dataIndex: "protocol",
|
||||
title: t("协议")
|
||||
}
|
||||
] as AntColumnsType[]
|
||||
}).mount<PortConfigItem[]>(KvOptionsDialogVue)) || []
|
||||
);
|
||||
}
|
||||
|
@ -17,6 +17,6 @@ export interface AntTableCell {
|
||||
}
|
||||
|
||||
export interface AntColumnsType extends ColumnType, Record<string, any> {
|
||||
key: string;
|
||||
key?: string;
|
||||
align: any;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import { Dayjs } from "dayjs";
|
||||
import _ from "lodash";
|
||||
import { GLOBAL_INSTANCE_NAME } from "../../../config/const";
|
||||
import { dayjsToTimestamp, timestampToDayjs } from "../../../tools/time";
|
||||
import { useCmdAssistantDialog } from "@/components/fc";
|
||||
import { useCmdAssistantDialog, usePortEditDialog } from "@/components/fc";
|
||||
|
||||
interface FormDetail extends InstanceDetail {
|
||||
dayjsEndTime?: Dayjs;
|
||||
@ -165,6 +165,28 @@ const openCmdAssistDialog = async () => {
|
||||
if (options.value && cmd) options.value.config.startCommand = cmd;
|
||||
};
|
||||
|
||||
const handleEditDockerConfig = async (type: "port" | "network") => {
|
||||
if (type === "port" && options.value?.config) {
|
||||
// "25565:25565/tcp 8080:8080/tcp" -> Array
|
||||
const portArray = (options.value?.config.docker.ports || []).map((iterator) => {
|
||||
const pad = iterator.split("/");
|
||||
const ports = pad[0];
|
||||
const protocol = pad[1];
|
||||
const port1 = ports.split(":")[0];
|
||||
const port2 = ports.split(":")[1];
|
||||
return {
|
||||
host: port1,
|
||||
container: port2,
|
||||
protocol
|
||||
};
|
||||
});
|
||||
const result = await usePortEditDialog(portArray);
|
||||
const portsArray = result.map((v) => `${v.host}:${v.container}/${v.protocol}`);
|
||||
options.value!.config.docker.ports = portsArray;
|
||||
console.log("结根:", portArray);
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
@ -335,7 +357,7 @@ defineExpose({
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row v-if="options.config.processType === 'docker'" :gutter="20">
|
||||
<a-col :xs="24" :lg="6" :offset="0">
|
||||
<a-col :xs="24" :lg="8" :offset="0">
|
||||
<a-form-item name="dockerImage">
|
||||
<a-typography-title :level="5" class="require-field">
|
||||
{{ t("TXT_CODE_6904cb3") }}
|
||||
@ -361,25 +383,27 @@ defineExpose({
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :lg="18" :offset="0">
|
||||
<a-col :xs="24" :lg="8" :offset="0">
|
||||
<a-form-item>
|
||||
<a-typography-title :level="5">{{ t("TXT_CODE_cf88c936") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("TXT_CODE_c7b95258") }}
|
||||
{{ t("通过映射让容器进程使用主机的真实端口") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-input-group v-if="options.config.docker.ports" compact>
|
||||
<a-button type="default">{{ t("编辑") }}</a-button>
|
||||
<a-input-group compact>
|
||||
<a-button type="default" @click="() => handleEditDockerConfig('port')">
|
||||
{{ t("编辑") }}
|
||||
</a-button>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :offset="0">
|
||||
<a-col :xs="24" :lg="8" :offset="0">
|
||||
<a-form-item>
|
||||
<a-typography-title :level="5">{{ t("TXT_CODE_3e68ca00") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("TXT_CODE_29c2884") }}
|
||||
{{ t("挂载额外路径到容器中") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-input-group compact>
|
||||
|
Loading…
Reference in New Issue
Block a user