Browse Source

add terminal

master
许孟阳 2 years ago
parent
commit
b17ef8876c
  1. 26
      examples/App.vue
  2. 1
      index.ts
  3. 13
      package.json
  4. 3
      packages/index.ts
  5. 19
      packages/list-table/src/listTable.vue
  6. 3
      packages/termimal/index.ts
  7. 122
      packages/termimal/src/terminal.vue
  8. 15
      yarn.lock

26
examples/App.vue

@ -1,14 +1,27 @@
<template> <template>
<WkbTag>ssss</WkbTag> <WkbTag>ssss</WkbTag>
<el-button type="success" @click="newTerminal()">新终端</el-button>
<ListTable :data="data" :height="500" :props="prop"></ListTable> <ListTable :data="data" :height="500" :props="prop"></ListTable>
<el-dialog class="no-padding" title="实时脚本终端" v-model="flag.termial" size="tiny" :close-on-click-modal="false" top="10vh"
width="80%">
<Terminal :url="url"></Terminal>
</el-dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, onMounted } from "vue"; import { reactive, onMounted } from "vue";
import { useStore } from "vuex"; import { useStore } from "vuex";
import { WkbTag, ListTable } from "../packages"; import { WkbTag, ListTable, Terminal } from "../packages";
const store = useStore(); const store = useStore();
const prefix = "ws://localhost/websocket/";
let index = 111;
let url = "";
const flag = reactive({
termial: false
})
const data = [ const data = [
{ caseName: 111, taskName: 111, userId: 'test', content: 'content', createTime: 'createTime' } { caseName: 111, taskName: 111, userId: 'test', content: 'content', createTime: 'createTime' }
@ -42,12 +55,19 @@ const prop = [
}, },
]; ];
const newTerminal = () => {
url = prefix + ++index;
flag.termial = true;
}
onMounted(() => { onMounted(() => {
}); });
</script> </script>
<style> <style lang="scss">
@charset "UTF-8"; ::v-deep .no-padding .el-dialog__body {
background-color: black;
}
</style> </style>

1
index.ts

@ -0,0 +1 @@
export * from "./packages"

13
package.json

@ -2,20 +2,23 @@
"name": "noob", "name": "noob",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"main": "packages/index.ts", "main": "index.ts",
"scripts": { "scripts": {
"dev": "vue-cli-service serve", "dev": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"axios": "^0.19.2",
"core-js": "^3.8.3", "core-js": "^3.8.3",
"element-plus": "2.2.18",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-class-component": "^8.0.0-0", "vue-class-component": "^8.0.0-0",
"element-plus": "2.2.18",
"axios": "^0.19.2",
"vue-router": "^4.0.3", "vue-router": "^4.0.3",
"vuex": "^4.0.0" "vuex": "^4.0.0",
"xterm": "^5.2.1",
"xterm-addon-attach": "^0.8.0",
"xterm-addon-fit": "^0.7.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-babel": "~5.0.0",
@ -47,4 +50,4 @@
"not dead", "not dead",
"not ie 11" "not ie 11"
] ]
} }

3
packages/index.ts

@ -2,5 +2,6 @@ import WkbTag from './tag';
import ListTable from './list-table'; import ListTable from './list-table';
import SearchRow from './search-row'; import SearchRow from './search-row';
import ModifyForm from './modify-form'; import ModifyForm from './modify-form';
import Terminal from './termimal';
export { WkbTag, ListTable, SearchRow }; export { WkbTag, ListTable, SearchRow, ModifyForm, Terminal };

19
packages/list-table/src/listTable.vue

@ -1,19 +1,14 @@
<template> <template>
<div class="my-table"> <div class="my-table">
<el-table :data="page ? data.data : data" <el-table :data="page ? data.data : data" @selection-change="selectionChange"
:height="height ? height : (page ? state.sizes.pTableHei : state.sizes.tableHei)" highlight-current-row> :height="height ? height : (page ? state.sizes.pTableHei : state.sizes.tableHei)" highlight-current-row>
<el-table-column v-for="item in props" :key="item.code" :prop="item.code" :label="item.name" <el-table-column v-for="item in props" :key="item.code" :prop="item.code" :label="item.name" :type="item.type"
:min-width="item.width" :width="item.type ? item.width : ''" :align="item.align ? item.align : 'center'" :min-width="item.width" :width="item.type ? item.width : ''" :align="item.align ? item.align : 'center'"
show-overflow-tooltip :formatter="formatter"> show-overflow-tooltip :formatter="formatter">
<template v-if="item.slot" #default="scope"> <template v-if="item.slot" #default="scope">
<slot :name="item.code" :row="scope.row"></slot> <slot :name="item.code" :row="scope.row"></slot>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column v-if="prop.actionWidth" label="操作" :min-width="prop.actionWidth" align="center">
<template #default="scope">
<slot v-bind:row="scope.row" v-bind:$index="scope.$index"></slot>
</template>
</el-table-column>
</el-table> </el-table>
</div> </div>
<div v-if="page" class="my-pagination"> <div v-if="page" class="my-pagination">
@ -45,10 +40,6 @@ const prop = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
actionWidth: {
type: Number,
default: null,
},
height: { height: {
type: Number, type: Number,
default: null, default: null,
@ -60,7 +51,11 @@ const prop = defineProps({
}); });
const { state } = useStore(); const { state } = useStore();
const emit = defineEmits(["query"]); const emit = defineEmits(["query", "selection-change"]);
const selectionChange = selection => {
emit("selection-change", selection);
};
const handleSizeChange = (val) => { const handleSizeChange = (val) => {
prop.example.size = val; prop.example.size = val;

3
packages/termimal/index.ts

@ -0,0 +1,3 @@
import Terminal from './src/terminal.vue'
export default Terminal

122
packages/termimal/src/terminal.vue

@ -0,0 +1,122 @@
<template>
<div id="terminal" v-loading="flag.loading" ref="terminal" element-loading-text="拼命连接中"></div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, watch, reactive } from "vue";
import { Terminal } from "xterm";
import "xterm/css/xterm.css";
const prop = defineProps({
url: {
type: String,
default: null
}
});
const flag = reactive({
loading: true,
lock: true
});
const terminal = ref(null);
const terminalSocket = ref();
const term = ref();
let text = "";
const runRealTerminal = () => {
flag.loading = false;
}
const onWSReceive = (message) => {
term.value.write(message.data)
flag.lock = false;
}
const errorRealTerminal = (ex) => {
let message = ex.message;
if (!message) message = 'disconnected'
term.value.write(`\x1b[31m${message}\x1b[m\r\n`)
console.log("err");
}
const closeRealTerminal = () => {
console.log("close");
}
const initWS = () => {
if (!prop.url) {
return;
}
terminalSocket.value = new WebSocket(prop.url);
terminalSocket.value.onopen = runRealTerminal;
terminalSocket.value.onmessage = onWSReceive;
terminalSocket.value.onclose = closeRealTerminal;
terminalSocket.value.onerror = errorRealTerminal;
}
const initTerm = () => {
term.value = new Terminal({
lineHeight: 1.2,
fontSize: 18,
fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
theme: {
background: '#181d28',
},
//
cursorBlink: true,
cursorStyle: 'underline',
scrollback: 100,
tabStopWidth: 4,
cols: 120
});
term.value.open(terminal.value);
term.value.onKey(e => {
if (flag.lock) {
return;
}
//\x1B ESC
//\x1BOP-\x1B[24~ F1-F12
const key = e.key;
if (key.indexOf("\u001b") !== -1) {
return;
}
console.log(e);
if (isWsOpen()) {
if (key == '\r') {
term.value.writeln(key)
terminalSocket.value.send(text);
text = "";
} else {
text += key
term.value.write(key)
}
}
});
}
// 0 1 2 3
const isWsOpen = () => {
const readyState = terminalSocket.value && terminalSocket.value.readyState;
return readyState === 1
}
//term
watch(() => prop.url, () => {
flag.loading = true;
terminalSocket.value && terminalSocket.value.close();
terminalSocket.value = null;
initWS();
//
term.value.reset();
})
onMounted(() => {
initWS();
initTerm();
})
onBeforeUnmount(() => {
terminalSocket.value && terminalSocket.value.close();
term.value.dispose();
})
</script>
<style lang="scss" scoped>
#terminal {
width: 100%;
height: 100%;
}
</style>

15
yarn.lock

@ -5841,6 +5841,21 @@ ws@^8.13.0:
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
xterm-addon-attach@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/xterm-addon-attach/-/xterm-addon-attach-0.8.0.tgz#86e0ed08460efffb7d4bad74a57b600226594def"
integrity sha512-k8N5boSYn6rMJTTNCgFpiSTZ26qnYJf3v/nJJYexNO2sdAHDN3m1ivVQWVZ8CHJKKnZQw1rc44YP2NtgalWHfQ==
xterm-addon-fit@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.7.0.tgz#b8ade6d96e63b47443862088f6670b49fb752c6a"
integrity sha512-tQgHGoHqRTgeROPnvmtEJywLKoC/V9eNs4bLLz7iyJr1aW/QFzRwfd3MGiJ6odJd9xEfxcW36/xRU47JkD5NKQ==
xterm@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.1.tgz#b3fea7bdb55b9be1d4b31f4cd1091f26ac42afb8"
integrity sha512-cs5Y1fFevgcdoh2hJROMVIWwoBHD80P1fIP79gopLHJIE4kTzzblanoivxTiQ4+92YM9IxS36H1q0MxIJXQBcA==
y18n@^5.0.5: y18n@^5.0.5:
version "5.0.8" version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"

Loading…
Cancel
Save