feat: Multi tab editing file (#8738)

* feat: Multi tab editing file

* feat: link underline
This commit is contained in:
2025-05-19 17:41:51 +08:00 committed by GitHub
parent 9732b09cf0
commit bf7935e313
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 483 additions and 89 deletions

View file

@ -1462,6 +1462,7 @@ const message = {
existFileSize: 'File size (new -> old)',
existFileDirHelper: 'The selected file/folder has a duplicate name. Please proceed with caution!',
noSuchFile: 'The file or directory was not found. Please check and try again.',
setting: 'Setting',
},
ssh: {
autoStart: 'Auto Start',

View file

@ -1403,9 +1403,9 @@ const message = {
existFileSize: 'ファイルサイズ新しい -> 古い',
existFileDirHelper: '選択したファイル/フォルダーには同じ名前のものが既に存在します慎重に操作してください',
noSuchFile: 'ファイルまたはディレクトリが見つかりませんでした確認して再試行してください',
setting: '設定',
},
ssh: {
setting: '設定',
autoStart: 'オートスタート',
enable: 'AutoStartを有効にします',
disable: 'AutoStartを無効にします',

View file

@ -1389,9 +1389,9 @@ const message = {
existFileSize: '파일 크기 (새로운 -> 오래된)',
existFileDirHelper: '선택한 파일/폴더에 동일한 이름이 이미 존재합니다. 신중하게 작업하세요!',
noSuchFile: '파일 또는 디렉터리를 찾을 없습니다. 확인 다시 시도하세요.',
setting: '설정',
},
ssh: {
setting: '설정',
autoStart: '자동 시작',
enable: '자동 시작 활성화',
disable: '자동 시작 비활성화',

View file

@ -1446,9 +1446,9 @@ const message = {
existFileSize: 'Saiz fail (baru -> lama)',
existFileDirHelper: 'Fail/folder yang dipilih mempunyai nama yang sama. Sila berhati-hati!',
noSuchFile: 'Fail atau direktori tidak ditemui. Sila periksa dan cuba lagi.',
setting: 'tetapan',
},
ssh: {
setting: 'tetapan',
autoStart: 'Mula automatik',
enable: 'Aktifkan Mula Automatik',
disable: 'Nyahaktif Mula Automatik',

View file

@ -1432,9 +1432,9 @@ const message = {
existFileSize: 'Tamanho do arquivo (novo -> antigo)',
existFileDirHelper: 'O arquivo/pasta selecionado tem um nome duplicado. Por favor, prossiga com cautela!',
noSuchFile: 'O arquivo ou diretório não foi encontrado. Por favor, verifique e tente novamente.',
setting: 'configuração',
},
ssh: {
setting: 'configuração',
autoStart: 'Início automático',
enable: 'Habilitar início automático',
disable: 'Desabilitar início automático',

View file

@ -1433,9 +1433,9 @@ const message = {
existFileSize: 'Размер файла (новый -> старый)',
existFileDirHelper: 'Выбранный файл/папка имеет дублирующееся имя. Пожалуйста, действуйте осторожно!',
noSuchFile: 'Файл или каталог не найдены. Пожалуйста, проверьте и повторите попытку.',
setting: 'настройка',
},
ssh: {
setting: 'настройка',
autoStart: 'Автозапуск',
enable: 'Включить автозапуск',
disable: 'Отключить автозапуск',

View file

@ -1388,6 +1388,7 @@ const message = {
existFileSize: '文件大小->',
existFileDirHelper: '選擇的檔案/資料夾存在同名請謹慎操作',
noSuchFile: '找不到該檔案或目錄請檢查後重試',
setting: '设置',
},
ssh: {
autoStart: '開機自啟',

View file

@ -1384,6 +1384,7 @@ const message = {
existFileSize: '文件大小 ( -> )',
existFileDirHelper: '选择的文件/文件夹存在同名请谨慎操作',
noSuchFile: '未能找到该文件或目录请检查后重试',
setting: '设置',
},
ssh: {
autoStart: '开机自启',

View file

@ -345,3 +345,9 @@ html {
overflow: hidden;
text-overflow: ellipsis;
}
.code-dialog {
.el-dialog__header {
--el-dialog-padding-primary: 0px !important;
}
}

View file

@ -1,8 +1,15 @@
<template>
<DialogPro v-model="open" size="w-70" @opened="onOpen" :show-close="false" :top="'5vh'" :fullscreen="isFullscreen">
<DialogPro
v-model="open"
size="w-70"
class="code-dialog !p-0"
@opened="onOpen"
:show-close="false"
:fullscreen="isFullscreen"
>
<template #header>
<div ref="dialogHeader" class="flex items-center justify-between">
<span class="truncate-text">{{ $t('commons.button.edit') + ' - ' + form.path }}</span>
<div ref="dialogHeader" class="flex items-center justify-between code-header px-4 rounded-t">
<span class="truncate-text">{{ $t('home.dir') + ' - ' + form.path }}</span>
<el-space alignment="center" :size="1" class="dialog-header-icon">
<el-tooltip :content="loadTooltip()" placement="top">
<el-button
@ -21,54 +28,82 @@
</div>
</template>
<template #content>
<div ref="dialogForm">
<el-form :inline="true" :model="config" class="mt-1.5">
<el-form-item :label="$t('file.theme')">
<el-select v-model="config.theme" @change="changeTheme()" class="p-w-200">
<el-option
v-for="item in themes"
:key="item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('file.language')">
<el-select v-model="config.language" @change="changeLanguage()" class="!w-32">
<el-option
v-for="lang in Languages"
:key="lang.label"
:value="lang.label"
:label="lang.label"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('file.eol')">
<el-select v-model="config.eol" @change="changeEOL()" class="p-w-150">
<el-option v-for="eol in eols" :key="eol.label" :value="eol.value" :label="eol.label" />
</el-select>
</el-form-item>
<el-form-item :label="$t('file.wordWrap')">
<el-select v-model="config.wordWrap" @change="changeWarp()" class="p-w-100">
<el-option :label="$t('commons.button.enable')" value="on"></el-option>
<el-option :label="$t('commons.button.disable')" value="off"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('file.minimap')">
<el-select v-model="config.minimap" @change="changeMinimap()" class="p-w-100">
<el-option :label="$t('commons.button.enable')" :value="true"></el-option>
<el-option :label="$t('commons.button.disable')" :value="false"></el-option>
</el-select>
</el-form-item>
</el-form>
<div ref="dialogForm" class="px-4 py-2">
<div class="flex justify-start items-center gap-x-4 card-action">
<el-text @click="handleReset">{{ $t('commons.button.reset') }}</el-text>
<el-text @click="saveContent()" class="ml-0">{{ $t('commons.button.save') }}</el-text>
<el-dropdown trigger="click" max-height="300" placement="bottom-start" @command="changeTheme">
<span class="el-dropdown-link">{{ $t('file.theme') }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in themes" :key="item.label" :command="item.value">
<div class="flex items-center justify-between gap-4 w-full">
{{ item.label }}
<el-icon v-if="config.theme == item.value"><Check /></el-icon>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown trigger="click" max-height="300" placement="bottom-start" @command="changeLanguage">
<span class="el-dropdown-link">{{ $t('file.language') }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="item in Languages"
:key="item.label"
@click="changeLanguage()"
:command="item.label"
>
<div class="flex items-center justify-between gap-4 w-full">
{{ item.label }}
<el-icon v-if="config.language == item.label"><Check /></el-icon>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown trigger="click" max-height="300" placement="bottom-start" @command="changeEOL">
<span class="el-dropdown-link">{{ $t('file.eol') }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in eols" :key="item.label" :command="item.value">
<div class="flex items-center justify-between gap-4 w-full">
{{ item.label }}
<el-icon v-if="config.eol == item.value"><Check /></el-icon>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown trigger="click" max-height="300" placement="bottom-start">
<span class="el-dropdown-link">{{ $t('file.setting') }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="changeMinimap(!config.minimap)">
<div class="flex items-center justify-between gap-4 w-full">
{{ $t('file.minimap') }}
<el-icon v-if="config.minimap"><Check /></el-icon>
</div>
</el-dropdown-item>
<el-dropdown-item @click="changeWarp(config.wordWrap)">
<div class="flex items-center justify-between gap-4 w-full">
{{ $t('file.wordWrap') }}
<el-icon v-if="config.wordWrap == 'on'"><Check /></el-icon>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<div v-loading="loading">
<div v-loading="loading" class="">
<div class="flex">
<div
class="monaco-editor sm:w-48 w-1/3 monaco-editor-background border-0 tree-container"
v-if="isShow"
>
<div class="flex items-center justify-between pl-1 sm:pr-4 pr-1 pt-1">
<div class="flex items-center justify-between pl-1 sm:pr-4 pr-1 py-0.5 h-6">
<el-tooltip :content="$t('file.top')" placement="top">
<el-text size="small" @click="getUpData()" class="cursor-pointer">
<el-icon>
@ -87,7 +122,7 @@
</el-text>
</el-tooltip>
</div>
<el-divider class="!my-1" />
<el-divider class="!my-0" />
<el-tree-v2
ref="treeRef"
:data="treeData"
@ -119,40 +154,157 @@
<el-divider
v-if="isShow"
direction="vertical"
:style="{ height: codeHeight }"
style="height: 100%; width: 0"
class="!m-0 p-0"
:class="isShow ? 'opacity-100' : 'opacity-0'"
></el-divider>
<el-icon
v-if="isShow"
class="cursor-pointer absolute bg-gray-100 py-2 rounded-l-sm block top-1/3 -left-[9px]"
size="9"
@click="toggleShow"
>
<DArrowLeft />
</el-icon>
<el-icon
v-else
class="cursor-pointer absolute bg-gray-100 py-2 rounded-r-sm block top-1/3 right-[7px]"
size="9"
@click="toggleShow"
>
<DArrowRight />
</el-icon>
</div>
<div
ref="codeBox"
id="codeBox"
:style="{ height: codeHeight }"
class="flex-1 sm:w-4/5 w-2/3 relative"
></div>
<div class="flex-1 sm:w-4/5 w-2/3 relative">
<CodeTabs
class="monaco-editor monaco-editor-background"
ref="codeTabsRef"
:select-tab="selectTab"
:file-tabs="fileTabs"
:on-remove-tab="removeTab"
:on-change-tab="changeTab"
:on-remove-all-tab="removeAllTab"
:on-remove-other-tab="removeOtherTab"
></CodeTabs>
<div ref="codeBox" class="relative" :style="{ height: codeHeight }">
<el-icon
v-if="isShow"
class="cursor-pointer absolute bg-gray-100 py-2 rounded-l-sm block top-1/3 -left-[9px]"
size="9"
@click="toggleShow"
>
<DArrowLeft />
</el-icon>
<el-icon
v-else
class="cursor-pointer absolute bg-gray-100 py-2 rounded-r-sm block top-1/3 z-50"
size="9"
@click="toggleShow"
>
<DArrowRight />
</el-icon>
</div>
</div>
</div>
<div class="code-footer pl-4 h-6 flex justify-end items-center gap-4 rounded-b">
<el-divider direction="vertical" class="!h-6" v-if="config.theme" />
<el-dropdown trigger="click" max-height="300" placement="top" @command="changeTheme">
<span class="el-dropdown-link">
{{ themes.find((item) => item.value === config.theme)?.label || $t('file.theme') }}
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in themes" :key="item.label" :command="item.value">
<div class="flex items-center justify-between gap-4 w-full">
{{ item.label }}
<el-icon v-if="config.theme == item.value"><Check /></el-icon>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-divider direction="vertical" class="!h-6" />
<el-dropdown trigger="click" max-height="300" placement="top" @command="changeEOL">
<span class="el-dropdown-link">
{{ eols.find((item) => item.value === config.eol)?.label || $t('file.eol') }}
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in eols" :key="item.label" :command="item.value">
<div class="flex items-center justify-between gap-4 w-full">
{{ item.label }}
<el-icon v-if="config.eol == item.value"><Check /></el-icon>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-divider direction="vertical" class="!h-6" />
<el-dropdown trigger="click" max-height="300" placement="top" @command="changeLanguage">
<span class="el-dropdown-link">
{{
config.language
? `${$t('file.language')}: ${
Languages.find((item) => item.label === config.language)?.label ||
config.language
}`
: $t('file.language')
}}
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="item in Languages"
:key="item.label"
@click="changeLanguage()"
:command="item.label"
>
<div class="flex items-center justify-between gap-4 w-full">
{{ item.label }}
<el-icon v-if="config.language == item.label"><Check /></el-icon>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-divider direction="vertical" class="!h-6" />
<el-dropdown trigger="click" max-height="300" placement="top">
<span class="el-dropdown-link">
{{
$t('file.wordWrap') +
': ' +
$t(config.wordWrap === 'on' ? 'commons.button.enable' : 'commons.button.disable')
}}
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="changeWarp('off')">
<div class="flex items-center justify-between gap-4 w-full">
{{ $t('commons.button.enable') }}
<el-icon v-if="config.wordWrap == 'on'"><Check /></el-icon>
</div>
</el-dropdown-item>
<el-dropdown-item @click="changeWarp('on')">
<div class="flex items-center justify-between gap-4 w-full">
{{ $t('commons.button.disable') }}
<el-icon v-if="config.wordWrap == 'off'"><Check /></el-icon>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-divider direction="vertical" class="!h-6" />
<el-dropdown trigger="click" max-height="300" placement="top">
<span class="el-dropdown-link">
{{
$t('file.minimap') +
': ' +
$t(config.minimap ? 'commons.button.enable' : 'commons.button.disable')
}}
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="changeMinimap(true)">
<div class="flex items-center justify-between gap-4 w-full">
{{ $t('commons.button.enable') }}
<el-icon v-if="config.minimap"><Check /></el-icon>
</div>
</el-dropdown-item>
<el-dropdown-item @click="changeMinimap(false)">
<div class="flex items-center justify-between gap-4 w-full">
{{ $t('commons.button.disable') }}
<el-icon v-if="!config.minimap"><Check /></el-icon>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-divider direction="vertical" class="!h-6 !mr-3.5" />
</div>
</div>
</template>
<template #footer>
<div class="dialog-footer" ref="dialogFooter">
<el-button @click="handleReset">{{ $t('commons.button.reset') }}</el-button>
<el-button type="primary" @click="saveContent()">{{ $t('commons.button.confirm') }}</el-button>
</div>
</template>
</DialogPro>
@ -179,7 +331,10 @@ import { TreeKey, TreeNodeData } from 'element-plus/es/components/tree-v2/src/ty
import { Top, Refresh, DArrowLeft, DArrowRight } from '@element-plus/icons-vue';
import { loadBaseDir } from '@/api/modules/setting';
import { GlobalStore } from '@/store';
import CodeTabs from './tabs/index.vue';
import type { TabPaneName } from 'element-plus';
const codeTabsRef = ref();
let editor: monaco.editor.IStandaloneCodeEditor | undefined;
self.MonacoEnvironment = {
@ -238,9 +393,9 @@ const fileExtension = ref('');
const baseDir = ref();
const treeData = ref([]);
const codeBox = ref();
const defaultHeight = ref(55);
const defaultHeight = ref(56);
const treeHeight = ref(0);
const codeHeight = ref('55vh');
const codeHeight = ref('56vh');
const codeReq = reactive({ path: '', expand: false, page: 1, pageSize: 100 });
const isShow = ref(true);
const isEdit = ref(false);
@ -270,6 +425,170 @@ const config = reactive<EditorConfig>({
minimap: false,
});
const selectTab = ref();
const fileTabs = ref([]);
const removeTab = (targetPath: TabPaneName) => {
if (isEdit.value) {
ElMessageBox.confirm(i18n.global.t('file.saveContentAndClose'), {
confirmButtonText: i18n.global.t('commons.button.save'),
cancelButtonText: i18n.global.t('commons.button.notSave'),
type: 'info',
distinguishCancelAndClose: true,
})
.then(() => {
const tabs = fileTabs.value;
let activeName = selectTab.value;
if (activeName === targetPath) {
tabs.forEach((tab, index) => {
if (tab.path === targetPath) {
const nextTab = tabs[index + 1] || tabs[index - 1];
if (nextTab) {
activeName = nextTab.path;
}
}
});
}
selectTab.value = activeName;
fileTabs.value = tabs.filter((tab) => tab.path !== targetPath);
saveContent();
})
.finally(() => {});
} else {
const tabs = fileTabs.value;
let activeName = selectTab.value;
if (activeName === targetPath) {
tabs.forEach((tab, index) => {
if (tab.path === targetPath) {
const nextTab = tabs[index + 1] || tabs[index - 1];
if (nextTab) {
activeName = nextTab.path;
}
}
});
}
selectTab.value = activeName;
fileTabs.value = tabs.filter((tab) => tab.path !== targetPath);
}
getContent(selectTab.value, '');
};
const removeAllTab = (targetPath: string, type: string) => {
if (isEdit.value) {
ElMessageBox.confirm(i18n.global.t('file.saveContentAndClose'), {
confirmButtonText: i18n.global.t('commons.button.save'),
cancelButtonText: i18n.global.t('commons.button.notSave'),
type: 'info',
distinguishCancelAndClose: true,
})
.then(() => {
const tabs = fileTabs.value;
let activeName = selectTab.value;
if (activeName !== targetPath) {
tabs.forEach((tab, index) => {
if (tab.path === targetPath) {
const nextTab = tabs[index];
if (nextTab) {
activeName = nextTab.path;
}
}
});
}
selectTab.value = activeName;
if (type === 'left') {
fileTabs.value = fileTabs.value.filter((tab, index, arr) => {
const targetIndex = arr.findIndex((t) => t.path === targetPath);
return index >= targetIndex;
});
} else if (type === 'right') {
fileTabs.value = fileTabs.value.filter((tab, index, arr) => {
const targetIndex = arr.findIndex((t) => t.path === targetPath);
return index <= targetIndex;
});
}
saveContent();
})
.finally(() => {});
} else {
const tabs = fileTabs.value;
let activeName = selectTab.value;
if (activeName !== targetPath) {
tabs.forEach((tab, index) => {
if (tab.path === targetPath) {
const nextTab = tabs[index];
if (nextTab) {
activeName = nextTab.path;
}
}
});
}
selectTab.value = activeName;
if (type === 'left') {
fileTabs.value = fileTabs.value.filter((tab, index, arr) => {
const targetIndex = arr.findIndex((t) => t.path === targetPath);
return index >= targetIndex;
});
} else if (type === 'right') {
fileTabs.value = fileTabs.value.filter((tab, index, arr) => {
const targetIndex = arr.findIndex((t) => t.path === targetPath);
return index <= targetIndex;
});
}
}
getContent(selectTab.value, '');
};
const removeOtherTab = (targetPath: string) => {
if (isEdit.value) {
ElMessageBox.confirm(i18n.global.t('file.saveContentAndClose'), {
confirmButtonText: i18n.global.t('commons.button.save'),
cancelButtonText: i18n.global.t('commons.button.notSave'),
type: 'info',
distinguishCancelAndClose: true,
})
.then(() => {
const tabs = fileTabs.value;
let activeName = selectTab.value;
if (activeName !== targetPath) {
tabs.forEach((tab, index) => {
if (tab.path === targetPath) {
const nextTab = tabs[index];
if (nextTab) {
activeName = nextTab.path;
}
}
});
}
selectTab.value = activeName;
fileTabs.value = tabs.filter((tab) => tab.path === targetPath);
saveContent();
})
.finally(() => {});
} else {
const tabs = fileTabs.value;
let activeName = selectTab.value;
if (activeName !== targetPath) {
tabs.forEach((tab, index) => {
if (tab.path === targetPath) {
const nextTab = tabs[index];
if (nextTab) {
activeName = nextTab.path;
}
}
});
}
selectTab.value = activeName;
fileTabs.value = tabs.filter((tab) => tab.path === targetPath);
}
getContent(selectTab.value, '');
};
const changeTab = (targetPath: TabPaneName) => {
getContent(targetPath.toString(), '');
};
const eols = [
{
label: 'LF (Linux)',
@ -375,11 +694,13 @@ const toggleFullscreen = () => {
updateHeights();
};
const changeLanguage = () => {
const changeLanguage = (command: string) => {
config.language = command;
monaco.editor.setModelLanguage(editor.getModel(), config.language);
};
const changeTheme = () => {
const changeTheme = (command: string) => {
config.theme = command;
monaco.editor.setTheme(config.theme);
const themes = {
vs: 'monaco-editor-tree-light',
@ -399,18 +720,21 @@ const changeTheme = () => {
localStorage.setItem(codeThemeKey, config.theme);
};
const changeEOL = () => {
const changeEOL = (command: number) => {
config.eol = command;
editor.getModel().pushEOL(config.eol);
};
const changeWarp = () => {
const changeWarp = (command: string) => {
config.wordWrap = command === 'on' ? 'off' : 'on';
localStorage.setItem(warpKey, config.wordWrap);
editor.updateOptions({
wordWrap: config.wordWrap,
});
};
const changeMinimap = () => {
const changeMinimap = (command: boolean) => {
config.minimap = command;
localStorage.setItem(minimapKey, JSON.stringify(config.minimap));
editor.updateOptions({
minimap: {
@ -486,6 +810,13 @@ const acceptParams = (props: EditProps) => {
directoryPath.value = getDirectoryPath(props.path);
fileExtension.value = props.extension;
fileName.value = props.name;
fileTabs.value = [];
selectTab.value = '';
fileTabs.value.push({
name: fileName.value,
path: props.path,
});
selectTab.value = props.path;
config.language = props.language;
config.eol = monaco.editor.EndOfLineSequence.LF;
config.theme = localStorage.getItem(codeThemeKey) || 'vs-dark';
@ -521,7 +852,7 @@ const getDirectoryPath = (filePath: string) => {
const onOpen = () => {
initEditor();
changeTheme();
changeTheme(config.theme);
search(directoryPath.value).then((res) => {
handleSearchResult(res);
});
@ -577,6 +908,22 @@ const getContent = (path: string, extension: string) => {
fileExtension.value = res.data.extension;
fileName.value = res.data.name;
initEditor();
if (extension == '') {
Languages.forEach((language) => {
const ext = fileExtension.value.substring(1);
if (language.value.indexOf(ext) > -1) {
config.language = language.label;
}
});
}
const exists = fileTabs.value.some((tab) => tab.path === path);
if (!exists) {
fileTabs.value.push({
name: res.data.name,
path: path,
});
}
selectTab.value = res.data.path;
})
.catch(() => {});
};
@ -726,4 +1073,42 @@ defineExpose({ acceptParams });
overflow: hidden;
text-overflow: ellipsis;
}
.code-header {
background-color: var(--panel-color-primary-light-9);
}
.code-footer {
background-color: var(--panel-color-primary-light-9);
}
.card-action {
.el-button + .el-button {
margin-left: 0 !important;
}
.el-button.is-link {
padding: 0;
}
}
.code-dialog {
.el-dialog__footer {
padding-top: 0 !important;
}
}
:deep(.el-tabs) {
--el-tabs-header-height: 28px;
--el-text-color-primary: var(--el-text-color-regular);
.el-tabs__header {
height: 28px;
margin: 0;
}
.el-tabs__nav-wrap {
height: 27px;
line-height: 27px;
}
.el-tabs__nav,
.el-tabs__nav-next,
.el-tabs__nav-prev {
height: 28px;
line-height: 28px;
}
}
</style>

View file

@ -160,7 +160,7 @@
</el-alert>
</template>
<template #leftToolBar>
<div ref="leftWrapper">
<div ref="leftWrapper" class="flex items-center gap-2 flex-wrap">
<el-dropdown @command="handleCreate" class="mr-2.5">
<el-button type="primary">
{{ $t('commons.button.create') }}