mirror of https://github.com/1Panel-dev/1Panel
feat: 编排删除增加删除文件勾选 (#808)
parent
5b68332b9a
commit
b42cf32326
|
@ -146,6 +146,7 @@ type ComposeOperation struct {
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Path string `json:"path" validate:"required"`
|
Path string `json:"path" validate:"required"`
|
||||||
Operation string `json:"operation" validate:"required,oneof=start stop down"`
|
Operation string `json:"operation" validate:"required,oneof=start stop down"`
|
||||||
|
WithFile bool `json:"withFile"`
|
||||||
}
|
}
|
||||||
type ComposeUpdate struct {
|
type ComposeUpdate struct {
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
|
|
|
@ -182,7 +182,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||||
global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name)
|
global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name)
|
||||||
if req.Operation == "down" {
|
if req.Operation == "down" {
|
||||||
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
|
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
|
||||||
_ = os.RemoveAll(strings.ReplaceAll(req.Path, "/docker-compose.yml", ""))
|
if req.WithFile {
|
||||||
|
_ = os.RemoveAll(path.Dir(req.Path))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -208,6 +208,7 @@ export namespace Container {
|
||||||
name: string;
|
name: string;
|
||||||
operation: string;
|
operation: string;
|
||||||
path: string;
|
path: string;
|
||||||
|
withFile: boolean;
|
||||||
}
|
}
|
||||||
export interface ComposeUpdate {
|
export interface ComposeUpdate {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -546,6 +546,11 @@ const message = {
|
||||||
compose: 'Compose',
|
compose: 'Compose',
|
||||||
fromChangeHelper: 'Switching the source will clear the current edited content. Do you want to continue?',
|
fromChangeHelper: 'Switching the source will clear the current edited content. Do you want to continue?',
|
||||||
composePathHelper: 'Config file save path: {0}',
|
composePathHelper: 'Config file save path: {0}',
|
||||||
|
composeHelper:
|
||||||
|
'The composition created through 1Panel editor or template will be saved in the {0}/docker/compose directory.',
|
||||||
|
deleteFile: 'Delete file',
|
||||||
|
deleteComposeHelper: 'Delete the corresponding composition file.',
|
||||||
|
deleteCompose: '" Delete this composition.',
|
||||||
apps: 'Apps',
|
apps: 'Apps',
|
||||||
local: 'Local',
|
local: 'Local',
|
||||||
createCompose: 'Create compose',
|
createCompose: 'Create compose',
|
||||||
|
|
|
@ -563,6 +563,10 @@ const message = {
|
||||||
compose: '编排',
|
compose: '编排',
|
||||||
fromChangeHelper: '切换来源将清空当前已编辑内容,是否继续?',
|
fromChangeHelper: '切换来源将清空当前已编辑内容,是否继续?',
|
||||||
composePathHelper: '配置文件保存路径: {0}',
|
composePathHelper: '配置文件保存路径: {0}',
|
||||||
|
composeHelper: '通过 1Panel 编辑或者模版创建的编排,将保存在 {0}/docker/compose 路径下',
|
||||||
|
deleteFile: '删除文件',
|
||||||
|
deleteComposeHelper: '删除对应的编排文件',
|
||||||
|
deleteCompose: '" 删除此编排',
|
||||||
apps: '应用商店',
|
apps: '应用商店',
|
||||||
local: '本地',
|
local: '本地',
|
||||||
createCompose: '创建编排',
|
createCompose: '创建编排',
|
||||||
|
|
|
@ -172,6 +172,7 @@ const acceptParams = (): void => {
|
||||||
form.from = 'edit';
|
form.from = 'edit';
|
||||||
form.path = '';
|
form.path = '';
|
||||||
form.file = '';
|
form.file = '';
|
||||||
|
form.template = null;
|
||||||
logInfo.value = '';
|
logInfo.value = '';
|
||||||
loadTemplates();
|
loadTemplates();
|
||||||
loadPath();
|
loadPath();
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisiable"
|
||||||
|
:title="$t('commons.button.delete') + ' - ' + composeName"
|
||||||
|
width="30%"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form ref="deleteForm" v-loading="loading">
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="deleteFile" :label="$t('container.deleteFile')" />
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('container.deleteComposeHelper') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<div>
|
||||||
|
<span style="font-size: 12px">{{ $t('database.delete') }}</span>
|
||||||
|
<span style="font-size: 12px; color: red; font-weight: 500">{{ composeName }}</span>
|
||||||
|
<span style="font-size: 12px">{{ $t('container.deleteCompose') }}</span>
|
||||||
|
</div>
|
||||||
|
<el-input v-model="deleteInfo" :placeholder="composeName"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisiable = false" :disabled="loading">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" @click="submit" :disabled="deleteInfo != composeName || loading">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { FormInstance } from 'element-plus';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { composeOperator } from '@/api/modules/container';
|
||||||
|
|
||||||
|
let dialogVisiable = ref(false);
|
||||||
|
let loading = ref(false);
|
||||||
|
let deleteInfo = ref('');
|
||||||
|
|
||||||
|
const deleteFile = ref();
|
||||||
|
const composeName = ref();
|
||||||
|
const composePath = ref();
|
||||||
|
|
||||||
|
const deleteForm = ref<FormInstance>();
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
|
||||||
|
const acceptParams = async (prop: DialogProps) => {
|
||||||
|
deleteFile.value = false;
|
||||||
|
composeName.value = prop.name;
|
||||||
|
composePath.value = prop.path;
|
||||||
|
deleteInfo.value = '';
|
||||||
|
dialogVisiable.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
let params = {
|
||||||
|
name: composeName.value,
|
||||||
|
path: composePath.value,
|
||||||
|
operation: 'down',
|
||||||
|
withFile: deleteFile.value,
|
||||||
|
};
|
||||||
|
await composeOperator(params)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
emit('search');
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||||
|
dialogVisiable.value = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -10,6 +10,20 @@
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<LayoutContent v-if="!isOnDetail" :title="$t('container.compose')" :class="{ mask: dockerStatus != 'Running' }">
|
<LayoutContent v-if="!isOnDetail" :title="$t('container.compose')" :class="{ mask: dockerStatus != 'Running' }">
|
||||||
|
<template #prompt>
|
||||||
|
<el-alert type="info" :closable="false">
|
||||||
|
<template #default>
|
||||||
|
<span>
|
||||||
|
<span>{{ $t('container.composeHelper', [baseDir]) }}</span>
|
||||||
|
<el-button type="primary" link @click="toFolder">
|
||||||
|
<el-icon>
|
||||||
|
<FolderOpened />
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
</template>
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="16">
|
<el-col :span="16">
|
||||||
|
@ -72,6 +86,7 @@
|
||||||
|
|
||||||
<EditDialog ref="dialogEditRef" />
|
<EditDialog ref="dialogEditRef" />
|
||||||
<CreateDialog @search="search" ref="dialogRef" />
|
<CreateDialog @search="search" ref="dialogRef" />
|
||||||
|
<DeleteDialog @search="search" ref="dialogDelRef" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -83,12 +98,13 @@ import { reactive, onMounted, ref } from 'vue';
|
||||||
import LayoutContent from '@/layout/layout-content.vue';
|
import LayoutContent from '@/layout/layout-content.vue';
|
||||||
import EditDialog from '@/views/container/compose/edit/index.vue';
|
import EditDialog from '@/views/container/compose/edit/index.vue';
|
||||||
import CreateDialog from '@/views/container/compose/create/index.vue';
|
import CreateDialog from '@/views/container/compose/create/index.vue';
|
||||||
|
import DeleteDialog from '@/views/container/compose/delete/index.vue';
|
||||||
import ComposeDetial from '@/views/container/compose/detail/index.vue';
|
import ComposeDetial from '@/views/container/compose/detail/index.vue';
|
||||||
import { composeOperator, loadDockerStatus, searchCompose } from '@/api/modules/container';
|
import { loadDockerStatus, searchCompose } from '@/api/modules/container';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
|
||||||
import { LoadFile } from '@/api/modules/files';
|
import { LoadFile } from '@/api/modules/files';
|
||||||
|
import { loadBaseDir } from '@/api/modules/setting';
|
||||||
import router from '@/routers';
|
import router from '@/routers';
|
||||||
|
|
||||||
const data = ref();
|
const data = ref();
|
||||||
|
@ -96,6 +112,7 @@ const selects = ref<any>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
const isOnDetail = ref(false);
|
const isOnDetail = ref(false);
|
||||||
|
const baseDir = ref();
|
||||||
|
|
||||||
const paginationConfig = reactive({
|
const paginationConfig = reactive({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
@ -124,6 +141,15 @@ const goSetting = async () => {
|
||||||
router.push({ name: 'ContainerSetting' });
|
router.push({ name: 'ContainerSetting' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toFolder = async () => {
|
||||||
|
router.push({ path: '/hosts/files', query: { path: baseDir.value + '/docker/compose' } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadPath = async () => {
|
||||||
|
const pathRes = await loadBaseDir();
|
||||||
|
baseDir.value = pathRes.data;
|
||||||
|
};
|
||||||
|
|
||||||
const search = async () => {
|
const search = async () => {
|
||||||
let params = {
|
let params = {
|
||||||
info: searchName.value,
|
info: searchName.value,
|
||||||
|
@ -163,14 +189,13 @@ const onOpenDialog = async () => {
|
||||||
dialogRef.value!.acceptParams();
|
dialogRef.value!.acceptParams();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dialogDelRef = ref();
|
||||||
const onDelete = async (row: Container.ComposeInfo) => {
|
const onDelete = async (row: Container.ComposeInfo) => {
|
||||||
const param = {
|
const param = {
|
||||||
name: row.name,
|
name: row.name,
|
||||||
path: row.path,
|
path: row.path,
|
||||||
operation: 'down',
|
|
||||||
};
|
};
|
||||||
await useDeleteData(composeOperator, param, 'commons.msg.delete');
|
dialogDelRef.value.acceptParams(param);
|
||||||
search();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const dialogEditRef = ref();
|
const dialogEditRef = ref();
|
||||||
|
@ -205,6 +230,7 @@ const buttons = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
loadPath();
|
||||||
loadStatus();
|
loadStatus();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue