Feat: add docker config dialog

This commit is contained in:
unitwk 2023-12-21 11:11:23 +08:00
parent d36361883f
commit 2d66ecd419
6 changed files with 199 additions and 12 deletions

View File

@ -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']

View File

@ -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;
}

View 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>

View File

@ -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)) || []
);
}

View File

@ -17,6 +17,6 @@ export interface AntTableCell {
}
export interface AntColumnsType extends ColumnType, Record<string, any> {
key: string;
key?: string;
align: any;
}

View File

@ -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>