270 lines
9.7 KiB
HTML
270 lines
9.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<link href="static/css/bootstrap.min.css" rel="stylesheet">
|
|
<script src="static/js/bootstrap.bundle.min.js"></script>
|
|
<script src="static/js/axios.min.js"></script>
|
|
<script src="static/js/vue.global.prod.js"></script>
|
|
<meta charset="UTF-8">
|
|
<title>应用管理</title>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="container" id="app">
|
|
|
|
<h1>应用管理页面</h1>
|
|
|
|
<br/>
|
|
|
|
<div class="d-flex">
|
|
<div class="align-self-start">用户名:{{ username }}</div>
|
|
<div class="ms-auto align-self-end">
|
|
<button class="btn btn-success" @click="showModal(null)">添加应用</button>
|
|
</div>
|
|
</div>
|
|
|
|
<br/>
|
|
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>应用名</th>
|
|
<th>描述</th>
|
|
<th>镜像名</th>
|
|
<th>启动命令</th>
|
|
<th>环境变量</th>
|
|
<th>状态</th>
|
|
<th>服务名</th>
|
|
<th>端口</th>
|
|
<th>操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(item, index) in apps">
|
|
<td> {{ index + 1 }}</td>
|
|
<td> {{ item.AppName }}</td>
|
|
<td> {{ item.Comment }}</td>
|
|
<td> {{ item.Image }}</td>
|
|
<td> {{ item.Command }}</td>
|
|
<td> {{ item.Env }}</td>
|
|
<td>
|
|
<span v-if="item.Status == 'Halt'">
|
|
{{ item.Status }}
|
|
</span>
|
|
<span v-else>
|
|
<a href="javascript:void(0)" @click="showPodStatus(item)">{{ item.Status }}</a>
|
|
</span>
|
|
</td>
|
|
<td><a :href="'/' + item.ServiceDisplayName + '/'" target="_blank">{{ item.ServiceDisplayName }}</a></td>
|
|
<td> {{ item.Ports }}</td>
|
|
<td>
|
|
<div class="btn-group">
|
|
<button class="btn btn-success" @click="startApp(item)">启动</button>
|
|
<button class="btn btn-dark" @click="stopApp(item)">停止</button>
|
|
<button class="btn btn-primary" @click="showModal(item)">修改</button>
|
|
<button class="btn btn-danger" @click="deleteApp(item)">删除</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr v-if="apps.length == 0">
|
|
<td colspan="7" style="text-align: center;">点击+按钮添加应用</td>
|
|
</tr>
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
|
|
<div class="modal" tabindex="-1" id="edit-modal">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">编辑应用</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
|
|
<div class="row">
|
|
<label for="app-name" class="form-label">应用名</label>
|
|
<input type="text" id="app-name" class="form-control" v-model="selectApp.AppName"
|
|
placeholder="只能为小写字母、数字和下划线"/>
|
|
|
|
</div>
|
|
<div class="row">
|
|
<label for="app-image" class="form-label">镜像</label>
|
|
<input type="text" id="app-image" class="form-control" v-model="selectApp.Image"
|
|
placeholder="Docker镜像名"/>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<label for="app-cmd" class="form-label">启动命令</label>
|
|
<input type="text" id="app-cmd" class="form-control" v-model="selectApp.Command"
|
|
placeholder="启动命令,可选"/>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<label for="app-env" class="form-label">环境变量</label>
|
|
<input type="text" id="app-env" class="form-control" v-model="selectApp.Env"
|
|
placeholder="环境变量,可选"/>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<label for="app-svc" class="form-label">服务名</label>
|
|
<input type="text" id="app-svc" class="form-control" v-model="selectApp.SvcName"
|
|
placeholder="服务名,可选"/>
|
|
</div>
|
|
|
|
|
|
<div class="row">
|
|
<label for="app-ports" class="form-label">端口号</label>
|
|
<input type="text" id="app-ports" class="form-control" v-model="selectApp.Ports"
|
|
placeholder="端口号"/>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<label for="app-ports" class="form-label">持久化路径</label>
|
|
<input type="text" id="app-persistent-path" class="form-control"
|
|
v-model="selectApp.PersistentPath"
|
|
placeholder="持久化路径"/>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<label for="app-comment" class="form-label">描述</label>
|
|
<textarea type="text" id="app-comment" class="form-control" v-model="selectApp.Comment"
|
|
placeholder="应用描述"></textarea>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @click="updateApp()">保存
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="position-fixed top-0 end-0 p-3" style="z-index: 11">
|
|
<div id="liveToast" class="toast alert-danger" role="alert" aria-live="assertive" aria-atomic="true">
|
|
<div class="toast-header">
|
|
<strong class="me-auto">请求失败</strong>
|
|
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
</div>
|
|
<div class="toast-body" v-html="toastText">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="modal" tabindex="-1" id="pod-status-modal">
|
|
<div class="modal-dialog modal-dialog-scrollable">
|
|
<div class="modal-header">
|
|
Pod详情
|
|
</div>
|
|
<div class="modal-content">{{ podStatus }}</div>
|
|
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
</body>
|
|
|
|
<script>
|
|
|
|
const {createApp} = Vue
|
|
createApp({
|
|
data() {
|
|
return {
|
|
username: "",
|
|
apps: [],
|
|
blankApp: {},
|
|
toastText: "",
|
|
selectApp: {
|
|
Id: "",
|
|
AppName: "",
|
|
UserName: "",
|
|
Image: "",
|
|
Command: "",
|
|
Comment: "",
|
|
Env: "",
|
|
SvcName: "",
|
|
PersistentPath: "",
|
|
Ports: "",
|
|
},
|
|
timer: null,
|
|
podStatus: ""
|
|
}
|
|
},
|
|
methods: {
|
|
toast(text) {
|
|
this.toastText = text
|
|
let toastLiveExample = document.getElementById('liveToast')
|
|
let toast = new bootstrap.Toast(toastLiveExample)
|
|
toast.show()
|
|
},
|
|
handleResponse(e) {
|
|
if (e.data.msg !== "ok") {
|
|
this.toast(e.data.msg)
|
|
} else {
|
|
this.refresh()
|
|
}
|
|
},
|
|
showModal(a) {
|
|
if (!a) {
|
|
a = JSON.parse(JSON.stringify(this.blankApp))
|
|
}
|
|
this.selectApp = a;
|
|
(new bootstrap.Modal("#edit-modal")).show()
|
|
},
|
|
startApp(item) {
|
|
axios.post("dep?id=" + item.Id).then(this.handleResponse)
|
|
},
|
|
deleteApp(app) {
|
|
axios.delete("app", {
|
|
data: app
|
|
}).then(this.handleResponse)
|
|
},
|
|
stopApp(app) {
|
|
axios.delete("dep?id=" + app.Id).then(this.handleResponse)
|
|
},
|
|
updateApp() {
|
|
let app = this.selectApp
|
|
if (app.Id) {
|
|
axios.put("app", app).then(this.handleResponse)
|
|
} else {
|
|
app.Id = null
|
|
app.UserName = this.username
|
|
axios.post("app", app).then(this.handleResponse);
|
|
}
|
|
},
|
|
refresh() {
|
|
axios.get("apps").then(r => {
|
|
this.username = r.data.Username
|
|
this.apps = r.data.Apps
|
|
})
|
|
},
|
|
showPodStatus(app) {
|
|
axios.get("pod?id=" + app.Id).then(r => {
|
|
this.podStatus = JSON.stringify(r.data, null, 2);
|
|
(new bootstrap.Modal("#pod-status-modal")).show();
|
|
});
|
|
}
|
|
},
|
|
mounted() {
|
|
this.blankApp = JSON.parse(JSON.stringify(this.selectApp))
|
|
this.refresh()
|
|
if (this.timer) {
|
|
clearInterval(this.timer)
|
|
this.timer = null
|
|
}
|
|
setInterval(this.timer, this.refresh, 5000)
|
|
}
|
|
}).mount("#app")
|
|
|
|
</script>
|
|
</html> |