多维表格
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.
 
 
 
 
 
 

472 lines
14 KiB

<template>
<div>
<v-progress-linear
v-show="loading"
color="success"
indeterminate
absolute
height="4"
/>
<v-tabs
v-model="tab"
style="height: 100%"
height="40"
>
<v-tab
v-for="(n,i) in terminals"
:key="n"
class="caption"
>
<span @dblclick="dblClick"> Local ({{ i + 1 }})</span>
<v-icon
v-if="terminals.length > 1"
class="ml-2 grey--text text--lighten-1"
small
@click.native.stop="closeTerminal(i)"
>
mdi-close
</v-icon>
</v-tab>
<x-icon class="mx-2" small @click="addTerminal">
mdi-plus
</x-icon>
<span v-shortkey="['meta','t']" @shortkey="addTerminal" />
<span v-if="isModal" v-shortkey="['meta','w']" @shortkey="terminals.length && closeTerminal(tab)" />
<div v-if="!isDocker" class="flex-grow-1 d-flex text-right pr-4 justify-end ">
<x-icon
v-if="_isWindows"
icon-class="mr-4"
:disabled="loading"
tooltip="Set-ExecutionPolicy"
color="success success"
@click="setExecutionPolicy()"
>
mdi-consolidate
</x-icon>
<template v-if="!isNoApis">
<x-icon
v-if="isDashboard && !isGraphql"
icon-class="mr-4"
:disabled="loading"
tooltip="Generate REST Code (^ ⇧ M)"
color="success success"
@click="runXcGenApisRest()"
>
{{ isTsEnabled ? 'mdi-language-typescript' : 'mdi-language-javascript' }}
</x-icon>
<span v-if="isDashboard" v-shortkey="['ctrl','shift','m']" @shortkey="runXcGenApisRest()" />
<x-icon
v-if="isDashboard && isGraphql"
icon-class="mr-4"
:disabled="loading"
color="pink pink"
tooltip="Generate GraphQL Code (^ ⇧ Q)"
@click="runXcGenApisGql()"
>
{{ isTsEnabled ? 'mdi-language-typescript' : 'mdi-language-javascript' }}
</x-icon>
<span v-if="isDashboard" v-shortkey="['ctrl','shift','q']" @shortkey="runXcGenApisGql()" />
<x-icon
v-if="isDashboard && showDelete"
icon-class="mr-4"
:disabled="loading"
color="error error"
tooltip="Delete all code (^ ⇧ C)"
@click="codeClear()"
>
mdi-delete-variant
</x-icon>
<span v-if="isDashboard" v-shortkey="['ctrl','shift','c']" @shortkey="codeClear()" />
<x-icon
v-if="isDashboard"
icon-class="mr-4"
:disabled="loading"
size="30"
color="error error"
tooltip="npm install (^ ⇧ I)"
@click="runNpmInstall()"
>
mdi-npm
</x-icon>
<span v-if="isDashboard" v-shortkey="['ctrl','shift','i']" @shortkey="runNpmInstall()" />
<x-icon
color="success success"
:disabled="loading"
tooltip="Run Command (^ ⇧ R)"
icon-class="mr-2"
size="29"
@click="runScript"
>
mdi-play
</x-icon>
<span v-if="isDashboard" v-shortkey="['ctrl','shift','r']" @shortkey="runScript()" />
<x-icon
color="red red"
:disabled="loading"
tooltip="Stop Command (^ ⇧ S)"
icon-class="mr-2"
@click="stopScript"
>
mdi-stop
</x-icon>
<span v-if="isDashboard" v-shortkey="['ctrl','shift','s']" @shortkey="stopScript()" />
<x-icon
color="info info"
:disabled="loading"
tooltip="Clear (^ ⇧ E)"
icon-class="mr-2"
@click="clearScript"
>
mdi-notification-clear-all
</x-icon>
<span v-if="isDashboard" v-shortkey="['ctrl','shift','e']" @shortkey="stopScript()" />
<v-tooltip bottom>
<template #activator="{on}">
<img height="24" class="mt-2 mr-2 pointer" src="~/assets/img/brand/favicon-64.png" v-on="on" @click="installCliTool">
</template>
Installs NocoDB CLI<br>( npm install -g xc-cli )
</v-tooltip>
<div class="flex-shrink-1 ">
<v-select
v-model="selectedScript"
height="32"
class="caption envs"
dense
:items="scripts"
placeholder="package.json"
single-line
outlined
>
<template #selection="{item}">
<span
style="text-transform: uppercase"
>{{ item }}</span>&nbsp; <span class="grey--text">(env)</span>
</template>
<!-- <template v-slot:prepend-outer>-->
<!-- -->
<!-- </template>-->
</v-select>
</div>
</template>
</div>
<v-tab-item
v-for="i in terminals"
:key="i"
>
<div :key="i" ref="term" style="height:100%" class="terminal-window" />
</v-tab-item>
</v-tabs>
</div>
</template>
<script>
import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
import { WebLinksAddon } from 'xterm-addon-web-links'
import { mapGetters } from 'vuex'
import XIcon from './global/xIcon'
export default {
name: 'XTerm',
components: { XIcon },
props: {
isModal: {
default: false,
type: Boolean
}
},
computed: {
...mapGetters({
currentProjectFolder: 'project/currentProjectFolder',
sqlMgr: 'sqlMgr/sqlMgr',
phql: 'project/GtrProjectIsGraphql',
isNoApis: 'project/GtrProjectIsNoApis',
isDocker: 'project/GtrIsDocker'
}),
isTsEnabled() {
return false // return process.env.TS_ENABLED;
}
},
data() {
return {
tab: 0,
terminals: [Date.now()],
termRef: [],
scripts: [],
selectedScript: null,
loading: false,
showDelete: false
}
},
async mounted() {
// try {
// const packageData = await jsonfile.readFileSync(path.join(this.currentProjectFolder, 'package.json'));
// this.scripts = Object.keys(packageData.scripts);
// } catch (e) {
// console.log('package============', e)
// }
setTimeout(() => {
this.initTerminal(this.$refs.term[0], 0)
}, 200)
},
destroyed() {
let l = this.terminals.length
while (l--) { this.closeTerminal(l) }
},
methods: {
dblClick() {
// this.showDelete = true;
// this.$set(process.env, 'TS_ENABLED', process.env.TS_ENABLED ? '' : '1')
},
async setExecutionPolicy() {
// if (!this._isWindows) return
// try {
// let {ptyProc} = this.termRef[this.tab];
// ptyProc.write(`${ptyProc.process === 'node' ? '\u0003\n\r' : ''}Set-ExecutionPolicy "Bypass";\n\r`)
// } catch (e) {
// console.log(e)
// }
},
async handleKeyDown(event) {
// switch ([this._isMac ? event.metaKey : event.ctrlKey, event.key].join('_')) {
// case 'true_w' :
// event.preventDefault();
// event.stopPropagation();
// if (this.termRef.length > 0) {
// if (!this.isModal)
// this.closeTerminal(this.tab);
// return true;
// }
// break;
// }
// return false;
},
async codeGenerateMvc() {
this.loading = true
// try {
// await this.setExecutionPolicy();
// await this.sqlMgr.projectGenerateBackend({
// env: 'dev',
// });
// this.$toast.info('Yay, code generated').goAway(4000);
// this.runNpmInstall()
// this.runScript()
// } catch (e) {
// this.$toast.error('Error generating REST APIs code :' + e).goAway(4000);
// throw e;
// } finally {
// this.loading = false;
// }
},
async codeGenerateMvcGql() {
this.loading = true
// try {
// await this.sqlMgr.projectGenerateBackendGql({
// env: 'dev',
// });
// this.$toast.info('Yay, GraphQL with MVC generated').goAway(4000);
// this.runNpmInstall()
// this.runScript()
//
// } catch (e) {
// this.$toast.error('Error generating GraphQL code :' + e).goAway(4000);
// throw e;
// } finally {
// this.loading = false;
// }
},
async codeClear() {
this.loading = true
// try {
// await this.setExecutionPolicy();
// await this.sqlMgr.projectGeneratedCodeClear({
// env: 'dev',
// });
// this.$toast.info('Yay, code cleared').goAway(4000);
// } catch (e) {
// this.$toast.error('Error generating GraphQL code :' + e).goAway(4000);
// throw e;
// } finally {
// this.loading = false;
// }
},
runXcGenApisRest() {
// this.setExecutionPolicy();
// let {ptyProc} = this.termRef[this.tab];
// ptyProc.write(`${ptyProc.process === 'node' ? '\u0003\n\r' : ''}cd ${this.currentProjectFolder};\n\rxc gen.apis.rest;\n\r`)
},
runXcGenApisGql() {
// let {ptyProc} = this.termRef[this.tab];
// ptyProc.write(`${ptyProc.process === 'node' ? '\u0003\n\r' : ''}cd ${this.currentProjectFolder};\n\rxc gen.apis.graphql;\n\r`)
},
runNpmInstall() {
const { client } = this.termRef[this.tab]
client.emit('req', '\n\rnpm install;\n\r')
},
runScript() {
// let {ptyProc} = this.termRef[this.tab];
// ptyProc.write(`${ptyProc.process === 'node' ? '\u0003\n\r' : ''}cd ${this.currentProjectFolder};\n\rnpm run ${this.selectedScript || 'dev'};\n\r`)
},
stopScript() {
// let {ptyProc} = this.termRef[this.tab];
// ptyProc.write(`\u0003\n\r`)
},
clearScript() {
// let {ptyProc} = this.termRef[this.tab];
// ptyProc.write(`clear\n\r`)
},
installCliTool() {
// let {ptyProc} = this.termRef[this.tab];
// ptyProc.write(`npm install -g xc-cli\n\r`)
},
addTerminal() {
if (this.terminals.length > 4) {
this.$toast.info('Only 5 terminals can be opened').goAway()
return
}
this.terminals.push(Date.now())
this.tab = this.terminals.length - 1
this.$nextTick(() => {
this.initTerminal(this.$refs.term[this.terminals.length - 1], this.terminals.length - 1)
})
},
initTerminal($el, index) {
try {
// todo: change to hostname
const client = require('socket.io-client')('http://localhost:8081')
const term = new Terminal({
theme: {
background: 'black',
foreground: 'green'
}
})
term.loadAddon(new WebLinksAddon((e, url) => {
e.preventDefault()
console.log(url)
}))
// //
// //
const fitAddon = new FitAddon()
term.loadAddon(fitAddon)
// //
term.open($el)
// //
fitAddon.fit()
//
// const ligaturesAddon = new LigaturesAddon();
// term.loadAddon(ligaturesAddon)
//
// fixPath();
//
// const ptyProc = pty.spawn(os.platform() === 'win32' ? 'powershell.exe' : process.env.SHELL || '/bin/bash', [], {
// cols: term.cols,
// rows: term.rows,
// cwd: this.currentProjectFolder || process.env.HOME,
// env: process.env
// });
//
term.onData((data) => {
console.log(data)
client.emit('req', data)
// term.write(data.replace(/\n/, '\r\n'));
})
client.on('res', (data) => {
term.write(`${data}`)
})
// ptyProc.onData(data => {
// term.write(data);
// });
//
term.focus()
//
this.termRef[index] = {
term,
client
}
} catch (e) {
this.$toast.error('Error opening the terminal\n\nPlease use your system terminal').goAway(5000)
}
},
closeTerminal(index) {
console.log('======= close terminal')
try {
const proc = this.termRef.splice(index, 1)[0]
proc.term.dispose()
proc.client.off()
proc.client.disconnect()
this.terminals.splice(index, 1)
} catch (e) {
console.log(e)
throw e
}
}
}
}
</script>
<style scoped>
/deep/ .v-tabs-items {
height: calc(100% - 40px);
}
/deep/ .v-window__container {
height: 100%;
}
/deep/ .v-window__container .v-window-item {
height: 100%;
}
/deep/ .terminal-window > div {
padding: 0 5px;
}
</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/>.
*
*/
-->