fix: 编排创建样式调整、日志改为异步加载 (#794)

pull/795/head
ssongliu 2 years ago committed by GitHub
parent 8a32d8032f
commit 0c09b12680
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"os/exec" "os/exec"
"path" "path"
@ -154,9 +155,10 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
go func() { go func() {
defer file.Close() defer file.Close()
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d") cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
stdout, err := cmd.CombinedOutput() multiWriter := io.MultiWriter(os.Stdout, file)
_, _ = file.Write(stdout) cmd.Stdout = multiWriter
if err != nil { cmd.Stderr = multiWriter
if err := cmd.Run(); err != nil {
global.LOG.Errorf("docker-compose up %s failed, err: %v", req.Name, err) global.LOG.Errorf("docker-compose up %s failed, err: %v", req.Name, err)
_, _ = compose.Down(req.Path) _, _ = compose.Down(req.Path)
_, _ = file.WriteString("docker-compose up failed!") _, _ = file.WriteString("docker-compose up failed!")

@ -539,6 +539,7 @@ const message = {
registrieHelper: 'One in a row, for example:\n172.16.10.111:8081 \n172.16.10.112:8081', registrieHelper: 'One in a row, for example:\n172.16.10.111:8081 \n172.16.10.112:8081',
compose: 'Compose', compose: 'Compose',
fromChangeHelper: 'Switching the source will clear the current edited content. Do you want to continue?',
composeHelper: composeHelper:
'The current content has passed the format verification. Please click Submit to complete the creation', 'The current content has passed the format verification. Please click Submit to complete the creation',
composePathHelper: 'Config file save path: {0}', composePathHelper: 'Config file save path: {0}',

@ -556,6 +556,7 @@ const message = {
registrieHelper: '\n172.16.10.111:8081 \n172.16.10.112:8081', registrieHelper: '\n172.16.10.111:8081 \n172.16.10.112:8081',
compose: '', compose: '',
fromChangeHelper: '',
composeHelper: '', composeHelper: '',
composePathHelper: ': {0}', composePathHelper: ': {0}',
apps: '', apps: '',

@ -14,7 +14,7 @@
<el-col :span="22"> <el-col :span="22">
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" label-width="80px"> <el-form ref="formRef" label-position="top" :model="form" :rules="rules" label-width="80px">
<el-form-item :label="$t('container.from')"> <el-form-item :label="$t('container.from')">
<el-radio-group v-model="form.from" @change="hasChecked = false"> <el-radio-group v-model="form.from" @change="changeFrom">
<el-radio label="edit">{{ $t('commons.button.edit') }}</el-radio> <el-radio label="edit">{{ $t('commons.button.edit') }}</el-radio>
<el-radio label="path">{{ $t('container.pathSelect') }}</el-radio> <el-radio label="path">{{ $t('container.pathSelect') }}</el-radio>
<el-radio label="template">{{ $t('container.composeTemplate') }}</el-radio> <el-radio label="template">{{ $t('container.composeTemplate') }}</el-radio>
@ -30,14 +30,20 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item v-if="form.from === 'edit' || form.from === 'template'" prop="name"> <el-form-item v-if="form.from === 'edit' || form.from === 'template'" prop="name">
<el-input @input="changePath" v-model.trim="form.name"> <el-input @input="changePath" v-model.trim="form.name">
<template #prepend>{{ $t('file.dir') }}</template> <template #prepend>{{ $t('file.dir') }}</template>
</el-input> </el-input>
<span class="input-help">{{ $t('container.composePathHelper', [composeFile]) }}</span> <span class="input-help">
{{ $t('container.composePathHelper', [composeFile]) }}
</span>
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.from === 'template'" prop="template"> <el-form-item v-if="form.from === 'template'" prop="template">
<el-select v-model="form.template" @change="hasChecked = false"> <el-select v-model="form.template" @change="changeTemplate">
<el-option <el-option
v-for="item in templateOptions" v-for="item in templateOptions"
:key="item.id" :key="item.id"
@ -46,13 +52,21 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="form.from === 'edit'"> </el-col>
</el-row>
<el-form-item>
<div v-if="form.from === 'edit' || form.from === 'template'" style="width: 100%">
<el-radio-group v-model="mode" size="small">
<el-radio-button label="edit">{{ $t('commons.button.edit') }}</el-radio-button>
<el-radio-button label="log">{{ $t('commons.button.log') }}</el-radio-button>
</el-radio-group>
<codemirror <codemirror
v-if="mode === 'edit'"
:autofocus="true" :autofocus="true"
placeholder="#Define or paste the content of your docker-compose file here" placeholder="#Define or paste the content of your docker-compose file here"
:indent-with-tab="true" :indent-with-tab="true"
:tabSize="4" :tabSize="4"
style="width: 100%; height: 250px" style="width: 100%; height: calc(100vh - 375px)"
:lineWrapping="true" :lineWrapping="true"
:matchBrackets="true" :matchBrackets="true"
theme="cobalt" theme="cobalt"
@ -60,32 +74,16 @@
:styleActiveLine="true" :styleActiveLine="true"
:extensions="extensions" :extensions="extensions"
v-model="form.file" v-model="form.file"
:disabled="onCreating"
/> />
</el-form-item> </div>
</el-form>
<codemirror
v-if="logVisiable && form.from !== 'edit'"
:autofocus="true"
placeholder="Waiting for docker-compose up output..."
:indent-with-tab="true"
:tabSize="4"
style="height: calc(100vh - 370px)"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
@ready="handleReady"
v-model="logInfo"
:readOnly="true"
/>
<codemirror <codemirror
v-if="logVisiable && form.from === 'edit'" v-if="mode === 'log'"
:autofocus="true" :autofocus="true"
placeholder="Waiting for docker-compose up output..." placeholder="Waiting for docker-compose up output..."
:indent-with-tab="true" :indent-with-tab="true"
:tabSize="4" :tabSize="4"
style="height: calc(100vh - 590px)" style="width: 100%; height: calc(100vh - 375px)"
:lineWrapping="true" :lineWrapping="true"
:matchBrackets="true" :matchBrackets="true"
theme="cobalt" theme="cobalt"
@ -93,8 +91,10 @@
:extensions="extensions" :extensions="extensions"
@ready="handleReady" @ready="handleReady"
v-model="logInfo" v-model="logInfo"
:readOnly="true" :disabled="true"
/> />
</el-form-item>
</el-form>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
@ -122,7 +122,7 @@ import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElForm } from 'element-plus'; import { ElForm, ElMessageBox } from 'element-plus';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { listComposeTemplate, testCompose, upCompose } from '@/api/modules/container'; import { listComposeTemplate, testCompose, upCompose } from '@/api/modules/container';
import { loadBaseDir } from '@/api/modules/setting'; import { loadBaseDir } from '@/api/modules/setting';
@ -132,12 +132,15 @@ import { MsgSuccess } from '@/utils/message';
const loading = ref(); const loading = ref();
const mode = ref('edit');
const onCreating = ref();
const oldFrom = ref('edit');
const extensions = [javascript(), oneDark]; const extensions = [javascript(), oneDark];
const view = shallowRef(); const view = shallowRef();
const handleReady = (payload) => { const handleReady = (payload) => {
view.value = payload.view; view.value = payload.view;
}; };
const logVisiable = ref();
const logInfo = ref(); const logInfo = ref();
const drawerVisiable = ref(false); const drawerVisiable = ref(false);
@ -161,14 +164,12 @@ const form = reactive({
const rules = reactive({ const rules = reactive({
name: [Rules.requiredInput, Rules.imageName], name: [Rules.requiredInput, Rules.imageName],
path: [Rules.requiredSelect], path: [Rules.requiredSelect],
template: [Rules.requiredSelect],
}); });
const loadTemplates = async () => { const loadTemplates = async () => {
const res = await listComposeTemplate(); const res = await listComposeTemplate();
templateOptions.value = res.data; templateOptions.value = res.data;
if (templateOptions.value && templateOptions.value.length !== 0) {
form.template = templateOptions.value[0].id;
}
}; };
const acceptParams = (): void => { const acceptParams = (): void => {
@ -177,7 +178,6 @@ const acceptParams = (): void => {
form.from = 'edit'; form.from = 'edit';
form.path = ''; form.path = '';
form.file = ''; form.file = '';
logVisiable.value = false;
hasChecked.value = false; hasChecked.value = false;
logInfo.value = ''; logInfo.value = '';
loadTemplates(); loadTemplates();
@ -185,6 +185,44 @@ const acceptParams = (): void => {
}; };
const emit = defineEmits<{ (e: 'search'): void }>(); const emit = defineEmits<{ (e: 'search'): void }>();
const changeTemplate = () => {
hasChecked.value = false;
for (const item of templateOptions.value) {
if (form.template === item.id) {
form.file = item.content;
break;
}
}
};
const changeFrom = () => {
if ((oldFrom.value === 'edit' || oldFrom.value === 'template') && form.file) {
ElMessageBox.confirm(i18n.global.t('container.fromChangeHelper'), i18n.global.t('container.from'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
})
.then(() => {
hasChecked.value = false;
if (form.from === 'template') {
if (!form.template && templateOptions.value && templateOptions.value.length !== 0) {
form.template = templateOptions.value[0].id;
}
changeTemplate();
}
if (form.from === 'edit') {
form.file = '';
}
oldFrom.value = form.from;
})
.catch(() => {
form.from = oldFrom.value;
});
} else {
oldFrom.value = form.from;
}
};
const handleClose = () => { const handleClose = () => {
emit('search'); emit('search');
clearInterval(Number(timer)); clearInterval(Number(timer));
@ -210,6 +248,7 @@ const onTest = async (formEl: FormInstance | undefined) => {
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
loading.value = true; loading.value = true;
logInfo.value = '';
await testCompose(form) await testCompose(form)
.then((res) => { .then((res) => {
loading.value = false; loading.value = false;
@ -228,17 +267,17 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return; if (!formEl) return;
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
onCreating.value = true;
mode.value = 'log';
const res = await upCompose(form); const res = await upCompose(form);
logInfo.value = ''; logInfo.value = '';
buttonDisabled.value = true; buttonDisabled.value = true;
logVisiable.value = true;
loadLogs(res.data); loadLogs(res.data);
}); });
}; };
const loadLogs = async (path: string) => { const loadLogs = async (path: string) => {
timer = setInterval(async () => { timer = setInterval(async () => {
if (logVisiable.value) {
const res = await LoadFile({ path: path }); const res = await LoadFile({ path: path });
logInfo.value = formatImageStdout(res.data); logInfo.value = formatImageStdout(res.data);
nextTick(() => { nextTick(() => {
@ -252,11 +291,11 @@ const loadLogs = async (path: string) => {
logInfo.value.endsWith('docker-compose up failed!') || logInfo.value.endsWith('docker-compose up failed!') ||
logInfo.value.endsWith('docker-compose up successful!') logInfo.value.endsWith('docker-compose up successful!')
) { ) {
onCreating.value = false;
clearInterval(Number(timer)); clearInterval(Number(timer));
timer = null; timer = null;
buttonDisabled.value = false; buttonDisabled.value = false;
} }
}
}, 1000 * 3); }, 1000 * 3);
}; };

Loading…
Cancel
Save