💄 优化表格样式.
This commit is contained in:
@@ -1,12 +1,25 @@
|
||||
> 版本号严格遵循 Semver 规范。
|
||||
|
||||
[//]: # (🐞修复)
|
||||
## v1.0.1
|
||||
|
||||
`2024-03-` `release`
|
||||
|
||||
🐞 修复 用户操作日志条件重置后类型框数据不正常的问题
|
||||
🩰 修改 主机连接日志 UI
|
||||
🌈 新增 SFTP 使用日志列表
|
||||
🌈 新增 主机连接日志强制下线会话
|
||||
🌈 新增 主机连接日志删除/清理
|
||||
🌈 新增 用户操作日志日志删除/清理
|
||||
🌈 新增 用户操作日志日志删除/清理
|
||||
🔨 优化 用户锁定次数/时间可配置
|
||||
|
||||
[如何升级](/about/update.md?id=_v101)
|
||||
|
||||
## v1.0.0
|
||||
|
||||
`2024-03-01` `release`
|
||||
|
||||
🌈 用户自定义终端标签颜色
|
||||
🌈 新增 用户自定义终端标签颜色
|
||||
🔨 拓展数据模块添加缓存
|
||||
|
||||
[如何升级](/about/update.md?id=_v100)
|
||||
|
||||
@@ -80,7 +80,6 @@
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
||||
@@ -28,7 +28,7 @@ public enum OutputTypeEnum {
|
||||
/**
|
||||
* 关闭连接
|
||||
*/
|
||||
CLOSE("cl", "${type}|${sessionId}|${msg}"),
|
||||
CLOSE("cl", "${type}|${sessionId}|${forceClose}|${msg}"),
|
||||
|
||||
/**
|
||||
* pong
|
||||
|
||||
@@ -23,6 +23,9 @@ import lombok.experimental.SuperBuilder;
|
||||
@Schema(name = "TerminalCloseResponse", description = "主机连接关闭响应 实体对象")
|
||||
public class TerminalCloseResponse extends TerminalBasePayload {
|
||||
|
||||
@Schema(description = "是否为强制关闭")
|
||||
private Integer forceClose;
|
||||
|
||||
@Schema(description = "关闭信息")
|
||||
private String msg;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.orion.lang.utils.io.Streams;
|
||||
import com.orion.net.host.SessionStore;
|
||||
import com.orion.net.host.ssh.shell.ShellExecutor;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.common.enums.BooleanBit;
|
||||
import com.orion.ops.framework.websocket.core.utils.WebSockets;
|
||||
import com.orion.ops.module.asset.define.AssetThreadPools;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage;
|
||||
@@ -53,7 +54,7 @@ public class SshSession extends TerminalSession implements ISshSession {
|
||||
executor.size(cols, rows);
|
||||
executor.terminalType(terminalType);
|
||||
executor.streamHandler(this::streamHandler);
|
||||
executor.callback(this::eofCallback);
|
||||
executor.callback(this::close);
|
||||
executor.connect();
|
||||
// 开始监听输出
|
||||
AssetThreadPools.TERMINAL_STDOUT.execute(executor);
|
||||
@@ -122,20 +123,4 @@ public class SshSession extends TerminalSession implements ISshSession {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* eof 回调
|
||||
*/
|
||||
private void eofCallback() {
|
||||
log.info("terminal eof回调 {}, forClose: {}, forceOffline: {}", sessionId, this.close, this.forceOffline);
|
||||
// 发送关闭信息
|
||||
TerminalCloseResponse resp = TerminalCloseResponse.builder()
|
||||
.type(OutputTypeEnum.CLOSE.getType())
|
||||
.sessionId(this.sessionId)
|
||||
.msg(this.forceOffline ? TerminalMessage.FORCED_OFFLINE : TerminalMessage.CLOSED_CONNECTION)
|
||||
.build();
|
||||
WebSockets.sendText(channel, OutputTypeEnum.CLOSE.format(resp));
|
||||
// 需要调用关闭 - 可能是 logout 需要手动触发
|
||||
this.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.orion.ops.module.asset.handler.host.terminal.session;
|
||||
|
||||
import com.orion.ops.framework.common.enums.BooleanBit;
|
||||
import com.orion.ops.framework.websocket.core.utils.WebSockets;
|
||||
import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCloseResponse;
|
||||
import com.orion.ops.module.asset.service.HostConnectLogService;
|
||||
import com.orion.spring.SpringHolder;
|
||||
import lombok.Getter;
|
||||
@@ -41,6 +46,21 @@ public abstract class TerminalSession implements ITerminalSession {
|
||||
*/
|
||||
protected abstract void releaseResource();
|
||||
|
||||
/**
|
||||
* 发送关闭消息
|
||||
*/
|
||||
protected void sendCloseMessage() {
|
||||
log.info("TerminalSession close {}, forClose: {}, forceOffline: {}", sessionId, this.close, this.forceOffline);
|
||||
// 发送关闭信息
|
||||
TerminalCloseResponse resp = TerminalCloseResponse.builder()
|
||||
.type(OutputTypeEnum.CLOSE.getType())
|
||||
.sessionId(this.sessionId)
|
||||
.forceClose(BooleanBit.of(this.forceOffline).getValue())
|
||||
.msg(this.forceOffline ? TerminalMessage.FORCED_OFFLINE : TerminalMessage.CLOSED_CONNECTION)
|
||||
.build();
|
||||
WebSockets.sendText(channel, OutputTypeEnum.CLOSE.format(resp));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
log.info("terminal close {}", sessionId);
|
||||
@@ -56,6 +76,7 @@ public abstract class TerminalSession implements ITerminalSession {
|
||||
public void forceOffline() {
|
||||
log.info("terminal forceOffline {}", sessionId);
|
||||
this.forceOffline = true;
|
||||
// 关闭
|
||||
this.checkAndClose();
|
||||
}
|
||||
|
||||
@@ -75,6 +96,12 @@ public abstract class TerminalSession implements ITerminalSession {
|
||||
} catch (Exception e) {
|
||||
log.error("terminal release error {}", sessionId, e);
|
||||
}
|
||||
// 发送关闭信息
|
||||
try {
|
||||
this.sendCloseMessage();
|
||||
} catch (Exception e) {
|
||||
log.error("terminal send close error {}", sessionId, e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,12 +85,25 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
|
||||
HostConnectLogDO record = hostConnectLogDAO.of()
|
||||
.createWrapper()
|
||||
.eq(HostConnectLogDO::getToken, token)
|
||||
.orderByDesc(HostConnectLogDO::getId)
|
||||
.then()
|
||||
.getOne();
|
||||
if (record == null) {
|
||||
log.info("HostConnectLogService-updateStatusByToken no record token: {}", token);
|
||||
return Const.N_0;
|
||||
}
|
||||
return this.updateStatus(record, status, partial);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新状态
|
||||
*
|
||||
* @param record record
|
||||
* @param status status
|
||||
* @param partial partial
|
||||
* @return effect
|
||||
*/
|
||||
private int updateStatus(HostConnectLogDO record, HostConnectStatusEnum status, Map<String, Object> partial) {
|
||||
// 更新
|
||||
HostConnectLogDO update = new HostConnectLogDO();
|
||||
update.setId(record.getId());
|
||||
@@ -151,20 +164,20 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
|
||||
public Integer forceOffline(HostConnectLogQueryRequest request) {
|
||||
Long id = request.getId();
|
||||
// 查询数据是否存在
|
||||
HostConnectLogDO connect = hostConnectLogDAO.selectById(id);
|
||||
Valid.notNull(connect, ErrorMessage.LOG_ABSENT);
|
||||
Valid.eq(connect.getStatus(), HostConnectStatusEnum.CONNECTING.name(), ErrorMessage.ILLEGAL_STATUS);
|
||||
HostConnectLogDO record = hostConnectLogDAO.selectById(id);
|
||||
Valid.notNull(record, ErrorMessage.LOG_ABSENT);
|
||||
Valid.eq(record.getStatus(), HostConnectStatusEnum.CONNECTING.name(), ErrorMessage.ILLEGAL_STATUS);
|
||||
// 设置日志参数
|
||||
OperatorLogs.add(OperatorLogs.HOST_NAME, connect.getHostName());
|
||||
OperatorLogs.add(OperatorLogs.HOST_NAME, record.getHostName());
|
||||
// 获取会话
|
||||
HostConnectLogExtraDTO extra = JSON.parseObject(connect.getExtraInfo(), HostConnectLogExtraDTO.class);
|
||||
HostConnectLogExtraDTO extra = JSON.parseObject(record.getExtraInfo(), HostConnectLogExtraDTO.class);
|
||||
ITerminalSession session = terminalManager.getSession(extra.getChannelId(), extra.getSessionId());
|
||||
if (session != null) {
|
||||
// 关闭会话
|
||||
session.forceOffline();
|
||||
}
|
||||
// 更新状态
|
||||
return this.updateStatusByToken(connect.getToken(), HostConnectStatusEnum.FORCE_OFFLINE, null);
|
||||
return this.updateStatus(record, HostConnectStatusEnum.FORCE_OFFLINE, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryReques
|
||||
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
|
||||
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
@@ -25,7 +24,6 @@ public interface OperatorLogConvert {
|
||||
|
||||
OperatorLogDO to(OperatorLogQueryRequest request);
|
||||
|
||||
@Mapping(target = "extra", ignore = true)
|
||||
OperatorLogVO to(OperatorLogDO domain);
|
||||
|
||||
LoginHistoryVO toLoginHistory(OperatorLogDO domain);
|
||||
|
||||
@@ -8,7 +8,6 @@ import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作日志 视图响应对象
|
||||
@@ -60,7 +59,7 @@ public class OperatorLogVO implements Serializable {
|
||||
private String logInfo;
|
||||
|
||||
@Schema(description = "参数")
|
||||
private Map<String, Object> extra;
|
||||
private String extra;
|
||||
|
||||
@Schema(description = "操作结果 0失败 1成功")
|
||||
private Integer result;
|
||||
|
||||
@@ -50,11 +50,7 @@ public class OperatorLogServiceImpl implements OperatorLogService {
|
||||
// 查询
|
||||
return operatorLogDAO.of(wrapper)
|
||||
.page(request)
|
||||
.dataGrid(s -> {
|
||||
OperatorLogVO vo = OperatorLogConvert.MAPPER.to(s);
|
||||
vo.setExtra(JSON.parseObject(s.getExtra()));
|
||||
return vo;
|
||||
});
|
||||
.dataGrid(OperatorLogConvert.MAPPER::to);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
||||
@@ -72,7 +72,6 @@
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
||||
@@ -91,7 +91,6 @@
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
|
||||
@@ -44,8 +44,12 @@
|
||||
</a-breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 已关闭-右侧操作 -->
|
||||
<div v-if="isClose" class="sftp-table-header-right">
|
||||
<span class="close-message">{{ closeMessage }}</span>
|
||||
</div>
|
||||
<!-- 路径编辑模式-右侧操作 -->
|
||||
<a-space v-if="pathEditable" class="sftp-table-header-right">
|
||||
<a-space v-else-if="pathEditable" class="sftp-table-header-right">
|
||||
<!-- 进入 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
@@ -185,9 +189,11 @@
|
||||
import { openSftpCreateModalKey, openSftpUploadModalKey } from '../../types/terminal.const';
|
||||
|
||||
const props = defineProps<{
|
||||
isClose: boolean;
|
||||
closeMessage: string | undefined;
|
||||
currentPath: string;
|
||||
session: ISftpSession | undefined,
|
||||
selectedFiles: Array<string>
|
||||
session: ISftpSession | undefined;
|
||||
selectedFiles: Array<string>;
|
||||
}>();
|
||||
|
||||
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'download']);
|
||||
@@ -322,6 +328,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.close-message {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
padding-left: 16px;
|
||||
color: rgb(var(--red-6));
|
||||
}
|
||||
|
||||
.header-action-icon {
|
||||
font-size: 16px;
|
||||
padding: 4px;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
<!-- 表头 -->
|
||||
<sftp-table-header class="sftp-table-header"
|
||||
v-model:selected-files="selectFiles"
|
||||
:is-close="closed"
|
||||
:close-message="closeMessage"
|
||||
:current-path="currentPath"
|
||||
:session="session"
|
||||
@load-file="loadFiles"
|
||||
@@ -89,6 +91,8 @@
|
||||
const fileList = ref<Array<SftpFile>>([]);
|
||||
const selectFiles = ref<Array<string>>([]);
|
||||
const splitSize = ref(1);
|
||||
const closed = ref(false);
|
||||
const closeMessage = ref('');
|
||||
const editorView = ref(false);
|
||||
const editorRef = ref();
|
||||
const editorFileName = ref('');
|
||||
@@ -184,6 +188,14 @@
|
||||
return success;
|
||||
};
|
||||
|
||||
// 关闭回调
|
||||
const onClose = (forceClose: string, msg: string) => {
|
||||
console.log(forceClose);
|
||||
console.log(msg);
|
||||
closed.value = true;
|
||||
closeMessage.value = msg;
|
||||
};
|
||||
|
||||
// 接收列表回调
|
||||
const resolveList = (result: string, path: string, list: Array<SftpFile>) => {
|
||||
setTableLoading(false);
|
||||
@@ -240,6 +252,7 @@
|
||||
session.value = await sessionManager.openSftp(props.tab, {
|
||||
setLoading: setTableLoading,
|
||||
connectCallback,
|
||||
onClose,
|
||||
resolveList,
|
||||
resolveSftpMkdir: resolveFileAction,
|
||||
resolveSftpTouch: resolveFileAction,
|
||||
|
||||
@@ -81,9 +81,9 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
||||
}
|
||||
|
||||
// 处理关闭消息
|
||||
processClose({ sessionId, msg }: OutputPayload): void {
|
||||
processClose({ sessionId, msg, forceClose }: OutputPayload): void {
|
||||
const session = this.sessionManager.getSession(sessionId);
|
||||
// 无需处理 (直接关闭 tab )
|
||||
// 无需处理 (直接关闭 tab)
|
||||
if (!session) {
|
||||
return;
|
||||
}
|
||||
@@ -98,6 +98,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
||||
} else if (session instanceof SftpSession) {
|
||||
// sftp 设置状态
|
||||
session.connected = false;
|
||||
session.resolver?.onClose(forceClose, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ export const OutputProtocol = {
|
||||
// 主机连接关闭
|
||||
CLOSE: {
|
||||
type: 'cl',
|
||||
template: ['type', 'sessionId', 'msg'],
|
||||
template: ['type', 'sessionId', 'forceClose', 'msg'],
|
||||
processMethod: 'processClose'
|
||||
},
|
||||
// pong
|
||||
|
||||
@@ -347,6 +347,8 @@ export interface ISftpSessionResolver {
|
||||
setLoading: (loading: boolean) => void;
|
||||
// 连接后回调
|
||||
connectCallback: () => void;
|
||||
// 关闭回调
|
||||
onClose: (forceClose: string, msg: string) => void;
|
||||
// 接受文件列表响应
|
||||
resolveList: (result: string, path: string, list: Array<SftpFile>) => void;
|
||||
// 接收创建文件夹响应
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
:allow-search="true"
|
||||
:filter-option="labelFilter"
|
||||
placeholder="请选择操作模块"
|
||||
@change="m => selectedModule(m as string)"
|
||||
allow-clear />
|
||||
</a-form-item>
|
||||
<!-- 操作类型 -->
|
||||
@@ -81,7 +80,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
|
||||
import type { OperatorLogQueryRequest } from '@/api/user/operator-log';
|
||||
import { ref } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import { getOperatorLogCount, clearOperatorLog } from '@/api/user/operator-log';
|
||||
@@ -123,8 +122,8 @@
|
||||
|
||||
defineExpose({ open });
|
||||
|
||||
// 选择类型
|
||||
const selectedModule = (module: string) => {
|
||||
// 监听类型变化
|
||||
watch(() => formModel.value.module, (module: string | undefined) => {
|
||||
if (!module) {
|
||||
// 不选择则重置 options
|
||||
typeOptions.value = toOptions(operatorLogTypeKey);
|
||||
@@ -138,7 +137,7 @@
|
||||
if (formModel.value.type && !formModel.value.type.startsWith(modulePrefix)) {
|
||||
formModel.value.type = undefined;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// 确定
|
||||
const handlerOk = async () => {
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
:allow-search="true"
|
||||
:filter-option="labelFilter"
|
||||
placeholder="请选择操作模块"
|
||||
@change="m => selectedModule(m as string)"
|
||||
allow-clear />
|
||||
</a-form-item>
|
||||
<!-- 操作类型 -->
|
||||
@@ -67,12 +66,12 @@
|
||||
<script lang="ts" setup>
|
||||
import type { OperatorLogQueryRequest } from '@/api/user/operator-log';
|
||||
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { useDictStore } from '@/store';
|
||||
import UserSelector from '@/components/user/user/user-selector.vue';
|
||||
import { operatorLogModuleKey, operatorLogTypeKey, operatorRiskLevelKey, operatorLogResultKey } from '../types/const';
|
||||
import { labelFilter } from '@/types/form';
|
||||
import UserSelector from '@/components/user/user/user-selector.vue';
|
||||
|
||||
const emits = defineEmits(['submit']);
|
||||
const props = defineProps({
|
||||
@@ -94,8 +93,8 @@
|
||||
startTimeRange: undefined,
|
||||
});
|
||||
|
||||
// 选择类型
|
||||
const selectedModule = (module: string) => {
|
||||
// 监听类型变化
|
||||
watch(() => formModel.module, (module: string | undefined) => {
|
||||
if (!module) {
|
||||
// 不选择则重置 options
|
||||
typeOptions.value = toOptions(operatorLogTypeKey);
|
||||
@@ -109,7 +108,7 @@
|
||||
if (formModel.type && !formModel.type.startsWith(modulePrefix)) {
|
||||
formModel.type = undefined;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// 切换页码
|
||||
const submit = () => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<!-- 表格 -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:columns="tableColumns"
|
||||
@@ -33,6 +32,19 @@
|
||||
<icon-copy class="copy-left" @click="copy(record.originLogInfo, '已复制')" />
|
||||
<span v-html="replaceHtmlTag(record.logInfo)" />
|
||||
</template>
|
||||
<!-- 操作地址 -->
|
||||
<template #address="{ record }">
|
||||
<span class="operator-location" :title="record.location">
|
||||
{{ record.location }}
|
||||
</span>
|
||||
<br>
|
||||
<span class="copy-left" title="复制" @click="copy(record.address)">
|
||||
<icon-copy />
|
||||
</span>
|
||||
<span class="operator-address" :title="record.address">
|
||||
{{ record.address }}
|
||||
</span>
|
||||
</template>
|
||||
<!-- 操作 -->
|
||||
<template #handle="{ record }">
|
||||
<div class="table-handle-wrapper">
|
||||
@@ -144,7 +156,7 @@
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
let cols = [...columns].map(s => {
|
||||
let cols = columns.map(s => {
|
||||
return { ...s };
|
||||
}).filter(s => s.dataIndex !== 'username');
|
||||
if (props.handleColumn) {
|
||||
@@ -164,4 +176,13 @@
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.operator-location {
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
|
||||
.operator-address {
|
||||
margin-top: 4px;
|
||||
display: inline-block;
|
||||
color: var(--color-text-3);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
</template>
|
||||
<!-- 表格 -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
@@ -80,6 +79,19 @@
|
||||
<icon-copy class="copy-left" @click="copy(record.originLogInfo, '已复制')" />
|
||||
<span v-html="replaceHtmlTag(record.logInfo)" />
|
||||
</template>
|
||||
<!-- 操作地址 -->
|
||||
<template #address="{ record }">
|
||||
<span class="operator-location" :title="record.location">
|
||||
{{ record.location }}
|
||||
</span>
|
||||
<br>
|
||||
<span class="copy-left" title="复制" @click="copy(record.address)">
|
||||
<icon-copy />
|
||||
</span>
|
||||
<span class="operator-address" :title="record.address">
|
||||
{{ record.address }}
|
||||
</span>
|
||||
</template>
|
||||
<!-- 操作 -->
|
||||
<template #handle="{ record }">
|
||||
<div class="table-handle-wrapper">
|
||||
@@ -220,5 +232,13 @@
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.operator-location {
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
|
||||
.operator-address {
|
||||
margin-top: 4px;
|
||||
display: inline-block;
|
||||
color: var(--color-text-3);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -19,7 +19,7 @@ export const getLogDetail = (record: OperatorLogQueryResponse): Record<string, a
|
||||
detail.duration = `${record.duration} ms`;
|
||||
detail.startTime = dateFormat(new Date(record.startTime));
|
||||
detail.endTime = dateFormat(new Date(record.endTime));
|
||||
detail.extra = record?.extra;
|
||||
detail.extra = JSON.parse(record?.extra);
|
||||
detail.returnValue = JSON.parse(record?.returnValue);
|
||||
return detail;
|
||||
} catch (e) {
|
||||
|
||||
@@ -32,6 +32,13 @@ const columns = [
|
||||
slotName: 'originLogInfo',
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
}, {
|
||||
title: '操作地址',
|
||||
dataIndex: 'address',
|
||||
slotName: 'address',
|
||||
width: 156,
|
||||
align: 'left',
|
||||
ellipsis: true,
|
||||
}, {
|
||||
title: '操作时间',
|
||||
dataIndex: 'createTime',
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
||||
Reference in New Issue
Block a user