1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

Additions

This commit is contained in:
2020-06-05 21:00:15 +02:00
parent 1bb3e8c9ac
commit cd42b65b9e
11 changed files with 218 additions and 57 deletions

View File

@ -7,16 +7,22 @@ const connections = {};
export default () => { export default () => {
ipcMain.handle('testConnection', async (event, conn) => { ipcMain.handle('testConnection', async (event, conn) => {
try { const connection = knex({
await knex({
client: conn.client, client: conn.client,
connection: { connection: {
host: conn.host, host: conn.host,
port: +conn.port, port: +conn.port,
user: conn.user, user: conn.user,
password: conn.password password: conn.password
},
pool: {
min: 1,
max: 3
} }
}).raw('SELECT 1+1 AS result'); });
try {
await InformationSchema.testConnection(connection);
return { status: 'success' }; return { status: 'success' };
} }

View File

@ -1,11 +1,16 @@
'use strict'; 'use strict';
export default class { export default class {
static testConnection (connection) {
return connection('TABLES')
.select({ result: 1 })
.withSchema('information_schema');
}
static getStructure (connection) { static getStructure (connection) {
return connection() return connection('TABLES')
.select('*') .select('*')
.withSchema('information_schema') .withSchema('information_schema')
.from('TABLES')
.orderBy(['TABLE_SCHEMA', 'TABLE_NAME'], 'asc'); .orderBy(['TABLE_SCHEMA', 'TABLE_NAME'], 'asc');
} }
} }

View File

@ -82,6 +82,7 @@ export default {
#window-content{ #window-content{
display: flex; display: flex;
position: relative; position: relative;
overflow: hidden;
} }
#main-content { #main-content {

View File

@ -68,8 +68,22 @@ export default {
}; };
</script> </script>
<style scoped> <style lang="scss">
.editor-wrapper{ .editor-wrapper{
border-bottom: 1px solid #444444; border-bottom: 1px solid #444444;
} }
.CodeMirror{
height: 200px;
.CodeMirror-scroll{
max-width: 100%;
}
.CodeMirror-line {
word-break: break-word!important;
white-space: pre-wrap!important;
word-break: normal;
}
}
</style> </style>

View File

@ -13,33 +13,32 @@
v-for="connection in connections" v-for="connection in connections"
:key="connection.uid" :key="connection.uid"
draggable="true" draggable="true"
class="settingbar-element btn btn-link tooltip tooltip-right" class="settingbar-element btn btn-link ex-tooltip"
:class="{'selected': connection.uid === selectedWorkspace}" :class="{'selected': connection.uid === selectedWorkspace}"
:data-tooltip="`${getConnectionName(connection.uid)}`"
@click="selectWorkspace(connection.uid)" @click="selectWorkspace(connection.uid)"
@contextmenu.prevent="contextMenu($event, connection)" @contextmenu.prevent="contextMenu($event, connection)"
@mouseover.self="tooltipPosition"
> >
<i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${connected.includes(connection.uid) ? 'badge' : ''}`" /> <i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${connected.includes(connection.uid) ? 'badge' : ''}`" />
<span class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
</li> </li>
</draggable> </draggable>
<li <li
class="settingbar-element btn btn-link tooltip tooltip-right pt-3" class="settingbar-element btn btn-link ex-tooltip"
:data-tooltip="$t('message.addConnection')"
@click="showNewConnModal" @click="showNewConnModal"
@mouseover.self="tooltipPosition"
> >
<i class="settingbar-element-icon material-icons text-light">add</i> <i class="settingbar-element-icon material-icons text-light">add</i>
<span class="ex-tooltip-content">{{ $t('message.addConnection') }}</span>
</li> </li>
</ul> </ul>
</div> </div>
<div class="settingbar-bottom-elements"> <div class="settingbar-bottom-elements">
<ul class="settingbar-elements"> <ul class="settingbar-elements">
<li <li class="settingbar-element btn btn-link ex-tooltip" @click="showSettingModal('general')">
class="settingbar-element btn btn-link tooltip tooltip-right mb-2"
:data-tooltip="$t('word.settings')"
@click="showSettingModal('general')"
>
<i class="settingbar-element-icon material-icons text-light">settings</i> <i class="settingbar-element-icon material-icons text-light">settings</i>
<span class="ex-tooltip-content">{{ $t('word.settings') }}</span>
</li> </li>
</ul> </ul>
</div> </div>
@ -62,7 +61,8 @@ export default {
dragElement: null, dragElement: null,
isContext: false, isContext: false,
contextEvent: null, contextEvent: null,
contextConnection: {} contextConnection: {},
scale: 0
}; };
}, },
computed: { computed: {
@ -95,6 +95,11 @@ export default {
}, },
workspaceName (connection) { workspaceName (connection) {
return connection.ask ? '' : `${connection.user + '@'}${connection.host}:${connection.port}`; return connection.ask ? '' : `${connection.user + '@'}${connection.host}:${connection.port}`;
},
tooltipPosition (e) {
const el = e.target;
const fromTop = window.pageYOffset + el.getBoundingClientRect().top - (el.offsetHeight / 4);
el.querySelector('.ex-tooltip-content').style.top = `${fromTop}px`;
} }
} }
}; };
@ -113,6 +118,22 @@ export default {
box-shadow: 0 0 1px 0px #000; box-shadow: 0 0 1px 0px #000;
z-index: 9; z-index: 9;
.settingbar-top-elements{
overflow-x: hidden;
overflow-y: overlay;
max-height: calc((100vh - 3.5rem) - #{$excluding-size});
&::-webkit-scrollbar {
width: 3px;
}
}
.settingbar-bottom-elements{
padding-top: .5rem;
background: $bg-color-light;
z-index: 1;
}
.settingbar-elements{ .settingbar-elements{
list-style: none; list-style: none;
text-align: center; text-align: center;
@ -121,14 +142,16 @@ export default {
margin: 0; margin: 0;
.settingbar-element{ .settingbar-element{
height: initial; height: $settingbar-width;
width: 100%; width: 100%;
padding: 0;
padding: .3rem 0 0;
margin: 0; margin: 0;
border-left: 3px solid transparent; border-left: 3px solid transparent;
opacity: .5; opacity: .5;
transition: opacity .2s; transition: opacity .2s;
display: flex;
align-content: center;
justify-content: center;
flex-direction: column;
&:hover{ &:hover{
opacity: 1; opacity: 1;
@ -140,7 +163,6 @@ export default {
} }
.settingbar-element-icon{ .settingbar-element-icon{
margin-left: -3px;
&.badge::after{ &.badge::after{
bottom: -10px; bottom: -10px;
@ -153,4 +175,32 @@ export default {
} }
} }
.ex-tooltip{// Because both overflow-x: visible and overflow-y:auto are evil!!!
.ex-tooltip-content{
z-index: 999;
visibility: hidden;
opacity: 0;
display:block;
position:absolute;
background-color:#feffe1;
text-align: center;
margin:.0 0 0 calc(#{$settingbar-width} - 5px);
left: 0;
padding: .2rem .4rem;
font-size: .7rem;
background: rgba(48,55,66,.95);
border-radius: .1rem;
color: #fff;
max-width: 320px;
pointer-events: none;
text-overflow: ellipsis;
transition: opacity .2s;
}
&:hover .ex-tooltip-content{
visibility: visible;
opacity: 1;
}
}
</style> </style>

View File

@ -1,8 +1,18 @@
<template> <template>
<div v-show="isSelected" class="workspace column columns"> <div v-show="isSelected" class="workspace column columns">
<WorkspaceExploreBar :connection="connection" :is-selected="isSelected" /> <WorkspaceExploreBar :connection="connection" :is-selected="isSelected" />
<div class="workspace-tabs column"> <div class="workspace-tabs column p-0">
<pre>{{ JSON.stringify(workspace.structure, null, 3) }}</pre> <ul class="tab tab-block">
<li
v-for="(tab, key) of workspace.tabs"
:key="tab.uid"
class="tab-item"
:class="{'active': selectedTab === tab.uid}"
>
<a href="#">Query #{{ key }} <span class="btn btn-clear" /></a>
</li>
</ul>
<QueryEditor v-model="query" />
</div> </div>
</div> </div>
</template> </template>
@ -11,15 +21,22 @@
import { mapGetters, mapActions } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
import Connection from '@/ipc-api/Connection'; import Connection from '@/ipc-api/Connection';
import WorkspaceExploreBar from '@/components/WorkspaceExploreBar'; import WorkspaceExploreBar from '@/components/WorkspaceExploreBar';
import QueryEditor from '@/components/QueryEditor';
export default { export default {
name: 'Workspace', name: 'Workspace',
components: { components: {
WorkspaceExploreBar WorkspaceExploreBar,
QueryEditor
}, },
props: { props: {
connection: Object connection: Object
}, },
data () {
return {
query: ''
};
},
computed: { computed: {
...mapGetters({ ...mapGetters({
selectedWorkspace: 'workspaces/getSelected', selectedWorkspace: 'workspaces/getSelected',
@ -30,10 +47,13 @@ export default {
}, },
isSelected () { isSelected () {
return this.selectedWorkspace === this.connection.uid; return this.selectedWorkspace === this.connection.uid;
},
selectedTab () {
return this.workspace.tabs.filter(tab => tab.selected).uid || this.workspace.tabs[0].uid;
} }
}, },
async created () { async created () {
this.addWorkspace(this.connection.uid); await this.addWorkspace(this.connection.uid);
const isInitiated = await Connection.checkConnection(this.connection.uid); const isInitiated = await Connection.checkConnection(this.connection.uid);
if (isInitiated) if (isInitiated)
this.connectWorkspace(this.connection); this.connectWorkspace(this.connection);
@ -57,6 +77,15 @@ export default {
.workspace-tabs{ .workspace-tabs{
overflow: auto; overflow: auto;
height: calc(100vh - #{$excluding-size}); height: calc(100vh - #{$excluding-size});
.tab-block{
background: $bg-color-light;
margin-top: 0;
.tab-item{
max-width: 6rem;
}
}
} }
} }
</style> </style>

View File

@ -79,6 +79,5 @@ export default {
background: transparent; background: transparent;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center;
} }
</style> </style>

View File

@ -22,12 +22,17 @@
>exit_to_app</i> >exit_to_app</i>
</span> </span>
</div> </div>
<WorkspaceConnectPanel v-if="!workspace.connected" :connection="connection" /> <WorkspaceConnectPanel
<div class="workspace-explorebar-body"> v-if="!workspace.connected"
class="workspace-explorebar-body"
:connection="connection"
/>
<div v-else class="workspace-explorebar-body">
<WorkspaceExploreBarDatabase <WorkspaceExploreBarDatabase
v-for="db of workspace.structure" v-for="db of workspace.structure"
:key="db.dbName" :key="db.name"
:database="db" :database="db"
:connection="connection"
/> />
</div> </div>
</div> </div>
@ -178,7 +183,7 @@ export default {
.workspace-explorebar-body{ .workspace-explorebar-body{
width: 100%; width: 100%;
height: calc((100vh - 30px) - #{$excluding-size}); height: calc((100vh - 30px) - #{$excluding-size});
overflow: auto; overflow: overlay;
padding: 0 .1rem; padding: 0 .1rem;
} }
} }

View File

@ -1,9 +1,13 @@
<template> <template>
<details class="accordion workspace-explorebar-database"> <details class="accordion workspace-explorebar-database">
<summary class="accordion-header database-name pb-0"> <summary
class="accordion-header database-name pb-0"
:class="{'text-bold': breadcrumbs.database === database.name}"
@click="changeBreadcrumbs({database: database.name, table:null})"
>
<i class="icon material-icons md-18 mr-1">navigate_next</i> <i class="icon material-icons md-18 mr-1">navigate_next</i>
<i class="material-icons md-18 mr-1">view_agenda</i> <i class="material-icons md-18 mr-1">view_agenda</i>
<span>{{ database.dbName }}</span> <span>{{ database.name }}</span>
</summary> </summary>
<div class="accordion-body"> <div class="accordion-body">
<div class="database-bables"> <div class="database-bables">
@ -12,6 +16,8 @@
v-for="table of database.tables" v-for="table of database.tables"
:key="table.TABLE_NAME" :key="table.TABLE_NAME"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.database === database.name && breadcrumbs.table === table.TABLE_NAME}"
@click="changeBreadcrumbs({database: database.name, table: table.TABLE_NAME})"
> >
<a class="table-name"> <a class="table-name">
<i class="material-icons md-18 mr-1">grid_on</i> <i class="material-icons md-18 mr-1">grid_on</i>
@ -25,10 +31,26 @@
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex';
export default { export default {
name: 'WorkspaceExploreBarDatabase', name: 'WorkspaceExploreBarDatabase',
props: { props: {
database: Object database: Object,
connection: Object
},
computed: {
...mapGetters({
getWorkspace: 'workspaces/getWorkspace'
}),
breadcrumbs () {
return this.getWorkspace(this.connection.uid).breadcrumbs;
}
},
methods: {
...mapActions({
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
})
} }
}; };
</script> </script>
@ -48,6 +70,12 @@ export default {
display: block; display: block;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
&:hover{
color: $body-font-color;
background: rgba($color: #FFF, $alpha: .05);
border-radius: 2px;
}
} }
.database-bables{ .database-bables{

View File

@ -142,19 +142,11 @@ body{
} }
.menu{ .menu{
font-size: .6rem; font-size: .7rem;
.menu-item { .menu-item {
+ .menu-item{ + .menu-item{
margin-top: 0; margin-top: 0;
} }
> a{
&:hover{
color: $body-font-color;
background: rgba($color: #FFF, $alpha: .1);
}
}
} }
} }

View File

@ -1,12 +1,14 @@
'use strict'; 'use strict';
import Connection from '@/ipc-api/Connection'; import Connection from '@/ipc-api/Connection';
import { uidGen } from 'common/libs/utilities';
function remapStructure (structure) { function remapStructure (structure) {
const databases = structure.map(table => table.TABLE_SCHEMA) const databases = structure.map(table => table.TABLE_SCHEMA)
.filter((value, index, self) => self.indexOf(value) === index); .filter((value, index, self) => self.indexOf(value) === index);
return databases.map(db => { return databases.map(db => {
return { return {
dbName: db, name: db,
tables: structure.filter(table => table.TABLE_SCHEMA === db) tables: structure.filter(table => table.TABLE_SCHEMA === db)
}; };
}); });
@ -50,6 +52,25 @@ export default {
}, },
ADD_WORKSPACE (state, workspace) { ADD_WORKSPACE (state, workspace) {
state.workspaces.push(workspace); state.workspaces.push(workspace);
},
CHANGE_BREADCRUMBS (state, { uid, breadcrumbs }) {
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid ? { ...workspace, breadcrumbs } : workspace);
},
NEW_TAB (state, uid) {
const newTab = {
uid: uidGen(),
selected: false
};
state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === uid) {
return {
...workspace,
tabs: [...workspace.tabs, newTab]
};
}
else
return workspace;
});
} }
}, },
actions: { actions: {
@ -80,18 +101,29 @@ export default {
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true }); dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
} }
}, },
async removeConnected ({ commit }, uid) { removeConnected ({ commit }, uid) {
Connection.disconnect(uid); Connection.disconnect(uid);
commit('REMOVE_CONNECTED', uid); commit('REMOVE_CONNECTED', uid);
}, },
addWorkspace ({ commit }, uid) { addWorkspace ({ commit, dispatch, getters }, uid) {
const workspace = { const workspace = {
uid, uid,
connected: false, connected: false,
tabs: [], tabs: [],
structure: {} structure: {},
breadcrumbs: {}
}; };
commit('ADD_WORKSPACE', workspace); commit('ADD_WORKSPACE', workspace);
if (!getters.getWorkspace(uid).tabs.length)
dispatch('newTab', uid);
},
changeBreadcrumbs ({ commit, getters }, payload) {
commit('CHANGE_BREADCRUMBS', { uid: getters.getSelected, breadcrumbs: payload });
},
newTab ({ commit }, uid) {
commit('NEW_TAB', uid);
} }
} }
}; };