add http web ui files (#1260)

pull/1283/head
gongdewei 5 years ago committed by GitHub
parent 6952d92cbb
commit c69d79022f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Arthas WebUI</title>
</head>
<body>
<script src="../lib/vue/vue-2.6.11.js"></script>
<script src="../lib/vue/vue-router-3.1.6.js"></script>
<script src="../lib/axios-0.19.2.min.js"></script>
<script src="../element-ui@2.13.0/lib/index.js"></script>
<link rel="stylesheet" href="../element-ui@2.13.0/lib/theme-chalk/index.css">
<link rel="stylesheet" href="ui.css">
<div id="app">
<router-view/>
</div>
<template id="session-view">
<div>
<el-container class="container">
<el-header class="header">
<el-row >
<el-col :span="12">
<div class="logo">
<a href="https://github.com/alibaba/arthas" target="_blank">
<img src="../logo.png" height="25px">
</a>
</div>
<div class="logo">
<a class="nav-link" href="https://alibaba.github.io/arthas" target="_blank">Documentation</a>
<a class="nav-link" href="https://alibaba.github.io/arthas-tutorials" target="_blank">Online Tutorials</a>
<a class="nav-link" href="https://alibaba.github.io/arthas" target="_blank">Github</a>
</div>
</el-col>
<el-col :span="12" class="header-right">
<el-button type="primary" size="mini" @click="openNewSession">New</el-button>
<el-button type="primary" size="mini">Share</el-button>
<el-button type="primary" size="mini">Close</el-button>
<el-button type="primary" size="mini">Shutdown</el-button>
<div class="user-info">
<i class="el-icon-s-custom user-icon"></i>
<span>Tom</span>
</div>
</el-col>
</el-row>
</el-header>
<el-main id="main-body" class="main-body">
<div v-for="result in commandResults" >
<el-card class="resultMsg" v-bind:class="messageClass(result)">
{{JSON.stringify(result)}}
</el-card>
<div class="min-gap"></div>
</div>
</el-main>
<!-- 底部 -->
<el-footer class="footer-row">
<div class="footer-left">
<div>Command:</div>
</div>
<div class="footer-input">
<el-input ref="commandInput"
placeholder="Enter command"
autosize
clearable
@keyup.enter.native="executeCommand"
:disabled="inputStatus!='ALLOW_INPUT'"
v-model="commandLine">
</el-input>
</div>
<div class="footer-button">
<el-button v-if="inputStatus=='ALLOW_INPUT'" type="primary" size="mini" @click="executeCommand">Execute</el-button>
<el-button v-if="inputStatus=='ALLOW_INTERRUPT'" type="danger" size="mini" @click="interruptJob">Interrupt</el-button>
</div>
</el-footer>
</el-container>
</div>
</template>
<template id="history-view">
<div>
<el-container class="container">
<el-header class="header">
<el-row >
<el-col :span="12">
<div class="logo">
<a href="https://github.com/alibaba/arthas" target="_blank">
<img src="../logo.png" height="25px">
</a>
</div>
<div class="logo">
<a class="nav-link" href="https://alibaba.github.io/arthas" target="_blank">Documentation</a>
<a class="nav-link" href="https://alibaba.github.io/arthas-tutorials" target="_blank">Online Tutorials</a>
<a class="nav-link" href="https://alibaba.github.io/arthas" target="_blank">Github</a>
</div>
</el-col>
<el-col :span="12" class="header-right">
<el-button type="primary" size="mini">New</el-button>
<el-button type="primary" size="mini">Share</el-button>
<el-button type="primary" size="mini">Close</el-button>
<el-button type="primary" size="mini">Shutdown</el-button>
<div class="user-info">
<i class="el-icon-s-custom user-icon"></i>
<span>Tom</span>
</div>
</el-col>
</el-row>
</el-header>
<el-main>
</el-main>
</el-container>
</div>
</template>
<template id="search-view">
</template>
<script src="ui.js"></script>
</body>
</html>

@ -0,0 +1,116 @@
body {
margin: 0px;
overflow: hidden;
}
h4 {
margin: 0 5px 5px 5px;
}
p {
margin: 0px;
}
.min-gap {
height: 6px;
}
.logo {
vertical-align: middle;
display: inline-block;
}
.user-info {
margin-left: 10px;
vertical-align: middle;
display: inline-block;
}
.user-icon {
font-size: 24px;
}
a.nav-link:active, a.nav-link:hover, a.nav-link:visited {
color: rgba(0,0,0,.9);
text-decoration: none;
}
.nav-link {
color: rgba(0,0,0,.5);
display: inline-block;
padding: .5rem 0.3rem;
text-decoration: none;
}
.container {
display: flex;
flex-direction:column;
height: 100vh;
}
.header {
flex: 0 0 auto;
padding: 8px;
border-bottom: #cccccc 1px solid;
}
.header-right {
text-align: right;
}
.main-body {
/*height: 80vh;*/
flex: 1 1 auto;
overflow-y: auto;
}
.footer-row {
flex: 0 0 auto;
display: flex;
padding-top: 5px;
height: 45px !important;
vertical-align: middle;
border-top: #cccccc 1px solid;
}
.footer-left {
/*width: 100px;*/
vertical-align: middle;
padding: 0.5em;
}
.footer-input {
flex: 1;
margin: 0 10px;
}
.footer-input .el-input__inner {
height: 35px;
}
.footer-button {
/*width: 100px;*/
}
.main-body pre {
margin: 0;
}
.errorMsg {
color: #c82333;
}
.resultMsg {
background-color: #f0f0f0;
}
.commandMsg {
background-color: #d9ecff !important;
}
/*****************************/
/* override element-ui class*/
/*****************************/
.el-card__body {
/*margin: 2px;*/
padding: 8px;
/*background-color: #cccccc;*/
}
.el-main {
padding: 10px;
/*background-color: #f7f8f9;*/
}
.el-header {
padding: 10px;
background-color: #f7f8f9;
}

@ -0,0 +1,363 @@
const InputStatus= {
/**
* Allow input new commands
*/
ALLOW_INPUT: 'ALLOW_INPUT',
/**
* Allow interrupt running job
*/
ALLOW_INTERRUPT: 'ALLOW_INTERRUPT',
/**
* Disable input and interrupt
*/
DISABLED: 'DISABLED'
};
//session data
function getDefaultSessionData() {
return {
sessionId: null,
consumerId: null,
lastPullResultTime: 0,
commandLine: '',
commandLineDisabled: true,
commandResults: [],
executingJobId: null,
lastFinishedJobId: 0,
inputStatus: InputStatus.DISABLED,
startPullResults: false,
};
}
let sessionData = getDefaultSessionData();
//session view
let SessionView = Vue.component('session-view',{
template: '#session-view',
data() {
return sessionData;
},
methods: {
resetData: function () {
Object.assign(this.$data, getDefaultSessionData());
},
messageClass(item){
return {
commandMsg: item.command!=null,
errorMsg: item.state == 'FAILED' || item.state == 'REFUSED',
}
},
onSessionReady(){
this.commandLineDisabled = false;
},
openNewSession(){
window.open("/ui/", "_blank");
},
initPage(){
console.log("initPage");
let sessionId = this.$route.params.sessionId;
if (sessionId && sessionId!='undefined'){
//join_session, init new consumerId
axios
.post('/api',{
"action": "join_session",
"sessionId": sessionId
})
.then(response => {
let apiResponse = response.data;
if (apiResponse.state == "SUCCEEDED" && sessionId == apiResponse.sessionId){
this.sessionId = apiResponse.sessionId;
this.consumerId = apiResponse.consumerId;
this.pullResults();
this.onSessionReady();
}else {
//加入会话失败,创建新会话
this.askForNewSession(true);
}
})
.catch(error => { // 请求失败处理
console.log(error);
this.$alert('Connect to server failed: '+error.message, {
type: 'error'
});
});
} else {
this.askForNewSession(false);
}
},
askForNewSession(showAlert){
if (showAlert) {
this.$alert('The session does not exist, new session will be opened', {
type: 'warning',
callback: action => {
if (action == 'confirm') {
this.initSession();
}
}
});
} else {
this.initSession();
}
},
initSession(){
console.log("initSession");
this.resetData();
axios
.post('/api',{
"action": "init_session"
})
.then(response => {
let apiResponse = response.data;
if (apiResponse.state == "SUCCEEDED"){
this.sessionId = apiResponse.sessionId;
this.consumerId = apiResponse.consumerId;
this.$router.push("/session/"+this.sessionId);
this.pullResults();
this.onSessionReady();
}
})
.catch(error => { // 请求失败处理
console.log(error);
this.$alert('Init session failed: '+error.message, {
type: 'error'
});
});
},
interruptJob(){
console.log("interruptJob");
axios
.post('/api',{
"action": "interrupt_job",
"sessionId": this.sessionId,
})
.then(response => {
let apiResponse = response.data;
if (apiResponse.state == "SUCCEEDED"){
this.onSessionReady();
}
})
.catch(error =>{ // 请求失败处理
console.log(error);
this.$message.error('Interrupt current job failed: '+error.message);
});
},
pullResults(){
//保证只有一个拉取消息的timer
if (this.startPullResults){
return;
}
this.startPullResults = true;
this.lastPullResultTime = new Date().getTime();
//接收消息推送
axios
.post('/api',{
"action": "pull_results",
"sessionId": this.sessionId,
"consumerId": this.consumerId
})
.then(response => {
this.pullResultFailedCount = 0;
let apiResponse = response.data;
if (apiResponse.state == "SUCCEEDED"){
this.appendResults(apiResponse.body.results);
this.delayPullResults();
} else {
console.error("Pull results failed: ", apiResponse);
this.inputStatus = InputStatus.DISABLED;
this.$alert('Pull results failed: '+apiResponse.message, {
type: 'error'
});
}
})
.catch(error => { // 请求失败处理
console.log(error);
if(++this.pullResultFailedCount > 10){
this.inputStatus = InputStatus.DISABLED;
//show pull error
this.$alert('Pull results failed: '+error.message, {
type: 'error'
});
} else {
this.delayPullResults(2000);
}
});
},
delayPullResults(delay) {
this.startPullResults = false;
if(!delay){
delay = (new Date().getTime() - this.lastPullResultTime < 500)?500 : 50;
}
setTimeout(this.pullResults.bind(this), delay);
},
appendResults(results) {
if (!results || !results.length || results.length == 0){
return;
}
//split results
while(results.length > 0){
//Restrict command results
while(this.commandResults.length > 500){
this.commandResults.shift();
}
let result = results.shift();
console.log("result: ", result);
if (result.type == 'input_status') {
this.inputStatus = result.inputStatus;
continue;
}
if (result.type == "status" && result.statusCode!=null){
//命令执行完毕后允许输入
this.setFinishedJobId(result.jobId);
if (result.message) {
this.commandResults.push(result);
}
} else {
this.commandResults.push(result);
}
}
this.scrollContentToBottom();
},
// appendCommand(response){
// if (response && response.state) {
// //Restrict command results
// while(this.commandResults.length > 500){
// this.commandResults.shift();
// }
// response.body.state = response.state;
// if (response.message) {
// response.body.message = response.message;
// }
// if ( response.state == 'SCHEDULED' || response.state == 'SUCCEEDED' ){
// } else {
// }
// this.commandResults.push(response.body)
// }
// },
executeCommand(event) {
this.commandLine = this.commandLine.trim();
if (this.commandLine == ''){
return;
}
console.log("executing command: ", this.commandLine);
axios
.post('/api',{
"action": "async_exec",
"sessionId": this.sessionId,
"consumerId": this.consumerId,
"command": this.commandLine,
})
.then(response => {
let apiResponse = response.data;
if (apiResponse.state == "SCHEDULED" ){
//设置当前任务jobId禁止输入
this.setExecutingJobId(apiResponse.body.jobId);
} else {
//command process error or refused
this.setExecutingJobId(null);
}
//this.appendCommand(apiResponse);
})
.catch(error =>{ // 请求失败处理
console.log(error);
//this.setExecutingJobId(null);
this.$message.error('Execute command failed: '+error.message);
});
//disable input
this.commandLineDisabled = true;
},
setExecutingJobId(jobId) {
if (jobId!=null && jobId > this.lastFinishedJobId) {
this.executingJobId = jobId;
} else {
this.executingJobId = null;
this.setFocusCommandInput();
}
},
setFinishedJobId(jobId) {
this.lastFinishedJobId = jobId;
if (this.executingJobId && jobId >= this.executingJobId){
this.setExecutingJobId(null);
}
},
scrollContentToBottom(){
setTimeout(function () {
var container = this.$el.querySelector("#main-body");
container.scrollTop = container.scrollHeight;
}.bind(this), 50);
},
setFocusCommandInput() {
this.commandLine = '';
this.commandLineDisabled = false;
setTimeout(function () {
this.$refs.commandInput.focus();
}.bind(this), 50);
}
},
watch: {
'$route'(to, from){
console.log('route: ', to, from);
this.initPage();
}
},
created(){
this.initPage();
},
updated(){
}
});
let HistoryView = Vue.component('history-view', {
template: '#history-view',
data() {
return {
}
}
});
//页面路由
const routes = [
{ path: '/', redirect: '/session/' },
//{ path: '/', component: HistoryView },
{ path: '/session', component: SessionView },
{ path: '/session/:sessionId', component: SessionView },
{ path: '/history', component: HistoryView },
];
const router = new VueRouter({
routes
});
new Vue({
el: '#app',
router,
data: {
sessionData
},
methods: {
},
watch: {
},
created(){
}
});
Loading…
Cancel
Save