fix 新增个性化配置全屏打开日志弹窗(构建、SSH、脚本、Docker等日志)

This commit is contained in:
bwcx_jzy 2023-12-22 10:10:38 +08:00
parent c5d1d89f0f
commit 1fd12f10ba
No known key found for this signature in database
GPG Key ID: E187D6E9DDDE8C53
25 changed files with 549 additions and 170 deletions

View File

@ -5,6 +5,7 @@
### 🐞 解决BUG、优化功能
1. 【server】优化 逻辑节点节目取消全局 loading感谢@小菜鸡)
2. 【server】优化 新增个性化配置全屏打开日志弹窗构建、SSH、脚本、Docker等日志感谢@张飞鸿)
------

View File

@ -208,6 +208,9 @@ public class BuildInfoManageController extends BaseServerController {
// 运行中
Integer status = queryByBean.getStatus();
data.put("run", buildExecuteService.checkStatus(item) != null);
data.put("logId", queryByBean.getId());
data.put("status", status);
data.put("statusMsg", queryByBean.getStatusMsg());
// 构建中
//data.put("buildRun", status == BuildStatus.Ing.getCode());
return JsonMessage.success("ok", data);

View File

@ -109,8 +109,8 @@ public class ProjectManageControl extends BaseServerController {
*/
@RequestMapping(value = "project_copy_list", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST)
public IJsonMessage<Object> projectCopyList() {
return NodeForward.request(getNode(), getRequest(), NodeUrl.Manage_ProjectCopyList);
public IJsonMessage<Object> projectCopyList(HttpServletRequest request) {
return NodeForward.request(getNode(), request, NodeUrl.Manage_ProjectCopyList);
}
/**
@ -119,8 +119,8 @@ public class ProjectManageControl extends BaseServerController {
* @return json
*/
@RequestMapping(value = "getProjectPort", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public IJsonMessage<JSONObject> getProjectPort() {
return NodeForward.request(getNode(), getRequest(), NodeUrl.Manage_GetProjectPort);
public IJsonMessage<JSONObject> getProjectPort(HttpServletRequest request) {
return NodeForward.request(getNode(), request, NodeUrl.Manage_GetProjectPort);
}
/**
@ -129,8 +129,8 @@ public class ProjectManageControl extends BaseServerController {
* @return json
*/
@RequestMapping(value = "getProjectCopyPort", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public IJsonMessage<JSONObject> getProjectCopyPort() {
return NodeForward.request(getNode(), getRequest(), NodeUrl.Manage_GetProjectCopyPort);
public IJsonMessage<JSONObject> getProjectCopyPort(HttpServletRequest request) {
return NodeForward.request(getNode(), request, NodeUrl.Manage_GetProjectCopyPort);
}
@ -141,9 +141,8 @@ public class ProjectManageControl extends BaseServerController {
*/
@PostMapping(value = "get_project_info", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST)
public IJsonMessage<PageResultDto<ProjectInfoCacheModel>> getProjectInfo() {
PageResultDto<ProjectInfoCacheModel> modelPageResultDto = projectInfoCacheService.listPage(getRequest());
// JSONArray jsonArray = projectInfoService.listAll(nodeModel, getRequest());
public IJsonMessage<PageResultDto<ProjectInfoCacheModel>> getProjectInfo(HttpServletRequest request) {
PageResultDto<ProjectInfoCacheModel> modelPageResultDto = projectInfoCacheService.listPage(request);
return JsonMessage.success("", modelPageResultDto);
}
@ -155,33 +154,31 @@ public class ProjectManageControl extends BaseServerController {
*/
@PostMapping(value = "deleteProject", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.DEL)
public IJsonMessage<String> deleteProject(@ValidatorItem(value = ValidatorRule.NOT_BLANK) String id, String copyId) {
public IJsonMessage<String> deleteProject(@ValidatorItem(value = ValidatorRule.NOT_BLANK) String id,
String copyId,
HttpServletRequest request) {
NodeModel nodeModel = getNode();
HttpServletRequest servletRequest = getRequest();
if (StrUtil.isEmpty(copyId)) {
// 检查节点分发
outGivingServer.checkNodeProject(nodeModel.getId(), id, servletRequest);
outGivingServer.checkNodeProject(nodeModel.getId(), id, request);
// 检查日志阅读
logReadServer.checkNodeProject(nodeModel.getId(), id, servletRequest);
//
List<MonitorModel> monitorModels = monitorService.listByWorkspace(servletRequest);
logReadServer.checkNodeProject(nodeModel.getId(), id, request);
// 项目监控
List<MonitorModel> monitorModels = monitorService.listByWorkspace(request);
if (monitorModels != null) {
boolean match = monitorModels.stream().anyMatch(monitorModel -> monitorModel.checkNodeProject(nodeModel.getId(), id));
// if (monitorService.checkProject(nodeModel.getId(), id)) {
// return JsonMessage.getString(405, );
// }
Assert.state(!match, "当前项目存在监控项,不能直接删除");
}
boolean releaseMethod = buildService.checkReleaseMethod(nodeModel.getId() + StrUtil.COLON + id, servletRequest, BuildReleaseMethod.Project);
// 构建
boolean releaseMethod = buildService.checkReleaseMethod(nodeModel.getId() + StrUtil.COLON + id, request, BuildReleaseMethod.Project);
Assert.state(!releaseMethod, "当前项目存在构建项,不能直接删除");
}
JsonMessage<String> request = NodeForward.request(nodeModel, servletRequest, NodeUrl.Manage_DeleteProject);
if (request.success()) {
JsonMessage<String> jsonMessage = NodeForward.request(nodeModel, request, NodeUrl.Manage_DeleteProject);
if (jsonMessage.success()) {
//
projectInfoCacheService.syncExecuteNode(nodeModel);
}
return request;
return jsonMessage;
}
/**
@ -193,9 +190,9 @@ public class ProjectManageControl extends BaseServerController {
*/
@RequestMapping(value = "restart", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EXECUTE)
public IJsonMessage<Object> restart() {
public IJsonMessage<Object> restart(HttpServletRequest request) {
NodeModel nodeModel = getNode();
return NodeForward.request(nodeModel, getRequest(), NodeUrl.Manage_Restart);
return NodeForward.request(nodeModel, request, NodeUrl.Manage_Restart);
}
@ -208,9 +205,9 @@ public class ProjectManageControl extends BaseServerController {
*/
@RequestMapping(value = "start", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EXECUTE)
public IJsonMessage<Object> start() {
public IJsonMessage<Object> start(HttpServletRequest request) {
NodeModel nodeModel = getNode();
return NodeForward.request(nodeModel, getRequest(), NodeUrl.Manage_Start);
return NodeForward.request(nodeModel, request, NodeUrl.Manage_Start);
}
@ -223,9 +220,9 @@ public class ProjectManageControl extends BaseServerController {
*/
@RequestMapping(value = "stop", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EXECUTE)
public IJsonMessage<Object> stop() {
public IJsonMessage<Object> stop(HttpServletRequest request) {
NodeModel nodeModel = getNode();
return NodeForward.request(nodeModel, getRequest(), NodeUrl.Manage_Stop);
return NodeForward.request(nodeModel, request, NodeUrl.Manage_Stop);
}
@ -240,12 +237,12 @@ public class ProjectManageControl extends BaseServerController {
//
CsvWriter writer = CsvUtil.getWriter(response.getWriter());
writer.writeLine("id", "name", "groupName", "whitelistDirectory", "path", "logPath", "runMode",
"mainClass",
"jvm", "args",
"javaExtDirsCp",
"dslContent",
"webHooks",
"autoStart");
"mainClass",
"jvm", "args",
"javaExtDirsCp",
"dslContent",
"webHooks",
"autoStart");
writer.flush();
}
@ -264,11 +261,11 @@ public class ProjectManageControl extends BaseServerController {
CsvWriter writer = CsvUtil.getWriter(response.getWriter(), csvWriteConfig);
int pageInt = 0;
writer.writeLine("id", "name", "groupName", "whitelistDirectory", "path", "logPath", "runMode",
"mainClass",
"jvm", "args", "javaExtDirsCp",
"dslContent",
"webHooks",
"autoStart", "outGivingProject");
"mainClass",
"jvm", "args", "javaExtDirsCp",
"dslContent",
"webHooks",
"autoStart", "outGivingProject");
while (true) {
Map<String, String> paramMap = ServletUtil.getParamMap(request);
// 下一页
@ -278,26 +275,26 @@ public class ProjectManageControl extends BaseServerController {
break;
}
listPage.getResult()
.stream()
.map((Function<ProjectInfoCacheModel, List<Object>>) projectInfoCacheModel -> CollUtil.newArrayList(
projectInfoCacheModel.getProjectId(),
projectInfoCacheModel.getName(),
projectInfoCacheModel.getGroup(),
projectInfoCacheModel.getWhitelistDirectory(),
projectInfoCacheModel.getLib(),
projectInfoCacheModel.getLogPath(),
projectInfoCacheModel.getRunMode(),
projectInfoCacheModel.getMainClass(),
encodeCsv(projectInfoCacheModel.getJvm()),
encodeCsv(projectInfoCacheModel.getArgs()),
encodeCsv(projectInfoCacheModel.getJavaExtDirsCp()),
encodeCsv(projectInfoCacheModel.getDslContent()),
projectInfoCacheModel.getToken(),
projectInfoCacheModel.getAutoStart(),
projectInfoCacheModel.getOutGivingProject()
))
.map(objects -> objects.stream().map(StrUtil::toStringOrNull).toArray(String[]::new))
.forEach(writer::writeLine);
.stream()
.map((Function<ProjectInfoCacheModel, List<Object>>) projectInfoCacheModel -> CollUtil.newArrayList(
projectInfoCacheModel.getProjectId(),
projectInfoCacheModel.getName(),
projectInfoCacheModel.getGroup(),
projectInfoCacheModel.getWhitelistDirectory(),
projectInfoCacheModel.getLib(),
projectInfoCacheModel.getLogPath(),
projectInfoCacheModel.getRunMode(),
projectInfoCacheModel.getMainClass(),
encodeCsv(projectInfoCacheModel.getJvm()),
encodeCsv(projectInfoCacheModel.getArgs()),
encodeCsv(projectInfoCacheModel.getJavaExtDirsCp()),
encodeCsv(projectInfoCacheModel.getDslContent()),
projectInfoCacheModel.getToken(),
projectInfoCacheModel.getAutoStart(),
projectInfoCacheModel.getOutGivingProject()
))
.map(objects -> objects.stream().map(StrUtil::toStringOrNull).toArray(String[]::new))
.forEach(writer::writeLine);
if (ObjectUtil.equal(listPage.getPage(), listPage.getTotalPage())) {
// 最后一页
break;

View File

@ -1,24 +1,35 @@
<template>
<div :style="`margin-top:${this.marginTop}`">
<div class="log-filter">
<a-modal
destroyOnClose
:width="getFullscreenViewLogStyle.width"
:dialogStyle="getFullscreenViewLogStyle.dialogStyle"
:bodyStyle="getFullscreenViewLogStyle.bodyStyle"
v-model="visibleModel"
:footer="null"
:maskClosable="false"
@cancel="close"
>
<template #title>
<template>
<a-row type="flex" align="middle">
<a-col>
<slot name="before"></slot>
</a-col>
<a-page-header :title="titleName" :backIcon="false">
<template #subTitle>
<a-row type="flex" align="middle">
<a-col>
<slot name="before"></slot>
</a-col>
<a-col v-if="this.extendBar" style="padding-left: 10px">
<a-space>
<a-tooltip title="清空当前缓冲区内容">
<a-button type="primary" size="small" @click="clearLogCache" icon="delete">清空</a-button>
</a-tooltip>
<!-- <a-tooltip title="内容超过边界自动换行">
<a-col v-if="extendBar" style="padding-left: 10px">
<a-space>
<a-tooltip title="清空当前缓冲区内容">
<a-button type="primary" size="small" @click="clearLogCache" icon="delete">清空</a-button>
</a-tooltip>
<!-- <a-tooltip title="内容超过边界自动换行">
<a-switch v-model="temp.wordBreak" checked-children="自动换行" un-checked-children="不换行" @change="onChange" />
</a-tooltip> -->
<a-tooltip title="有新内容后是否自动滚动到底部">
<a-switch v-model="temp.logScroll" checked-children="自动滚动" un-checked-children="不滚动" @change="onChange" />
</a-tooltip>
<!-- <a-dropdown>
<a-tooltip title="有新内容后是否自动滚动到底部">
<a-switch v-model="temp.logScroll" checked-children="自动滚动" un-checked-children="不滚动" @change="onChange" />
</a-tooltip>
<!-- <a-dropdown>
<a-button type="link" style="padding: 0" icon="setting"> 设置 <a-icon type="down" /></a-button>
<a-menu slot="overlay">
<a-menu-item key="0"> </a-menu-item>
@ -26,19 +37,21 @@
<a-menu-item key="3"> </a-menu-item>
</a-menu>
</a-dropdown> -->
</a-space>
</a-col>
</a-row>
</a-space>
</a-col>
</a-row>
</template>
</a-page-header>
</template>
</div>
<!-- <pre class="log-view" :id="`${this.id}`" :style="`height:${this.height}`">{{ defText }}</pre> -->
<viewPre ref="viewPre" :height="this.height" :config="this.temp"></viewPre>
</div>
</template>
<viewPre ref="viewPre" :height="`calc(${getFullscreenViewLogStyle.bodyStyle.height} - 50px)`" :config="this.temp"></viewPre>
</a-modal>
</template>
<script>
import viewPre from "./view-pre";
import { mapGetters } from "vuex";
export default {
name: "LogView",
components: {
@ -49,14 +62,15 @@ export default {
// },
},
computed: {
...mapGetters(["getFullscreenViewLogStyle"]),
regModifier() {
return this.regModifiers.join("");
},
},
props: {
height: {
titleName: {
String,
default: "50vh",
default: "",
},
marginTop: {
String,
@ -66,6 +80,10 @@ export default {
Boolean,
default: true,
},
visible: {
type: Boolean,
default: false,
},
},
data() {
return {
@ -74,12 +92,16 @@ export default {
//
wordBreak: false,
},
visibleModel: false,
};
},
created() {
this.visibleModel = this.visible;
},
mounted() {
const cacehJson = localStorage.getItem("log-view-cache") || "{}";
const cacheJson = localStorage.getItem("log-view-cache") || "{}";
try {
const cacheData = JSON.parse(cacehJson);
const cacheData = JSON.parse(cacheJson);
this.temp = Object.assign({}, this.temp, cacheData);
} catch (e) {
console.error(e);
@ -87,14 +109,18 @@ export default {
},
methods: {
appendLine(data) {
this.$refs.viewPre.appendLine(data);
this.$refs.viewPre?.appendLine(data);
},
clearLogCache() {
this.$refs.viewPre.clearLogCache();
this.$refs.viewPre?.clearLogCache();
},
onChange() {
localStorage.setItem("log-view-cache", JSON.stringify(this.temp));
},
close() {
this.visibleModel = false;
this.$emit("close");
},
},
};
</script>
@ -113,4 +139,8 @@ export default {
display: flex;
align-items: center;
}
/deep/ .ant-page-header {
padding: 0;
}
</style>

View File

@ -0,0 +1,116 @@
<template>
<div :style="`margin-top:${this.marginTop}`">
<div class="log-filter">
<template>
<a-row type="flex" align="middle">
<a-col>
<slot name="before"></slot>
</a-col>
<a-col v-if="this.extendBar" style="padding-left: 10px">
<a-space>
<a-tooltip title="清空当前缓冲区内容">
<a-button type="primary" size="small" @click="clearLogCache" icon="delete">清空</a-button>
</a-tooltip>
<!-- <a-tooltip title="内容超过边界自动换行">
<a-switch v-model="temp.wordBreak" checked-children="自动换行" un-checked-children="不换行" @change="onChange" />
</a-tooltip> -->
<a-tooltip title="有新内容后是否自动滚动到底部">
<a-switch v-model="temp.logScroll" checked-children="自动滚动" un-checked-children="不滚动" @change="onChange" />
</a-tooltip>
<!-- <a-dropdown>
<a-button type="link" style="padding: 0" icon="setting"> 设置 <a-icon type="down" /></a-button>
<a-menu slot="overlay">
<a-menu-item key="0"> </a-menu-item>
<a-menu-divider />
<a-menu-item key="3"> </a-menu-item>
</a-menu>
</a-dropdown> -->
</a-space>
</a-col>
</a-row>
</template>
</div>
<!-- <pre class="log-view" :id="`${this.id}`" :style="`height:${this.height}`">{{ defText }}</pre> -->
<viewPre ref="viewPre" :height="this.height" :config="this.temp"></viewPre>
</div>
</template>
<script>
import viewPre from "./view-pre";
export default {
name: "LogView",
components: {
viewPre,
// VNodes: {
// functional: true,
// render: (h, ctx) => ctx.props.vnodes,
// },
},
computed: {
regModifier() {
return this.regModifiers.join("");
},
},
props: {
height: {
String,
default: "50vh",
},
marginTop: {
String,
default: "0",
},
extendBar: {
Boolean,
default: true,
},
},
data() {
return {
temp: {
logScroll: true,
//
wordBreak: false,
},
};
},
mounted() {
const cacehJson = localStorage.getItem("log-view-cache") || "{}";
try {
const cacheData = JSON.parse(cacehJson);
this.temp = Object.assign({}, this.temp, cacheData);
} catch (e) {
console.error(e);
}
},
methods: {
appendLine(data) {
this.$refs.viewPre.appendLine(data);
},
clearLogCache() {
this.$refs.viewPre.clearLogCache();
},
onChange() {
localStorage.setItem("log-view-cache", JSON.stringify(this.temp));
},
},
};
</script>
<style scoped>
.log-filter {
/* margin-top: -22px; */
/* margin-bottom: 10px; */
padding: 0 10px;
padding-top: 0;
padding-bottom: 10px;
line-height: 0;
}
/deep/ .ant-checkbox-group-item {
display: flex;
align-items: center;
}
</style>

View File

@ -217,6 +217,9 @@ export default {
id: item.id,
};
});
if (!tempArray.length) {
return;
}
this.dataArray = [...this.dataArray, ...tempArray];
// console.log(this.dataArray.length, this.showList.length);
if (this.config.logScroll) {

View File

@ -123,9 +123,17 @@
</a-row>
<!-- 构建日志 -->
<a-modal destroyOnClose :width="'80vw'" v-model="buildLogVisible" title="构建日志" :footer="null" :maskClosable="false" @cancel="closeBuildLogModel">
<build-log v-if="buildLogVisible" :temp="temp" />
</a-modal>
<build-log
v-if="buildLogVisible > 0"
:temp="temp"
:visible="buildLogVisible != 0"
:key="buildLogVisible"
@close="
() => {
buildLogVisible = 0;
}
"
/>
</div>
</template>
@ -134,6 +142,7 @@ import { getBuildGet, releaseMethodMap, statusMap, geteBuildHistory, statusColor
import { parseTime, PAGE_DEFAULT_LIST_QUERY, PAGE_DEFAULT_SIZW_OPTIONS, PAGE_DEFAULT_SHOW_TOTAL, renderSize, formatDuration } from "@/utils/const";
import { getRepositoryInfo } from "@/api/repository";
import BuildLog from "./log";
export default {
props: {
id: {
@ -154,9 +163,10 @@ export default {
listQuery: Object.assign({ buildDataId: this.id }, PAGE_DEFAULT_LIST_QUERY),
historyList: [],
tempRepository: null,
buildLogVisible: false,
buildLogVisible: 0,
};
},
computed: {},
created() {
this.refresh();
},
@ -215,7 +225,7 @@ export default {
id: record.buildDataId,
buildId: record.buildNumberId,
};
this.buildLogVisible = true;
this.buildLogVisible = new Date() * Math.random();
},
//
handleRollback(record) {
@ -237,14 +247,12 @@ export default {
id: record.buildDataId,
buildId: res.data,
};
this.buildLogVisible = true;
this.buildLogVisible = new Date() * Math.random();
}
});
},
});
},
//
closeBuildLogModel() {},
},
};
</script>

View File

@ -90,9 +90,17 @@
</template>
</a-table>
<!-- 构建日志 -->
<a-modal destroyOnClose :width="'80vw'" v-model="buildLogVisible" title="构建日志" :footer="null" :maskClosable="false" @cancel="closeBuildLogModel">
<build-log v-if="buildLogVisible" :temp="temp" />
</a-modal>
<build-log
v-if="buildLogVisible > 0"
:temp="temp"
:visible="buildLogVisible != 0"
:key="buildLogVisible"
@close="
() => {
buildLogVisible = 0;
}
"
/>
</div>
</template>
<script>
@ -116,7 +124,7 @@ export default {
statusMap,
statusColor,
temp: {},
buildLogVisible: false,
buildLogVisible: 0,
tableSelections: [],
columns: [
{ title: "构建名称", dataIndex: "buildName", width: 120, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
@ -231,7 +239,7 @@ export default {
id: record.buildDataId,
buildId: res.data,
};
this.buildLogVisible = true;
this.buildLogVisible = new Date() * Math.random();
}
});
},
@ -290,7 +298,7 @@ export default {
id: record.buildDataId,
buildId: record.buildNumberId,
};
this.buildLogVisible = true;
this.buildLogVisible = new Date() * Math.random();
},
//
closeBuildLogModel() {

View File

@ -385,9 +385,17 @@
</a-drawer>
<!-- 构建日志 -->
<a-modal destroyOnClose width="80vw" v-model="buildLogVisible" title="构建日志" :footer="null" :maskClosable="false" @cancel="closeBuildLogModel">
<build-log v-if="buildLogVisible" :temp="temp" />
</a-modal>
<build-log
v-if="buildLogVisible > 0"
:temp="temp"
:visible="buildLogVisible != 0"
:key="buildLogVisible"
@close="
() => {
buildLogVisible = 0;
}
"
/>
<!-- 构建确认 -->
<a-modal destroyOnClose width="40vw" v-model="buildConfirmVisible" title="构建确认弹窗" @ok="handleStartBuild" :maskClosable="false">
<a-form-model :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
@ -515,7 +523,7 @@ export default {
//
editBuildVisible: 0,
buildLogVisible: false,
buildLogVisible: 0,
buildConfirmVisible: false,
columns: [
{ title: "名称", dataIndex: "name", sorter: true, width: 200, ellipsis: true, scopedSlots: { customRender: "name" } },
@ -834,7 +842,7 @@ export default {
id: record.id,
buildId: record.buildId,
};
this.buildLogVisible = true;
this.buildLogVisible = new Date() * Math.random();
},
//
closeBuildLogModel() {

View File

@ -1,15 +1,27 @@
<template>
<div>
<log-view :ref="`logView`" height="70vh" marginTop="-10px">
<template #before>
<a-tag>{{ temp && temp.buildId }}</a-tag>
</template>
</log-view>
</div>
<log-view :ref="`logView`" titleName="构建日志" :visible="visible">
<template #before>
<a-space>
<span v-if="status">
当前状态
<a-tooltip :title="`当前状态:${statusMap[status]} ${statusMsg ? '状态消息:' + statusMsg : ''} `">
<a-tag :color="statusColor[status]" style="margin-right: 0"> {{ statusMap[status] || "未知状态" }}</a-tag>
</a-tooltip>
</span>
<span>
构建ID
<a-tag>{{ temp && temp.buildId }}</a-tag>
</span>
<a-button type="primary" icon="download" :disabled="!logId" size="small" @click="handleDownload"> 下载 </a-button>
|
</a-space>
</template>
</log-view>
</template>
<script>
import LogView from "@/components/logView";
import { loadBuildLog } from "@/api/build-info";
import { loadBuildLog, downloadBuildLog, statusColor, statusMap } from "@/api/build-info";
export default {
components: {
LogView,
@ -18,16 +30,28 @@ export default {
temp: {
type: Object,
},
visible: {
type: Boolean,
default: false,
},
},
data() {
return {
statusMap,
statusColor,
logTimer: null,
// logText: "loading...",
line: 1,
logId: "",
status: null,
statusMsg: "",
};
},
beforeDestroy() {
this.logTimer && clearTimeout(this.logTimer);
},
created() {
},
mounted() {
this.pullLog();
@ -59,6 +83,9 @@ export default {
}
this.$refs.logView.appendLine(res.data.dataLines);
this.line = res.data.line;
this.logId = res.data.logId;
this.status = res.data.status;
this.statusMsg = res.data.statusMsg;
} else if (res.code !== 201) {
// 201
this.$notification.warn({
@ -72,6 +99,10 @@ export default {
if (next) this.nextPull();
});
},
//
handleDownload() {
window.open(downloadBuildLog(this.logId), "_blank");
},
},
};
</script>

View File

@ -411,9 +411,22 @@
</a-table>
</template>
<!-- 日志 -->
<a-modal destroyOnClose :width="'80vw'" v-model="logVisible" title="执行日志" :footer="null" :maskClosable="false">
<log-view v-if="logVisible" :id="this.id" :urlPrefix="this.urlPrefix" :machineDockerId="this.machineDockerId" :containerId="temp.id" />
</a-modal>
<log-view
v-if="logVisible > 0"
:visible="logVisible != 0"
:key="logVisible"
@close="
() => {
logVisible = 0;
}
"
:id="this.id"
:urlPrefix="this.urlPrefix"
:machineDockerId="this.machineDockerId"
:containerId="temp.id"
/>
<!-- Terminal -->
<a-modal
v-model="terminalVisible"
@ -528,7 +541,7 @@ export default {
showAll: true,
},
terminalVisible: false,
logVisible: false,
logVisible: 0,
temp: {},
columns: [
@ -664,7 +677,7 @@ export default {
});
},
viewLog(record) {
this.logVisible = true;
this.logVisible = new Date() * Math.random();
this.temp = record;
},
//

View File

@ -99,9 +99,20 @@
/>
</a-drawer>
<!-- 日志 -->
<a-modal destroyOnClose :width="'80vw'" v-model="logVisible" title="pull日志" :footer="null" :maskClosable="false">
<pull-image-Log v-if="logVisible" :id="temp.id" :machineDockerId="this.machineDockerId" :urlPrefix="this.urlPrefix" />
</a-modal>
<pull-image-Log
v-if="logVisible > 0"
:id="temp.id"
:visible="logVisible != 0"
:key="logVisible"
@close="
() => {
logVisible = 0;
}
"
:machineDockerId="this.machineDockerId"
:urlPrefix="this.urlPrefix"
/>
</div>
</template>
<script>
@ -135,7 +146,7 @@ export default {
listQuery: {
showAll: false,
},
logVisible: false,
logVisible: 0,
pullImageName: "",
renderSize,
temp: {},
@ -325,7 +336,7 @@ export default {
repository: this.pullImageName,
}).then((res) => {
if (res.code === 200) {
this.logVisible = true;
this.logVisible = new Date() * Math.random();
this.temp = {
id: res.data,
};

View File

@ -1,7 +1,7 @@
<template>
<!-- console -->
<div>
<log-view ref="logView" seg="" height="60vh" marginTop="-10px">
<log-view ref="logView" titleName="容器日志" :visible="visible">
<template slot="before">
<a-space>
<a-tooltip title="为避免显示内容太多而造成浏览器卡顿,读取日志最后多少行日志。修改后需要回车才能重新读取,小于 1 则读取所有">
@ -47,6 +47,10 @@ export default {
urlPrefix: {
type: String,
},
visible: {
type: Boolean,
default: false,
},
},
data() {
return {

View File

@ -1,6 +1,6 @@
<template>
<div>
<log-view :ref="`logView`" height="70vh" marginTop="-10px" />
<log-view titleName="pull日志" :visible="visible" :ref="`logView`" />
</div>
</template>
<script>
@ -21,6 +21,10 @@ export default {
urlPrefix: {
type: String,
},
visible: {
type: Boolean,
default: false,
},
},
computed: {
reqDataId() {

View File

@ -1,6 +1,6 @@
<template>
<div>
<log-view :ref="`logView`" height="70vh" marginTop="-10px">
<log-view titleName="任务日志" :ref="`logView`" :visible="visible">
<template slot="before">
<a-space>
<a-tooltip title="为避免显示内容太多而造成浏览器卡顿,读取日志最后多少行日志。修改后需要回车才能重新读取,小于 1 则读取所有">
@ -42,6 +42,10 @@ export default {
urlPrefix: {
type: String,
},
visible: {
type: Boolean,
default: false,
},
},
data() {
return {

View File

@ -16,6 +16,7 @@
</a-tooltip>
<a-tooltip slot="id" slot-scope="text, item" placement="topLeft" :title="text" @click="handleLog(item)">
<span>{{ text }}</span>
<a-icon type="eye" />
</a-tooltip>
<a-tooltip slot="status" slot-scope="text, item" placement="topLeft" :title="`节点状态:${text} 节点可用性:${item.spec ? item.spec.availability || '' : ''}`">
@ -392,9 +393,21 @@
<swarm-task v-if="taskVisible" :visible="taskVisible" :taskState="this.temp.state" :id="this.id" :serviceId="this.temp.id" :urlPrefix="this.urlPrefix" />
</a-modal>
<!-- 查看日志 -->
<a-modal destroyOnClose v-model="logVisible" title="查看日志" width="80vw" :footer="null" :maskClosable="false">
<pull-log v-if="logVisible" :id="this.id" :dataId="this.temp.id" type="service" />
</a-modal>
<pull-log
v-if="logVisible > 0"
:key="logVisible"
:visible="logVisible != 0"
@close="
() => {
logVisible = 0;
}
"
:id="this.id"
:dataId="this.temp.id"
type="service"
:urlPrefix="this.urlPrefix"
/>
</div>
</template>
@ -430,7 +443,7 @@ export default {
editVisible: false,
initSwarmVisible: false,
taskVisible: false,
logVisible: false,
logVisible: 0,
rules: {
name: [{ required: true, message: "服务名称必填", trigger: "blur" }],
@ -459,8 +472,9 @@ export default {
countdownTime: Date.now(),
};
},
computed: {},
beforeDestroy() {},
computed: {},
mounted() {
this.loadData();
},
@ -488,7 +502,7 @@ export default {
},
//
handleLog(record) {
this.logVisible = true;
this.logVisible = new Date() * Math.random();
this.temp = record;
},
//

View File

@ -87,9 +87,21 @@
</a-form-model>
</a-modal>
<!-- 查看日志 -->
<a-modal destroyOnClose v-model="logVisible" title="查看日志" width="80vw" :footer="null" :maskClosable="false">
<pull-log v-if="logVisible" :id="this.id" :dataId="this.temp.id" type="taks" :urlPrefix="this.urlPrefix" />
</a-modal>
<pull-log
v-if="logVisible > 0"
:key="logVisible"
:visible="logVisible != 0"
@close="
() => {
logVisible = 0;
}
"
:id="this.id"
:dataId="this.temp.id"
type="taks"
:urlPrefix="this.urlPrefix"
/>
</div>
</template>
@ -126,7 +138,7 @@ export default {
editVisible: false,
initSwarmVisible: false,
autoUpdateTime: null,
logVisible: false,
logVisible: 0,
rules: {
role: [{ required: true, message: "请选择节点角色", trigger: "blur" }],
availability: [{ required: true, message: "请选择节点状态", trigger: "blur" }],
@ -196,7 +208,7 @@ export default {
},
//
handleLog(record) {
this.logVisible = true;
this.logVisible = new Date() * Math.random();
this.temp = record;
},
handleEdit(record) {

View File

@ -327,7 +327,7 @@
<a-alert banner>
<template slot="message"> 下列配置信息仅在当前浏览器生效,清空浏览器缓存配置将恢复默认 </template>
</a-alert>
<a-form-model-item label="页面导航" prop="token">
<a-form-model-item label="页面导航" prop="guideStatus">
<a-space>
<a-switch checked-children="" @click="toggleGuide" :checked="!this.guideStatus" :disabled="this.getDisabledGuide" un-checked-children="" />
@ -337,24 +337,30 @@
</div>
</a-space>
</a-form-model-item>
<a-form-model-item label="菜单配置" prop="token">
<a-form-model-item label="菜单配置" prop="menuMultipleFlag">
<a-space>
同时展开多个
<a-switch checked-children="" @click="toggleMenuMultiple" :checked="this.menuMultipleFlag" un-checked-children="" />
</a-space>
</a-form-model-item>
<a-form-model-item label="页面配置" prop="token">
<a-form-model-item label="页面配置" prop="fullScreenFlag">
<a-space>
自动撑开
<a-switch checked-children="" @click="toggleFullScreenFlag" :checked="this.fullScreenFlag" un-checked-children="" />
</a-space>
</a-form-model-item>
<a-form-model-item label="滚动条显示" prop="token">
<a-form-model-item label="滚动条显示" prop="scrollbarFlag">
<a-space>
全局配置
<a-switch checked-children="显示" @click="toggleScrollbarFlag" :checked="this.scrollbarFlag" un-checked-children="不显示" />
</a-space>
</a-form-model-item>
<a-form-model-item label="全屏日志" prop="fullscreenViewLog">
<a-space>
全屏查看日志
<a-switch checked-children="全屏" @click="toggleFullscreenViewLog" :checked="this.fullscreenViewLog" un-checked-children="非全屏" />
</a-space>
</a-form-model-item>
</a-form-model>
</a-modal>
<!-- mfa 提示 -->
@ -456,6 +462,9 @@ export default {
scrollbarFlag() {
return this.getGuideCache.scrollbarFlag === undefined ? true : this.getGuideCache.scrollbarFlag;
},
fullscreenViewLog() {
return !!this.getGuideCache.fullscreenViewLog;
},
selectCluster: {
get: function () {
const temp = this.myClusterList.find((item) => {
@ -594,6 +603,20 @@ export default {
}
});
},
//
toggleFullscreenViewLog() {
this.$store.dispatch("toggleFullscreenViewLog").then((fullscreenViewLog) => {
if (fullscreenViewLog) {
this.$notification.success({
message: "日志弹窗会全屏打开",
});
} else {
this.$notification.success({
message: "日志弹窗会非全屏打开",
});
}
});
},
restGuide() {
this.$store.dispatch("restGuide").then(() => {
this.$notification.success({

View File

@ -1,6 +1,6 @@
<template>
<div>
<log-view :ref="`logView`" height="70vh" marginTop="-10px" />
<log-view titleName="执行日志" :ref="`logView`" :visible="visible" />
</div>
</template>
<script>
@ -14,6 +14,10 @@ export default {
temp: {
type: Object,
},
visible: {
type: Boolean,
default: false,
},
},
data() {
return {

View File

@ -59,9 +59,18 @@
</template>
</a-table>
<!-- 日志 -->
<a-modal destroyOnClose :width="'80vw'" v-model="logVisible" title="执行日志" :footer="null" :maskClosable="false">
<script-log-view v-if="logVisible" :temp="temp" />
</a-modal>
<script-log-view
v-if="logVisible > 0"
:visible="logVisible != 0"
:key="logVisible"
@close="
() => {
logVisible = 0;
}
"
:temp="temp"
/>
</div>
</template>
<script>
@ -95,7 +104,7 @@ export default {
triggerExecTypeMap: triggerExecTypeMap,
list: [],
temp: {},
logVisible: false,
logVisible: 0,
columns: [
{ title: "名称", dataIndex: "scriptName", ellipsis: true, width: 100, scopedSlots: { customRender: "scriptName" } },
{ title: "执行时间", dataIndex: "createTimeMillis", ellipsis: true, width: "160px", scopedSlots: { customRender: "createTimeMillis" } },
@ -132,7 +141,7 @@ export default {
return parseTime(v);
},
viewLog(record) {
this.logVisible = true;
this.logVisible = new Date() * Math.random();
this.temp = record;
},
handleDelete(record) {

View File

@ -1,6 +1,6 @@
<template>
<div>
<log-view :ref="`logView`" height="70vh" marginTop="-10px" />
<log-view :ref="`logView`" titleName="脚本日志" :visible="visible" />
</div>
</template>
<script>
@ -14,6 +14,10 @@ export default {
temp: {
type: Object,
},
visible: {
type: Boolean,
default: false,
},
},
data() {
return {

View File

@ -58,9 +58,18 @@
</template>
</a-table>
<!-- 日志 -->
<a-modal destroyOnClose :width="'80vw'" v-model="logVisible" title="执行日志" :footer="null" :maskClosable="false">
<script-log-view v-if="logVisible" :temp="temp" />
</a-modal>
<script-log-view
v-if="logVisible > 0"
:visible="logVisible != 0"
:key="logVisible"
@close="
() => {
logVisible = 0;
}
"
:temp="temp"
/>
</div>
</template>
<script>
@ -87,7 +96,7 @@ export default {
statusMap,
list: [],
temp: {},
logVisible: false,
logVisible: 0,
columns: [
{ title: "名称", dataIndex: "scriptName", width: 100, ellipsis: true, scopedSlots: { customRender: "scriptName" } },
{ title: "执行时间", dataIndex: "createTimeMillis", sorter: true, ellipsis: true, width: "160px", scopedSlots: { customRender: "createTimeMillis" } },
@ -124,7 +133,7 @@ export default {
},
parseTime,
viewLog(record) {
this.logVisible = true;
this.logVisible = new Date() * Math.random();
this.temp = record;
},
handleDelete(record) {

View File

@ -42,8 +42,17 @@
</template>
</a-table>
<!-- 构建日志 -->
<a-modal destroyOnClose :width="'80vw'" v-model="logVisible" title="执行日志" :footer="null" :maskClosable="false">
<command-log v-if="logVisible" :temp="temp" />
<a-modal
destroyOnClose
:width="getFullscreenViewLogStyle.width"
:dialogStyle="getFullscreenViewLogStyle.dialogStyle"
:bodyStyle="getFullscreenViewLogStyle.bodyStyle"
v-model="logVisible"
title="执行日志"
:footer="null"
:maskClosable="false"
>
<command-log :height="getFullscreenViewLogStyle.bodyStyle.height" v-if="logVisible" :temp="temp" />
</a-modal>
</div>
</template>
@ -52,7 +61,7 @@
import { deleteCommandLog, downloadLog, getCommandLogList, statusMap, triggerExecTypeMap } from "@/api/command";
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime } from "@/utils/const";
import CommandLog from "./command-view-log";
import { mapGetters } from "vuex";
export default {
components: {
CommandLog,
@ -104,6 +113,7 @@ export default {
};
},
computed: {
...mapGetters(["getFullscreenViewLogStyle"]),
pagination() {
return COMPUTED_PAGINATION(this.listQuery);
},

View File

@ -7,14 +7,14 @@
{{ item.sshName }}
</span>
<!-- <a-input :id="`build-log-textarea-${item.id}`" v-model="logMap[item.id].logText" type="textarea" class="console" readOnly style="resize: none; height: 60vh" /> -->
<log-view :ref="`logView-${item.id}`" height="60vh" />
<log-view :ref="`logView-${item.id}`" :height="`calc(${height} - 130px)`" />
</a-tab-pane>
</a-tabs>
</div>
</template>
<script>
import { getCommandLogBarchList, getCommandLogInfo } from "@/api/command";
import LogView from "@/components/logView";
import LogView from "@/components/logView/index2";
export default {
components: {
LogView,
@ -23,6 +23,10 @@ export default {
temp: {
type: Object,
},
height: {
type: String,
default: "60vh",
},
},
data() {
return {

View File

@ -60,6 +60,15 @@ const app = {
resolve(cache.close);
});
},
// 切换全屏查看日志
toggleFullscreenViewLog({ commit, rootGetters }) {
return new Promise((resolve) => {
const cache = rootGetters.getGuideCache;
cache.fullscreenViewLog = !cache.fullscreenViewLog;
commit("setGuideCache", cache);
resolve(cache.fullscreenViewLog);
});
},
commitGuide({ commit }, value) {
commit("commitGuide", value);
},
@ -100,14 +109,7 @@ const app = {
},
getters: {
getGuideCache(state) {
const cacheStr = state.guideCache || "";
let cahce;
try {
cahce = JSON.parse(cacheStr);
} catch (e) {
cahce = {};
}
return cahce;
return cacheToJson(state);
},
getDisabledGuide(state) {
return state.disabledGuide;
@ -115,7 +117,54 @@ const app = {
getExtendPlugins(state) {
return state.extendPlugins || [];
},
// 计算弹窗全屏样式
getFullscreenViewLogStyle(state) {
const cache = cacheToJson(state);
if (cache.fullscreenViewLog) {
// 全屏
return {
dialogStyle: {
maxWidth: "100vw",
top: 0,
paddingBottom: 0,
},
bodyStyle: {
padding: "0 10px",
paddingTop: "10px",
marginRight: "10px",
height: "calc(100vh - 68px)",
},
width: "100vw",
};
}
// 非全屏
return {
dialogStyle: {
maxWidth: "100vw",
top: false,
paddingBottom: 0,
},
bodyStyle: {
padding: "0 10px",
paddingTop: "10px",
marginRight: "10px",
height: "70vh",
},
width: "80vw",
};
},
},
};
function cacheToJson(state) {
const cacheStr = state.guideCache || "";
let cahce;
try {
cahce = JSON.parse(cacheStr);
} catch (e) {
cahce = {};
}
return cahce;
}
export default app;