mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
546 lines
18 KiB
546 lines
18 KiB
<template> |
|
<v-container fluid class="project-container ma-0 pa-0" style="position: relative"> |
|
<v-tabs |
|
dark |
|
background-color="primary" |
|
height="40" |
|
class="project-tabs" |
|
color="" |
|
next-icon="mdi-arrow-right-bold-box-outline" |
|
prev-icon="mdi-arrow-left-bold-box-outline" |
|
show-arrows |
|
v-model="activeTab" |
|
ref="projectTabs" |
|
:class="{'dark-them' : $store.state.windows.darkTheme}" |
|
> |
|
<v-tabs-slider color=""></v-tabs-slider> |
|
|
|
<v-tab class="divider project-tab xc-border-right" |
|
:title="tab.name" v-for="(tab,index) in tabs" |
|
:key="`${pid}||${(tab._nodes && tab._nodes).type || ''}||${(tab._nodes && tab._nodes.dbAlias) || ''}||${tab.name}`" |
|
:href="`#${(tab._nodes && tab._nodes).type || ''}||${(tab._nodes && tab._nodes.dbAlias) || ''}||${tab.name}`" |
|
@change="tabActivated(tab)" |
|
> |
|
<v-icon icon :small="true" v-if="treeViewIcons[tab._nodes.type]">{{ treeViewIcons[tab._nodes.type].openIcon }} |
|
</v-icon> |
|
|
|
|
|
<!-- <v-progress-circular--> |
|
<!-- v-if="operationTab === index"--> |
|
<!-- :value="100"--> |
|
<!-- :size="20" class="mr-2"--> |
|
<!-- indeterminate--> |
|
<!-- ></v-progress-circular>--> |
|
<span class="flex-grow-1 caption font-weight-bold text-capitalize mx-2" style=" |
|
white-space: nowrap; |
|
overflow: hidden;max-width:140px;text-overflow:ellipsis">{{ tab.name }}</span> |
|
<v-icon icon @click="removeTab(index)" :small="true">mdi-close</v-icon> |
|
</v-tab> |
|
|
|
|
|
<!-- <v-tabs-items v-model="activeTab">--> |
|
<v-tabs-items :value="activeTab"> |
|
<v-tab-item v-for="(tab, index) in tabs" |
|
:key="`${pid}||${(tab._nodes && tab._nodes).type || ''}||${(tab._nodes && tab._nodes.dbAlias) || ''}||${tab.name}`" |
|
:value="`${(tab._nodes && tab._nodes.type) || ''}||${(tab._nodes && tab._nodes.dbAlias) || ''}||${tab.name}`" |
|
eager :transition="false" |
|
style="height:100%" |
|
:reverse-transition="false"> |
|
<div |
|
style="height:100%" v-if="tab._nodes.type === 'table'"> |
|
<!-- <sqlLogAndOutput :hide="hideLogWindows">--> |
|
<TableView :hideLogWindows.sync="hideLogWindows" :nodes="tab._nodes" :ref="'tabs'+index"/> |
|
<!-- </sqlLogAndOutput>--> |
|
</div> |
|
<div style="height:100%" v-else-if="tab._nodes.type === 'view'"> |
|
<!-- <sqlLogAndOutput>--> |
|
<ViewTab :nodes="tab._nodes" :ref="'tabs'+index"/> |
|
<!-- </sqlLogAndOutput>--> |
|
</div> |
|
<div style="height:100%" v-else-if="tab._nodes.type === 'function'"> |
|
<sqlLogAndOutput> |
|
<FunctionTab :nodes="tab._nodes" :ref="'tabs'+index"/> |
|
</sqlLogAndOutput> |
|
</div> |
|
<div style="height:100%" v-else-if="tab._nodes.type === 'procedure'"> |
|
<sqlLogAndOutput> |
|
<ProcedureTab :nodes="tab._nodes" :ref="'tabs'+index"/> |
|
</sqlLogAndOutput> |
|
</div> |
|
<div style="height:100%" v-else-if="tab._nodes.type === 'sequence'"> |
|
<sqlLogAndOutput> |
|
<SequenceTab :nodes="tab._nodes" :ref="'tabs'+index"/> |
|
</sqlLogAndOutput> |
|
</div> |
|
<div style="height:100%" v-else-if="tab._nodes.type === 'db'"> |
|
<audit-tab :nodes="tab._nodes" :ref="'tabs'+index"></audit-tab> |
|
<!-- <sqlLogAndOutput>--> |
|
<!-- <DbTab :nodes="tab._nodes" :ref="'tabs'+index"/>--> |
|
<!-- </sqlLogAndOutput>--> |
|
</div> |
|
<!-- <div v-else-if="tab._nodes.type === 'sqlEditor'">--> |
|
<!-- <SqlEditorTab :nodes="tab._nodes" ref=tabs/>--> |
|
<!-- </div>--> |
|
<div style="height:100%" v-else-if="tab._nodes.type === 'seedParserDir'"> |
|
|
|
<sqlLogAndOutput> |
|
<SeedTab :nodes="tab._nodes" :ref="'tabs'+index"/> |
|
</sqlLogAndOutput> |
|
</div> |
|
<div style="height:100%" v-else-if="tab._nodes.type === 'migrationsDir'"> |
|
<audit-tab :nodes="tab._nodes" :ref="'tabs'+index"></audit-tab> |
|
|
|
<!-- <sqlLogAndOutput>--> |
|
<!-- <DbTab :nodes="tab._nodes" :ref="'tabs'+index"/>--> |
|
<!-- </sqlLogAndOutput>--> |
|
</div> |
|
<div style="height:100%" v-else-if="tab._nodes.type === 'apisDir'"> |
|
<ApisTab :nodes="tab._nodes" :ref="'tabs'+index"/> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'apiClientDir'"> |
|
<ApiClientTab :nodes="tab._nodes" :ref="'tabs'+index"/> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'apiClientSwaggerDir'"> |
|
<ApiClientSwaggerTab :nodes="tab._nodes" :ref="'tabs'+index"/> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'sqlClientDir'"> |
|
<sqlLogAndOutput> |
|
<SqlClientTab :nodes="tab._nodes" :ref="'tabs'+index"/> |
|
</sqlLogAndOutput> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'terminal'"> |
|
<x-term :ref="'tabs'+index" style="height: 100%"></x-term> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'graphqlClientDir'"> |
|
<graphql-client style="height: 100%"></graphql-client> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'swaggerClientDir'"> |
|
<swagger-client style="height: 100%"></swagger-client> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'grpcClient'"> |
|
<grpc-client style="height: 100%"></grpc-client> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'meta'"> |
|
<xc-meta style="height: 100%"></xc-meta> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'roles'"> |
|
<auth-tab v-if="_isUIAllowed('team-auth')" :nodes="tab._nodes" style="height: 100%"></auth-tab> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'acl'"> |
|
<global-acl :nodes="tab._nodes" style="height: 100%"></global-acl> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'projectSettings'"> |
|
<project-settings v-if="_isUIAllowed('settings')" :nodes="tab._nodes" |
|
style="height: 100%"></project-settings> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'disableOrEnableModel'"> |
|
<disable-or-enable-models v-if="_isUIAllowed('project-metadata')" :nodes="tab._nodes" |
|
style="height: 100%"></disable-or-enable-models> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'cronJobs'"> |
|
<cron-jobs :nodes="tab._nodes" style="height: 100%"></cron-jobs> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'projectInfo'"> |
|
<xc-info :nodes="tab._nodes" class="h-100"></xc-info> |
|
</div> |
|
<div |
|
style="height:100%" v-else-if="tab._nodes.type === 'appStore'"> |
|
<app-store :nodes="tab._nodes" class="h-100"></app-store> |
|
</div> |
|
<div style="height:100%" v-else> |
|
<h1>{{ tab.name }}</h1> |
|
<h1>{{ tab._nodes }}</h1> |
|
</div> |
|
</v-tab-item> |
|
<!-- </v-tabs-items>--> |
|
|
|
</v-tabs-items> |
|
|
|
<x-icon |
|
v-if="_isUIAllowed('addTable')" |
|
tooltip="Create new table" |
|
iconClass="add-btn" :color="[ 'white','grey lighten-2']" @click="dialogCreateTableShow = true">mdi-plus-box |
|
</x-icon> |
|
<v-spacer></v-spacer> |
|
<div class="powered-by align-self-center grey--text text--lighten-3 d-flex align-center" |
|
style="margin-right: 34px;font-size: .65rem ;"> |
|
<span>Powered by <a href="https://nocodb.com" target="_blank" class=" white--text" |
|
style="text-decoration: none">NocoDB</a></span> |
|
<v-icon x-small class="ml-1 powered-by-close" color="grey lighten-1" @click="upgradeToEE">mdi-close-circle |
|
</v-icon> |
|
</div> |
|
|
|
</v-tabs> |
|
|
|
<dlg-table-create |
|
v-if="dialogCreateTableShow" |
|
v-model="dialogCreateTableShow" |
|
@create="$emit('tableCreate',$event); dialogCreateTableShow =false;" |
|
></dlg-table-create> |
|
|
|
<screensaver class="screensaver" v-if="showScreensaver"></screensaver> |
|
</v-container> |
|
</template> |
|
|
|
<script> |
|
import TableView from "./project/table"; |
|
import ViewTab from "./project/view"; |
|
import FunctionTab from "./project/function"; |
|
import ProcedureTab from "./project/procedure"; |
|
import SequenceTab from "./project/sequence"; |
|
import SeedTab from "./project/seed"; |
|
// import DbTab from "./project/auditTab/db"; |
|
// import SqlEditorTab from "./project/sqlClient"; |
|
import SqlClientTab from "./project/sqlClient"; |
|
import ApisTab from "./project/apis"; |
|
import ApiClientTab from "./project/apiClientOld"; |
|
import treeViewIcons from '../helpers/treeViewIcons' |
|
import sqlLogAndOutput from './project/sqlLogAndOutput'; |
|
import graphqlClient from './project/graphqlClient'; |
|
import xTerm from './xTerm' |
|
|
|
import {mapGetters, mapMutations} from "vuex"; |
|
import ApiClientSwaggerTab from "./project/apiClientSwagger"; |
|
import XcMeta from "./project/settings/xcMeta"; |
|
import Roles from "@/components/auth/roles"; |
|
import GlobalAcl from "@/components/globalAcl"; |
|
import GrpcClient from "@/components/project/grpcClient"; |
|
import ProjectSettings from "@/components/project/projectSettings"; |
|
import CreateOrEditProject from "@/components/createOrEditProject"; |
|
import DisableOrEnableModels from "@/components/project/projectMetadata/disableOrEnableModels"; |
|
import CronJobs from "@/components/project/cronJobs"; |
|
import AuthTab from "@/components/authTab"; |
|
import XcInfo from "./project/xcInfo"; |
|
import AppStore from "@/components/project/appStore"; |
|
import AuditTab from "~/components/project/auditTab"; |
|
import DlgTableCreate from "@/components/utils/dlgTableCreate"; |
|
import Screensaver from "@/components/screensaver"; |
|
import SwaggerClient from "@/components/project/swaggerClient"; |
|
|
|
export default { |
|
components: { |
|
SwaggerClient, |
|
Screensaver, |
|
DlgTableCreate, |
|
AuditTab, |
|
AppStore, |
|
XcInfo, |
|
AuthTab, |
|
CronJobs, |
|
DisableOrEnableModels, |
|
CreateOrEditProject, |
|
ProjectSettings, |
|
GrpcClient, |
|
GlobalAcl, |
|
Roles, |
|
XcMeta, |
|
ApiClientSwaggerTab, |
|
TableView, |
|
ViewTab, |
|
FunctionTab, |
|
ProcedureTab, |
|
// DbTab, |
|
// SqlEditorTab, |
|
ApisTab, |
|
SqlClientTab, |
|
ApiClientTab, |
|
SeedTab, |
|
SequenceTab, |
|
sqlLogAndOutput, |
|
xTerm, |
|
graphqlClient |
|
}, |
|
data() { |
|
return { |
|
dialogCreateTableShow: false, |
|
test: "", |
|
treeViewIcons, |
|
hideLogWindows: false, |
|
showScreensaver: false |
|
}; |
|
}, |
|
methods: { |
|
checkInactiveState() { |
|
let position = 0; |
|
let idleTime = 0; |
|
//Increment the idle time counter every minute. |
|
let idleInterval = setInterval(timerIncrement, 1000); |
|
|
|
const self = this; |
|
//Zero the idle timer on mouse movement. |
|
document.addEventListener('mousemove', (e) => { |
|
self.showScreensaver = false; |
|
idleTime = 0; |
|
clearInterval(idleInterval); |
|
idleInterval = setInterval(timerIncrement, 1000); |
|
}); |
|
document.addEventListener('keypress', (e) => { |
|
self.showScreensaver = false; |
|
idleTime = 0; |
|
clearInterval(idleInterval); |
|
idleInterval = setInterval(timerIncrement, 1000); |
|
}); |
|
|
|
|
|
function timerIncrement() { |
|
idleTime = idleTime + 1; |
|
if (idleTime > 120) { |
|
const title = document.title; |
|
|
|
function scrolltitle() { |
|
document.title = title + Array(position).fill(' .').join(''); |
|
position = ++position % 3; |
|
if (self.showScreensaver) { |
|
window.setTimeout(scrolltitle, 400); |
|
} else { |
|
document.title = title; |
|
} |
|
} |
|
|
|
self.showScreensaver = self.$store.state.windows.screensaver; |
|
scrolltitle(); |
|
clearInterval(idleInterval) |
|
} |
|
} |
|
}, |
|
async handleKeyDown(event) { |
|
console.log('======== project tabs key handler') |
|
let activeTabEleKey = `tabs${this.activeTab}`; |
|
let isHandled = false; |
|
|
|
if (this.$refs[activeTabEleKey] |
|
&& this.$refs[activeTabEleKey][0] |
|
&& this.$refs[activeTabEleKey][0].handleKeyDown) { |
|
isHandled = await this.$refs[activeTabEleKey][0].handleKeyDown(event); |
|
} |
|
if (!isHandled) |
|
switch ([this._isMac ? event.metaKey : event.ctrlKey, event.key].join('_')) { |
|
case 'true_w' : |
|
this.removeTab(this.activeTab); |
|
event.preventDefault(); |
|
event.stopPropagation(); |
|
break; |
|
} |
|
}, |
|
...mapMutations({ |
|
setActiveTab: "tabs/active", |
|
removeTab: "tabs/remove", |
|
updateActiveTabx: "tabs/activeTabCtx" |
|
}), |
|
tabActivated(tab) { |
|
|
|
|
|
// if (tab._nodes.type === 'apiClientDir' || tab._nodes.type === 'sqlClientDir' || tab._nodes.type === 'sqlEditor') { |
|
// this.$store.commit('windows/MutToggleLogWindowFromTab', {client: true, status: true}); |
|
// } else { |
|
// this.$store.commit('windows/MutToggleLogWindowFromTab', {client: false, status: false}); |
|
// } |
|
} |
|
}, |
|
computed: { |
|
...mapGetters({tabs: "tabs/list", activeTabCtx: "tabs/activeTabCtx"}), |
|
pid() { |
|
return this.$route.params.project_id |
|
}, |
|
activeTab: { |
|
// get() { |
|
// console.log('activateTab========== get', this.$store.state.tabs.activeTab) |
|
// return this.$store.state.tabs.activeTab; |
|
// }, |
|
// set(val) { |
|
// console.log('activateTab========== set', val) |
|
// this.setActiveTab(val); |
|
// } |
|
set(tab) { |
|
if (!tab) { |
|
return this.$router.push({ |
|
query: {} |
|
}) |
|
} |
|
const [type, dbalias, name] = tab.split('||') |
|
this.$router.push({ |
|
query: { |
|
...this.$route.query, |
|
type, dbalias, name |
|
} |
|
}) |
|
}, |
|
get() { |
|
return [this.$route.query.type, this.$route.query.dbalias, this.$route.query.name].join('||') |
|
} |
|
} |
|
}, |
|
|
|
beforeCreated() { |
|
}, |
|
created() { |
|
document.addEventListener('keydown', this.handleKeyDown) |
|
/** |
|
* Listening for tab change so that we can hide/show projectlogs based on tab |
|
*/ |
|
// this.$store.watch( |
|
// function (state) { |
|
// return state.tabs.activeTabCtx; |
|
// }, |
|
// () => { |
|
// const tab = this.tabs[this.$store.state.tabs.activeTab]; |
|
// if (tab) |
|
// this.tabActivated(tab) |
|
// }, |
|
// // { |
|
// // deep: true //add this if u need to watch object properties change etc. |
|
// // } |
|
// ); |
|
|
|
|
|
this.checkInactiveState(); |
|
}, |
|
mounted() { |
|
}, |
|
beforeDestroy() { |
|
}, |
|
destroyed() { |
|
document.removeEventListener('keydown', this.handleKeyDown); |
|
}, |
|
validate({params}) { |
|
return true; |
|
}, |
|
head() { |
|
return {}; |
|
}, |
|
props: {}, |
|
watch: { |
|
// tabs() { |
|
// if (this.tabs.length > 0) { |
|
// const index = this.tabs.length - 1; |
|
// if (this.activeTab !==0 && this.activeTab === index) |
|
// this.setActiveTab(index - 1) |
|
// setTimeout(() => this.setActiveTab(index)); |
|
// // this.$refs.projectTabs.onResize(); |
|
// } |
|
// } |
|
}, |
|
directives: {} |
|
}; |
|
</script> |
|
|
|
<style scoped> |
|
/*/deep/ .project-tabs > .v-tabs-items {*/ |
|
/* border-top: 1px solid #7F828B33;*/ |
|
/*}*/ |
|
|
|
|
|
/deep/ .project-tabs .v-tabs-bar { |
|
max-height: 30px; |
|
} |
|
|
|
|
|
/deep/ .project-tabs > .v-tabs-bar { |
|
max-height: 35px; |
|
} |
|
|
|
|
|
/*/deep/ .project-tabs .v-tabs-slider-wrapper {*/ |
|
/* display: none;*/ |
|
/*}*/ |
|
|
|
|
|
/deep/ .project-tabs .v-tab.project-tab { |
|
text-transform: capitalize; |
|
border-top-left-radius: 6px; |
|
border-top-right-radius: 6px; |
|
background: #ffffff22; |
|
margin: 0px 1px 0 1px; |
|
color: white !important; |
|
} |
|
|
|
/deep/ .project-tabs .v-tab.v-tab--active.project-tab { |
|
background-color: white !important; |
|
color: rgba(51, 51, 51, 1) !important; |
|
} |
|
|
|
/deep/ .project-tabs.dark-them .v-tab.v-tab--active.project-tab { |
|
background-color: #272727 !important; |
|
color: white !important; |
|
} |
|
|
|
/* |
|
/deep/ .project-tabs.dark-them > div > div > div > div > .v-tabs-slider { |
|
color: #272727 !important; |
|
} |
|
*/ |
|
|
|
/deep/ .project-tabs > div > div > div > div > .v-tabs-slider { |
|
color: transparent !important; |
|
} |
|
|
|
/deep/ .project-tabs .v-btn { |
|
text-transform: capitalize; |
|
} |
|
|
|
|
|
.powered-by .powered-by-close { |
|
opacity: 0; |
|
transition: .4s opacity; |
|
} |
|
|
|
.powered-by a { |
|
transition: .1s font-weight; |
|
} |
|
|
|
.powered-by:hover .powered-by-close { |
|
opacity: 1; |
|
} |
|
|
|
.powered-by:hover a { |
|
font-weight: bold; |
|
} |
|
|
|
/deep/ .add-btn { |
|
margin-left: 5px; |
|
} |
|
|
|
|
|
/deep/ .screensaver.body { |
|
position: absolute; |
|
} |
|
|
|
</style> |
|
<!-- |
|
/** |
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
|
* |
|
* @author Naveen MR <oof1lab@gmail.com> |
|
* @author Pranav C Balan <pranavxc@gmail.com> |
|
* |
|
* @license GNU AGPL version 3 or any later version |
|
* |
|
* This program is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU Affero General Public License as |
|
* published by the Free Software Foundation, either version 3 of the |
|
* License, or (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU Affero General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU Affero General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
* |
|
*/ |
|
-->
|
|
|