✨ 清空操作日志.
This commit is contained in:
@@ -25,8 +25,8 @@ public class HostConnectLogOperatorType extends InitializingOperatorTypes {
|
|||||||
@Override
|
@Override
|
||||||
public OperatorType[] types() {
|
public OperatorType[] types() {
|
||||||
return new OperatorType[]{
|
return new OperatorType[]{
|
||||||
new OperatorType(H, DELETE, "删除主机连接记录 <sb>${count}</sb>条"),
|
new OperatorType(H, DELETE, "删除主机连接记录 <sb>${count}</sb> 条"),
|
||||||
new OperatorType(H, CLEAR, "清空主机连接记录 <sb>${count}</sb>条"),
|
new OperatorType(H, CLEAR, "清空主机连接记录 <sb>${count}</sb> 条"),
|
||||||
new OperatorType(M, FORCE_OFFLINE, "强制下线主机连接 <sb>${hostName}</sb>"),
|
new OperatorType(M, FORCE_OFFLINE, "强制下线主机连接 <sb>${hostName}</sb>"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
package com.orion.ops.module.infra.controller;
|
package com.orion.ops.module.infra.controller;
|
||||||
|
|
||||||
import com.orion.lang.define.wrapper.DataGrid;
|
import com.orion.lang.define.wrapper.DataGrid;
|
||||||
|
import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
|
||||||
import com.orion.ops.framework.common.validator.group.Page;
|
import com.orion.ops.framework.common.validator.group.Page;
|
||||||
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
||||||
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
||||||
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
|
||||||
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
||||||
|
import com.orion.ops.module.infra.define.operator.OperatorLogOperatorType;
|
||||||
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
|
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
|
||||||
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
|
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
|
||||||
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
||||||
import com.orion.ops.module.infra.service.OperatorLogService;
|
import com.orion.ops.module.infra.service.OperatorLogService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
@@ -47,6 +49,29 @@ public class OperatorLogController {
|
|||||||
return operatorLogService.getOperatorLogPage(request);
|
return operatorLogService.getOperatorLogPage(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperatorLog(OperatorLogOperatorType.DELETE)
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除操作日志")
|
||||||
|
@Parameter(name = "idList", description = "idList", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:operator-log:delete')")
|
||||||
|
public Integer deleteOperatorLog(@RequestParam("idList") List<Long> idList) {
|
||||||
|
return operatorLogService.deleteOperatorLog(idList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/query-count")
|
||||||
|
@Operation(summary = "查询操作日志数量")
|
||||||
|
public Long getOperatorLogCount(@RequestBody OperatorLogQueryRequest request) {
|
||||||
|
return operatorLogService.getOperatorLogCount(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperatorLog(OperatorLogOperatorType.CLEAR)
|
||||||
|
@PostMapping("/clear")
|
||||||
|
@Operation(summary = "清空操作日志")
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:operator-log:clear')")
|
||||||
|
public Integer clearOperatorLog(@RequestBody OperatorLogQueryRequest request) {
|
||||||
|
return operatorLogService.clearOperatorLog(request);
|
||||||
|
}
|
||||||
|
|
||||||
@IgnoreLog(IgnoreLogMode.RET)
|
@IgnoreLog(IgnoreLogMode.RET)
|
||||||
@GetMapping("/login-history")
|
@GetMapping("/login-history")
|
||||||
@Operation(summary = "查询用户登录日志")
|
@Operation(summary = "查询用户登录日志")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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.LoginHistoryVO;
|
||||||
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,6 +25,7 @@ public interface OperatorLogConvert {
|
|||||||
|
|
||||||
OperatorLogDO to(OperatorLogQueryRequest request);
|
OperatorLogDO to(OperatorLogQueryRequest request);
|
||||||
|
|
||||||
|
@Mapping(target = "extra", ignore = true)
|
||||||
OperatorLogVO to(OperatorLogDO domain);
|
OperatorLogVO to(OperatorLogDO domain);
|
||||||
|
|
||||||
LoginHistoryVO toLoginHistory(OperatorLogDO domain);
|
LoginHistoryVO toLoginHistory(OperatorLogDO domain);
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.orion.ops.module.infra.define.operator;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.biz.operator.log.core.annotation.Module;
|
||||||
|
import com.orion.ops.framework.biz.operator.log.core.factory.InitializingOperatorTypes;
|
||||||
|
import com.orion.ops.framework.biz.operator.log.core.model.OperatorType;
|
||||||
|
|
||||||
|
import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.H;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作日志 操作日志类型
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2024-3-4 16:20
|
||||||
|
*/
|
||||||
|
@Module("infra:operator-log")
|
||||||
|
public class OperatorLogOperatorType extends InitializingOperatorTypes {
|
||||||
|
|
||||||
|
public static final String DELETE = "operator-log:delete";
|
||||||
|
|
||||||
|
public static final String CLEAR = "operator-log:clear";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OperatorType[] types() {
|
||||||
|
return new OperatorType[]{
|
||||||
|
new OperatorType(H, DELETE, "删除操作日志 <sb>${count}</sb> 条"),
|
||||||
|
new OperatorType(H, CLEAR, "清空操作日志 <sb>${count}</sb> 条"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import lombok.NoArgsConstructor;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作日志 视图响应对象
|
* 操作日志 视图响应对象
|
||||||
@@ -59,7 +60,7 @@ public class OperatorLogVO implements Serializable {
|
|||||||
private String logInfo;
|
private String logInfo;
|
||||||
|
|
||||||
@Schema(description = "参数")
|
@Schema(description = "参数")
|
||||||
private String extra;
|
private Map<String, Object> extra;
|
||||||
|
|
||||||
@Schema(description = "操作结果 0失败 1成功")
|
@Schema(description = "操作结果 0失败 1成功")
|
||||||
private Integer result;
|
private Integer result;
|
||||||
|
|||||||
@@ -32,6 +32,30 @@ public interface OperatorLogService {
|
|||||||
*/
|
*/
|
||||||
DataGrid<OperatorLogVO> getOperatorLogPage(OperatorLogQueryRequest request);
|
DataGrid<OperatorLogVO> getOperatorLogPage(OperatorLogQueryRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除操作日志
|
||||||
|
*
|
||||||
|
* @param idList idList
|
||||||
|
* @return effect
|
||||||
|
*/
|
||||||
|
Integer deleteOperatorLog(List<Long> idList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询操作日志数量
|
||||||
|
*
|
||||||
|
* @param request request
|
||||||
|
* @return count
|
||||||
|
*/
|
||||||
|
Long getOperatorLogCount(OperatorLogQueryRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空操作日志
|
||||||
|
*
|
||||||
|
* @param request request
|
||||||
|
* @return effect
|
||||||
|
*/
|
||||||
|
Integer clearOperatorLog(OperatorLogQueryRequest request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询用户登录日志
|
* 查询用户登录日志
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package com.orion.ops.module.infra.service.impl;
|
package com.orion.ops.module.infra.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.orion.lang.define.wrapper.DataGrid;
|
import com.orion.lang.define.wrapper.DataGrid;
|
||||||
import com.orion.lang.utils.Arrays1;
|
import com.orion.lang.utils.Arrays1;
|
||||||
import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel;
|
import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel;
|
||||||
|
import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||||
import com.orion.ops.framework.common.constant.Const;
|
import com.orion.ops.framework.common.constant.Const;
|
||||||
import com.orion.ops.module.infra.convert.OperatorLogConvert;
|
import com.orion.ops.module.infra.convert.OperatorLogConvert;
|
||||||
import com.orion.ops.module.infra.dao.OperatorLogDAO;
|
import com.orion.ops.module.infra.dao.OperatorLogDAO;
|
||||||
@@ -48,7 +50,38 @@ public class OperatorLogServiceImpl implements OperatorLogService {
|
|||||||
// 查询
|
// 查询
|
||||||
return operatorLogDAO.of(wrapper)
|
return operatorLogDAO.of(wrapper)
|
||||||
.page(request)
|
.page(request)
|
||||||
.dataGrid(OperatorLogConvert.MAPPER::to);
|
.dataGrid(s -> {
|
||||||
|
OperatorLogVO vo = OperatorLogConvert.MAPPER.to(s);
|
||||||
|
vo.setExtra(JSON.parseObject(s.getExtra()));
|
||||||
|
return vo;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer deleteOperatorLog(List<Long> idList) {
|
||||||
|
log.info("OperatorLogService.deleteOperatorLog start {}", JSON.toJSONString(idList));
|
||||||
|
int effect = operatorLogDAO.deleteBatchIds(idList);
|
||||||
|
log.info("OperatorLogService.deleteOperatorLog finish {}", effect);
|
||||||
|
// 设置日志参数
|
||||||
|
OperatorLogs.add(OperatorLogs.COUNT, effect);
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getOperatorLogCount(OperatorLogQueryRequest request) {
|
||||||
|
return operatorLogDAO.selectCount(this.buildQueryWrapper(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer clearOperatorLog(OperatorLogQueryRequest request) {
|
||||||
|
log.info("OperatorLogService.clearOperatorLog start {}", JSON.toJSONString(request));
|
||||||
|
// 删除
|
||||||
|
LambdaQueryWrapper<OperatorLogDO> wrapper = this.buildQueryWrapper(request);
|
||||||
|
int effect = operatorLogDAO.delete(wrapper);
|
||||||
|
log.info("OperatorLogService.clearOperatorLog finish {}", effect);
|
||||||
|
// 设置日志参数
|
||||||
|
OperatorLogs.add(OperatorLogs.COUNT, effect);
|
||||||
|
return effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { DataGrid, Pagination } from '@/types/global';
|
import type { DataGrid, Pagination } from '@/types/global';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import qs from 'query-string';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作日志查询参数
|
* 操作日志查询参数
|
||||||
@@ -60,6 +61,32 @@ export function getOperatorLogPage(request: OperatorLogQueryRequest) {
|
|||||||
return axios.post<DataGrid<OperatorLogQueryResponse>>('/infra/operator-log/query', request);
|
return axios.post<DataGrid<OperatorLogQueryResponse>>('/infra/operator-log/query', request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除操作日志
|
||||||
|
*/
|
||||||
|
export function deleteOperatorLog(idList: Array<number>) {
|
||||||
|
return axios.delete('/infra/operator-log/delete', {
|
||||||
|
params: { idList },
|
||||||
|
paramsSerializer: params => {
|
||||||
|
return qs.stringify(params, { arrayFormat: 'comma' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询操作日志数量
|
||||||
|
*/
|
||||||
|
export function getOperatorLogCount(request: OperatorLogQueryRequest) {
|
||||||
|
return axios.post<number>('/infra/operator-log/query-count', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空操作日志
|
||||||
|
*/
|
||||||
|
export function clearOperatorLog(request: OperatorLogQueryRequest) {
|
||||||
|
return axios.post<number>('/infra/operator-log/clear', request);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询登录日志
|
* 查询登录日志
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -14,9 +14,8 @@
|
|||||||
title="操作日志"
|
title="操作日志"
|
||||||
:header-style="{ paddingBottom: '0' }"
|
:header-style="{ paddingBottom: '0' }"
|
||||||
:body-style="{ padding: '8px 20px 8px 20px' }">
|
:body-style="{ padding: '8px 20px 8px 20px' }">
|
||||||
<operator-log-table :visible-user="false"
|
<operator-log-simple-table :current="true"
|
||||||
:visible-handle="false"
|
:handle-column="false" />
|
||||||
:current="true" />
|
|
||||||
</a-card>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
<a-grid class="right-side"
|
<a-grid class="right-side"
|
||||||
@@ -39,7 +38,7 @@
|
|||||||
import Banner from './components/banner.vue';
|
import Banner from './components/banner.vue';
|
||||||
import QuickOperation from './components/quick-operation.vue';
|
import QuickOperation from './components/quick-operation.vue';
|
||||||
import Docs from './components/docs.vue';
|
import Docs from './components/docs.vue';
|
||||||
import OperatorLogTable from '@/views/user/operator-log/components/operator-log-table.vue';
|
import OperatorLogSimpleTable from '@/views/user/operator-log/components/operator-log-simple-table.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -174,12 +174,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
<!-- 清空模态框 -->
|
|
||||||
<connect-log-clear-modal ref="clearModal"
|
|
||||||
@clear="fetchTableData" />
|
|
||||||
<!-- 详情模态框 -->
|
|
||||||
<connect-log-detail-drawer ref="detailModal" />
|
|
||||||
</a-card>
|
</a-card>
|
||||||
|
<!-- 清空模态框 -->
|
||||||
|
<connect-log-clear-modal ref="clearModal"
|
||||||
|
@clear="fetchTableData" />
|
||||||
|
<!-- 详情模态框 -->
|
||||||
|
<connect-log-detail-drawer ref="detailModal" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -12,14 +12,12 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import ConnectLogTable from './components/connect-log-table.vue';
|
|
||||||
import { ref, onBeforeMount, onUnmounted } from 'vue';
|
import { ref, onBeforeMount, onUnmounted } from 'vue';
|
||||||
import { useCacheStore, useDictStore } from '@/store';
|
import { useCacheStore, useDictStore } from '@/store';
|
||||||
import { dictKeys } from './types/const';
|
import { dictKeys } from './types/const';
|
||||||
|
import ConnectLogTable from './components/connect-log-table.vue';
|
||||||
|
|
||||||
const render = ref(false);
|
const render = ref(false);
|
||||||
const table = ref();
|
|
||||||
const modal = ref();
|
|
||||||
|
|
||||||
// 加载字典配置
|
// 加载字典配置
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
|
|||||||
@@ -18,20 +18,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- 表格组件 -->
|
<!-- 表格组件 -->
|
||||||
<operator-log-table ref="table"
|
<operator-log-simple-table ref="table"
|
||||||
:visible-user="false"
|
:current="!user"
|
||||||
:current="!user"
|
:base-params="{ userId: user?.id }" />
|
||||||
:base-params="{userId: user?.id}"
|
|
||||||
@viewDetail="(e) => view.open(e)" />
|
|
||||||
</a-card>
|
</a-card>
|
||||||
<!-- json 查看器模态框 -->
|
|
||||||
<json-editor-modal ref="view" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'operatorLogList'
|
name: 'userOperatorLog'
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -40,10 +36,9 @@
|
|||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import { ref, onBeforeMount } from 'vue';
|
import { ref, onBeforeMount } from 'vue';
|
||||||
import { useCacheStore, useDictStore } from '@/store';
|
import { useCacheStore, useDictStore } from '@/store';
|
||||||
import { dictKeys } from '../../operator-log/types/const';
|
import { dictKeys } from '@/views/user/operator-log/types/const';
|
||||||
import OperatorLogQueryHeader from '../../operator-log/components/operator-log-query-header.vue';
|
import OperatorLogQueryHeader from '@/views/user/operator-log/components/operator-log-query-header.vue';
|
||||||
import OperatorLogTable from '../../operator-log/components/operator-log-table.vue';
|
import OperatorLogSimpleTable from '@/views/user/operator-log/components/operator-log-simple-table.vue';
|
||||||
import JsonEditorModal from '@/components/view/json-editor/json-editor-modal.vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
user: Object as PropType<UserQueryResponse>,
|
user: Object as PropType<UserQueryResponse>,
|
||||||
@@ -53,7 +48,6 @@
|
|||||||
|
|
||||||
const render = ref();
|
const render = ref();
|
||||||
const table = ref();
|
const table = ref();
|
||||||
const view = ref();
|
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
// 加载字典值
|
// 加载字典值
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<a-tab-pane key="operatorLog"
|
<a-tab-pane key="operatorLog"
|
||||||
v-if="!user || hasPermission('infra:operator-log:query')"
|
v-if="!user || hasPermission('infra:operator-log:query')"
|
||||||
title="操作日志">
|
title="操作日志">
|
||||||
<operator-log-list :user="user" />
|
<user-operator-log :user="user" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<!-- 返回 -->
|
<!-- 返回 -->
|
||||||
<a-tab-pane key="back" v-if="userId">
|
<a-tab-pane key="back" v-if="userId">
|
||||||
@@ -48,14 +48,15 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import UserSession from './components/user-session.vue';
|
import type { UserQueryResponse } from '@/api/user/user';
|
||||||
import OperatorLogList from './components/operator-log-list.vue';
|
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { onBeforeMount, ref } from 'vue';
|
|
||||||
import usePermission from '@/hooks/permission';
|
import usePermission from '@/hooks/permission';
|
||||||
|
import { onBeforeMount, ref } from 'vue';
|
||||||
import { useUserStore } from '@/store';
|
import { useUserStore } from '@/store';
|
||||||
import { getUser, UserQueryResponse } from '@/api/user/user';
|
import { getUser } from '@/api/user/user';
|
||||||
import UserBaseInfo from './components/user-base-info.vue';
|
import UserBaseInfo from './components/user-base-info.vue';
|
||||||
|
import UserSession from './components/user-session.vue';
|
||||||
|
import UserOperatorLog from './components/user-operator-log.vue';
|
||||||
import LoginHistory from './components/login-history.vue';
|
import LoginHistory from './components/login-history.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal v-model:visible="visible"
|
||||||
|
body-class="modal-form"
|
||||||
|
title-align="start"
|
||||||
|
title="清空操作日志"
|
||||||
|
:align-center="false"
|
||||||
|
:draggable="true"
|
||||||
|
:mask-closable="false"
|
||||||
|
:unmount-on-close="true"
|
||||||
|
ok-text="清空"
|
||||||
|
:ok-button-props="{ disabled: loading }"
|
||||||
|
:cancel-button-props="{ disabled: loading }"
|
||||||
|
:on-before-ok="handlerOk"
|
||||||
|
@close="handleClose">
|
||||||
|
<a-spin class="full" :loading="loading">
|
||||||
|
<a-form :model="formModel"
|
||||||
|
label-align="right"
|
||||||
|
:style="{ width: '460px' }"
|
||||||
|
:label-col-props="{ span: 5 }"
|
||||||
|
:wrapper-col-props="{ span: 19 }">
|
||||||
|
<!-- 操作用户 -->
|
||||||
|
<a-form-item field="userId"
|
||||||
|
label="操作用户"
|
||||||
|
>
|
||||||
|
<user-selector v-model="formModel.userId"
|
||||||
|
placeholder="请选择操作用户"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 操作模块 -->
|
||||||
|
<a-form-item field="module" label="操作模块">
|
||||||
|
<a-select v-model="formModel.module"
|
||||||
|
:options="toOptions(operatorLogModuleKey)"
|
||||||
|
:allow-search="true"
|
||||||
|
:filter-option="labelFilter"
|
||||||
|
placeholder="请选择操作模块"
|
||||||
|
@change="m => selectedModule(m as string)"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 操作类型 -->
|
||||||
|
<a-form-item field="type" label="操作类型">
|
||||||
|
<a-select v-model="formModel.type"
|
||||||
|
:options="typeOptions"
|
||||||
|
:allow-search="true"
|
||||||
|
:filter-option="labelFilter"
|
||||||
|
placeholder="请选择操作类型"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 风险等级 -->
|
||||||
|
<a-form-item field="riskLevel" label="风险等级">
|
||||||
|
<a-select v-model="formModel.riskLevel"
|
||||||
|
:options="toOptions(operatorRiskLevelKey)"
|
||||||
|
placeholder="请选择风险等级"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 执行结果 -->
|
||||||
|
<a-form-item field="result" label="执行结果">
|
||||||
|
<a-select v-model="formModel.result"
|
||||||
|
:options="toOptions(operatorLogResultKey)"
|
||||||
|
placeholder="请选择执行结果"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 执行时间 -->
|
||||||
|
<a-form-item field="startTimeRange" label="执行时间">
|
||||||
|
<a-range-picker v-model="formModel.startTimeRange"
|
||||||
|
style="width: 100%"
|
||||||
|
:time-picker-props="{ defaultValue: ['00:00:00', '23:59:59'] }"
|
||||||
|
show-time
|
||||||
|
format="YYYY-MM-DD HH:mm:ss" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'userOperatorLogClearModal'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<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 useLoading from '@/hooks/loading';
|
||||||
|
import useVisible from '@/hooks/visible';
|
||||||
|
import { getOperatorLogCount, clearOperatorLog } from '@/api/user/operator-log';
|
||||||
|
import { Message, Modal } from '@arco-design/web-vue';
|
||||||
|
import { useDictStore } from '@/store';
|
||||||
|
import { operatorLogModuleKey, operatorLogResultKey, operatorLogTypeKey, operatorRiskLevelKey } from '@/views/user/operator-log/types/const';
|
||||||
|
import { labelFilter } from '@/types/form';
|
||||||
|
import UserSelector from '@/components/user/user/user-selector.vue';
|
||||||
|
|
||||||
|
const { $state: dictState, toOptions } = useDictStore();
|
||||||
|
const { visible, setVisible } = useVisible();
|
||||||
|
const { loading, setLoading } = useLoading();
|
||||||
|
|
||||||
|
const defaultForm = (): OperatorLogQueryRequest => {
|
||||||
|
return {
|
||||||
|
module: undefined,
|
||||||
|
type: undefined,
|
||||||
|
riskLevel: undefined,
|
||||||
|
result: undefined,
|
||||||
|
startTimeRange: undefined,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeOptions = ref<SelectOptionData[]>(toOptions(operatorLogTypeKey));
|
||||||
|
const formModel = ref<OperatorLogQueryRequest>({});
|
||||||
|
|
||||||
|
const emits = defineEmits(['clear']);
|
||||||
|
|
||||||
|
// 打开
|
||||||
|
const open = () => {
|
||||||
|
renderForm({ ...defaultForm() });
|
||||||
|
setVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 渲染表单
|
||||||
|
const renderForm = (record: any) => {
|
||||||
|
formModel.value = Object.assign({}, record);
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
|
||||||
|
// 选择类型
|
||||||
|
const selectedModule = (module: string) => {
|
||||||
|
if (!module) {
|
||||||
|
// 不选择则重置 options
|
||||||
|
typeOptions.value = toOptions(operatorLogTypeKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const moduleArr = module.split(':');
|
||||||
|
const modulePrefix = moduleArr[moduleArr.length - 1] + ':';
|
||||||
|
// 渲染 options
|
||||||
|
typeOptions.value = dictState[operatorLogTypeKey].filter(s => (s.value as string).startsWith(modulePrefix));
|
||||||
|
// 渲染输入框
|
||||||
|
if (formModel.value.type && !formModel.value.type.startsWith(modulePrefix)) {
|
||||||
|
formModel.value.type = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确定
|
||||||
|
const handlerOk = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// 获取总数量
|
||||||
|
const { data } = await getOperatorLogCount(formModel.value);
|
||||||
|
if (data) {
|
||||||
|
// 清空
|
||||||
|
doClear(data);
|
||||||
|
} else {
|
||||||
|
// 无数据
|
||||||
|
Message.warning('当前条件未查询到数据');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 执行删除
|
||||||
|
const doClear = (count: number) => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '删除清空',
|
||||||
|
content: `确定要删除 ${count} 条数据吗? 确定后将立即删除且无法恢复!`,
|
||||||
|
onOk: async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// 调用删除
|
||||||
|
await clearOperatorLog(formModel.value);
|
||||||
|
emits('clear');
|
||||||
|
// 清空
|
||||||
|
setVisible(false);
|
||||||
|
handlerClear();
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭
|
||||||
|
const handleClose = () => {
|
||||||
|
handlerClear();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空
|
||||||
|
const handlerClear = () => {
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 表格 -->
|
||||||
|
<a-table row-key="id"
|
||||||
|
class="table-wrapper-8"
|
||||||
|
ref="tableRef"
|
||||||
|
:loading="loading"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:data="tableRenderData"
|
||||||
|
:pagination="pagination"
|
||||||
|
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
||||||
|
@page-size-change="(size) => fetchTableData(1, size)"
|
||||||
|
:bordered="false">
|
||||||
|
<!-- 操作模块 -->
|
||||||
|
<template #module="{ record }">
|
||||||
|
{{ getDictValue(operatorLogModuleKey, record.module) }}
|
||||||
|
<icon-oblique-line />
|
||||||
|
{{ getDictValue(operatorLogTypeKey, record.type) }}
|
||||||
|
</template>
|
||||||
|
<!-- 风险等级 -->
|
||||||
|
<template #riskLevel="{ record }">
|
||||||
|
<a-tag :color="getDictValue(operatorRiskLevelKey, record.riskLevel, 'color')">
|
||||||
|
{{ getDictValue(operatorRiskLevelKey, record.riskLevel) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 执行结果 -->
|
||||||
|
<template #result="{ record }">
|
||||||
|
<a-tag :color="getDictValue(operatorLogResultKey, record.result, 'color')">
|
||||||
|
{{ getDictValue(operatorLogResultKey, record.result) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 操作日志 -->
|
||||||
|
<template #originLogInfo="{ record }">
|
||||||
|
<icon-copy class="copy-left" @click="copy(record.originLogInfo, '已复制')" />
|
||||||
|
<span v-html="replaceHtmlTag(record.logInfo)" />
|
||||||
|
</template>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<template #handle="{ record }">
|
||||||
|
<div class="table-handle-wrapper">
|
||||||
|
<!-- 详情 -->
|
||||||
|
<a-button type="text"
|
||||||
|
size="mini"
|
||||||
|
@click="openLogDetail(record)">
|
||||||
|
详情
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<!-- json 查看器模态框 -->
|
||||||
|
<json-editor-modal ref="jsonView" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'userOperatorLogSimpleTable'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { OperatorLogQueryRequest, OperatorLogQueryResponse } from '@/api/user/operator-log';
|
||||||
|
import { ref, onMounted, onBeforeMount } from 'vue';
|
||||||
|
import { operatorLogModuleKey, operatorLogTypeKey, operatorRiskLevelKey, operatorLogResultKey, dictKeys } from '../types/const';
|
||||||
|
import columns from '../types/table.columns';
|
||||||
|
import { getLogDetail } from '../types/const';
|
||||||
|
import useCopy from '@/hooks/copy';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import { usePagination } from '@/types/table';
|
||||||
|
import { useDictStore } from '@/store';
|
||||||
|
import { getOperatorLogPage } from '@/api/user/operator-log';
|
||||||
|
import { getCurrentUserOperatorLog } from '@/api/user/mine';
|
||||||
|
import { replaceHtmlTag, clearHtmlTag } from '@/utils';
|
||||||
|
import JsonEditorModal from '@/components/view/json-editor/json-editor-modal.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
handleColumn: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
current: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
baseParams: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const pagination = usePagination();
|
||||||
|
const { loading, setLoading } = useLoading();
|
||||||
|
const { getDictValue } = useDictStore();
|
||||||
|
const { copy } = useCopy();
|
||||||
|
|
||||||
|
const jsonView = ref();
|
||||||
|
const tableColumns = ref();
|
||||||
|
const tableRenderData = ref<OperatorLogQueryResponse[]>([]);
|
||||||
|
|
||||||
|
// 查看详情
|
||||||
|
const openLogDetail = (record: OperatorLogQueryResponse) => {
|
||||||
|
jsonView.value.open(getLogDetail(record));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const doFetchTableData = async (request: OperatorLogQueryRequest) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
let rows;
|
||||||
|
if (props.current) {
|
||||||
|
// 查询当前用户
|
||||||
|
const { data } = await getCurrentUserOperatorLog(request);
|
||||||
|
rows = data;
|
||||||
|
} else {
|
||||||
|
// 查询所有
|
||||||
|
const { data } = await getOperatorLogPage({ ...request, ...props.baseParams });
|
||||||
|
rows = data;
|
||||||
|
}
|
||||||
|
tableRenderData.value = rows.rows.map(s => {
|
||||||
|
return { ...s, originLogInfo: clearHtmlTag(s.logInfo) };
|
||||||
|
});
|
||||||
|
pagination.total = rows.total;
|
||||||
|
pagination.current = request.page;
|
||||||
|
pagination.pageSize = request.limit;
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换页码
|
||||||
|
const fetchTableData = (page = 1, limit = pagination.pageSize, form = {}) => {
|
||||||
|
doFetchTableData({ page, limit, ...form });
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
fetchTableData
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载字典值
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
const dictStore = useDictStore();
|
||||||
|
await dictStore.loadKeys(dictKeys);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
let cols = [...columns].map(s => {
|
||||||
|
return { ...s };
|
||||||
|
}).filter(s => s.dataIndex !== 'username');
|
||||||
|
if (props.handleColumn) {
|
||||||
|
const handleCol = cols.find(s => s.dataIndex === 'handle');
|
||||||
|
// 设置操作项宽度
|
||||||
|
if (handleCol) {
|
||||||
|
handleCol.width = 80;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 不显示操作
|
||||||
|
cols = cols.filter(s => s.dataIndex !== 'handle');
|
||||||
|
}
|
||||||
|
tableColumns.value = cols;
|
||||||
|
fetchTableData();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
||||||
@@ -1,117 +1,191 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-table row-key="id"
|
<!-- 查询头 -->
|
||||||
class="table-wrapper-8"
|
<a-card class="general-card table-search-card">
|
||||||
ref="tableRef"
|
<!-- 查询头组件 -->
|
||||||
:loading="loading"
|
<operator-log-query-header @submit="(e) => fetchTableData(undefined, undefined, e)" />
|
||||||
:columns="tableColumns"
|
</a-card>
|
||||||
:data="tableRenderData"
|
<!-- 表格 -->
|
||||||
:pagination="pagination"
|
<a-card class="general-card table-card">
|
||||||
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
<template #title>
|
||||||
@page-size-change="(size) => fetchTableData(1, size)"
|
<!-- 左侧操作 -->
|
||||||
:bordered="false">
|
<div class="table-left-bar-handle">
|
||||||
<!-- 操作模块 -->
|
<!-- 标题 -->
|
||||||
<template #module="{ record }">
|
<div class="table-title">
|
||||||
{{ getDictValue(operatorLogModuleKey, record.module) }}
|
操作日志
|
||||||
</template>
|
</div>
|
||||||
<!-- 操作类型 -->
|
</div>
|
||||||
<template #type="{ record }">
|
<!-- 右侧操作 -->
|
||||||
{{ getDictValue(operatorLogTypeKey, record.type) }}
|
<div class="table-right-bar-handle">
|
||||||
</template>
|
<a-space>
|
||||||
<!-- 风险等级 -->
|
<!-- 清空 -->
|
||||||
<template #riskLevel="{ record }">
|
<a-button v-permission="['infra:operator-log:clear']"
|
||||||
<a-tag :color="getDictValue(operatorRiskLevelKey, record.riskLevel, 'color')">
|
status="danger"
|
||||||
{{ getDictValue(operatorRiskLevelKey, record.riskLevel) }}
|
@click="openClear">
|
||||||
</a-tag>
|
清空
|
||||||
</template>
|
<template #icon>
|
||||||
<!-- 执行结果 -->
|
<icon-close />
|
||||||
<template #result="{ record }">
|
</template>
|
||||||
<a-tag :color="getDictValue(operatorLogResultKey, record.result, 'color')">
|
</a-button>
|
||||||
{{ getDictValue(operatorLogResultKey, record.result) }}
|
<!-- 删除 -->
|
||||||
</a-tag>
|
<a-popconfirm :content="`确认删除选中的 ${selectedKeys.length} 条记录吗?`"
|
||||||
</template>
|
position="br"
|
||||||
<!-- 操作日志 -->
|
type="warning"
|
||||||
<template #originLogInfo="{ record }">
|
@ok="deleteSelectRows">
|
||||||
<icon-copy class="copy-left" @click="copy(record.originLogInfo, '已复制')" />
|
<a-button v-permission="['infra:operator-log:delete']"
|
||||||
<span v-html="replaceHtmlTag(record.logInfo)" />
|
type="secondary"
|
||||||
</template>
|
status="danger"
|
||||||
<!-- 操作 -->
|
:disabled="selectedKeys.length === 0">
|
||||||
<template #handle="{ record }">
|
删除
|
||||||
<div class="table-handle-wrapper">
|
<template #icon>
|
||||||
<!-- 详情 -->
|
<icon-delete />
|
||||||
<a-button type="text"
|
</template>
|
||||||
size="mini"
|
</a-button>
|
||||||
@click="viewDetail(record)">
|
</a-popconfirm>
|
||||||
详情
|
</a-space>
|
||||||
</a-button>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
<!-- 表格 -->
|
||||||
|
<a-table row-key="id"
|
||||||
|
class="table-wrapper-8"
|
||||||
|
ref="tableRef"
|
||||||
|
:loading="loading"
|
||||||
|
v-model:selected-keys="selectedKeys"
|
||||||
|
:row-selection="rowSelection"
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableRenderData"
|
||||||
|
:pagination="pagination"
|
||||||
|
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
||||||
|
@page-size-change="(size) => fetchTableData(1, size)"
|
||||||
|
:bordered="false">
|
||||||
|
<!-- 操作模块 -->
|
||||||
|
<template #module="{ record }">
|
||||||
|
{{ getDictValue(operatorLogModuleKey, record.module) }}
|
||||||
|
<icon-oblique-line />
|
||||||
|
{{ getDictValue(operatorLogTypeKey, record.type) }}
|
||||||
|
</template>
|
||||||
|
<!-- 风险等级 -->
|
||||||
|
<template #riskLevel="{ record }">
|
||||||
|
<a-tag :color="getDictValue(operatorRiskLevelKey, record.riskLevel, 'color')">
|
||||||
|
{{ getDictValue(operatorRiskLevelKey, record.riskLevel) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 执行结果 -->
|
||||||
|
<template #result="{ record }">
|
||||||
|
<a-tag :color="getDictValue(operatorLogResultKey, record.result, 'color')">
|
||||||
|
{{ getDictValue(operatorLogResultKey, record.result) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 操作日志 -->
|
||||||
|
<template #originLogInfo="{ record }">
|
||||||
|
<icon-copy class="copy-left" @click="copy(record.originLogInfo, '已复制')" />
|
||||||
|
<span v-html="replaceHtmlTag(record.logInfo)" />
|
||||||
|
</template>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<template #handle="{ record }">
|
||||||
|
<div class="table-handle-wrapper">
|
||||||
|
<!-- 详情 -->
|
||||||
|
<a-button type="text"
|
||||||
|
size="mini"
|
||||||
|
@click="openLogDetail(record)">
|
||||||
|
详情
|
||||||
|
</a-button>
|
||||||
|
<!-- 删除 -->
|
||||||
|
<a-popconfirm content="确认删除这条记录吗?"
|
||||||
|
position="left"
|
||||||
|
type="warning"
|
||||||
|
@ok="deleteRow(record)">
|
||||||
|
<a-button v-permission="['infra:operator-log:delete']"
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
status="danger">
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
<!-- 清理模态框 -->
|
||||||
|
<operator-log-clear-modal ref="clearModal"
|
||||||
|
@clear="fetchTableData" />
|
||||||
|
<!-- json 查看器模态框 -->
|
||||||
|
<json-editor-modal ref="jsonView" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'operatorLogTable'
|
name: 'userOperatorLogTable'
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { OperatorLogQueryRequest, OperatorLogQueryResponse } from '@/api/user/operator-log';
|
import type { OperatorLogQueryRequest, OperatorLogQueryResponse } from '@/api/user/operator-log';
|
||||||
import { ref, onMounted, onBeforeMount } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { operatorLogModuleKey, operatorLogTypeKey, operatorRiskLevelKey, operatorLogResultKey, dictKeys } from '../types/const';
|
import { operatorLogModuleKey, operatorLogTypeKey, operatorRiskLevelKey, operatorLogResultKey, getLogDetail } from '../types/const';
|
||||||
import columns from '../types/table.columns';
|
import columns from '../types/table.columns';
|
||||||
import useLoading from '@/hooks/loading';
|
|
||||||
import { usePagination } from '@/types/table';
|
|
||||||
import { useDictStore } from '@/store';
|
|
||||||
import { getOperatorLogPage } from '@/api/user/operator-log';
|
|
||||||
import { replaceHtmlTag, clearHtmlTag, dateFormat } from '@/utils';
|
|
||||||
import { pick } from 'lodash';
|
|
||||||
import { getCurrentUserOperatorLog } from '@/api/user/mine';
|
|
||||||
import useCopy from '@/hooks/copy';
|
import useCopy from '@/hooks/copy';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
const emits = defineEmits(['viewDetail']);
|
import { usePagination, useRowSelection } from '@/types/table';
|
||||||
const props = defineProps({
|
import { useDictStore } from '@/store';
|
||||||
visibleUser: {
|
import { getOperatorLogPage, deleteOperatorLog } from '@/api/user/operator-log';
|
||||||
type: Boolean,
|
import { replaceHtmlTag, clearHtmlTag } from '@/utils';
|
||||||
default: true
|
import { Message } from '@arco-design/web-vue';
|
||||||
},
|
import OperatorLogQueryHeader from './operator-log-query-header.vue';
|
||||||
visibleHandle: {
|
import OperatorLogClearModal from './operator-log-clear-modal.vue';
|
||||||
type: Boolean,
|
import JsonEditorModal from '@/components/view/json-editor/json-editor-modal.vue';
|
||||||
default: true
|
|
||||||
},
|
|
||||||
current: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
baseParams: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const pagination = usePagination();
|
const pagination = usePagination();
|
||||||
|
const rowSelection = useRowSelection();
|
||||||
const { loading, setLoading } = useLoading();
|
const { loading, setLoading } = useLoading();
|
||||||
const { getDictValue } = useDictStore();
|
const { getDictValue } = useDictStore();
|
||||||
const { copy } = useCopy();
|
const { copy } = useCopy();
|
||||||
|
|
||||||
const tableColumns = ref();
|
const clearModal = ref();
|
||||||
|
const jsonView = ref();
|
||||||
const tableRenderData = ref<OperatorLogQueryResponse[]>([]);
|
const tableRenderData = ref<OperatorLogQueryResponse[]>([]);
|
||||||
|
const selectedKeys = ref<number[]>([]);
|
||||||
|
|
||||||
// 查看详情
|
// 查看详情
|
||||||
const viewDetail = (record: OperatorLogQueryResponse) => {
|
const openLogDetail = (record: OperatorLogQueryResponse) => {
|
||||||
|
jsonView.value.open(getLogDetail(record));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开清空
|
||||||
|
const openClear = () => {
|
||||||
|
clearModal.value?.open();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除选中行
|
||||||
|
const deleteSelectRows = async () => {
|
||||||
try {
|
try {
|
||||||
const detail = Object.assign({} as Record<string, any>,
|
setLoading(true);
|
||||||
pick(record, 'traceId', 'address', 'location',
|
// 调用删除接口
|
||||||
'userAgent', 'errorMessage'));
|
await deleteOperatorLog(selectedKeys.value);
|
||||||
detail.duration = `${record.duration} ms`;
|
Message.success(`成功删除 ${selectedKeys.value.length} 条数据`);
|
||||||
detail.startTime = dateFormat(new Date(record.startTime));
|
selectedKeys.value = [];
|
||||||
detail.endTime = dateFormat(new Date(record.endTime));
|
// 重新加载数据
|
||||||
detail.extra = JSON.parse(record?.extra);
|
fetchTableData();
|
||||||
detail.returnValue = JSON.parse(record?.returnValue);
|
|
||||||
emits('viewDetail', detail);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emits('viewDetail', record);
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除当前行
|
||||||
|
const deleteRow = async ({ id }: {
|
||||||
|
id: number
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
// 调用删除接口
|
||||||
|
await deleteOperatorLog([id]);
|
||||||
|
Message.success('删除成功');
|
||||||
|
selectedKeys.value = [];
|
||||||
|
// 重新加载数据
|
||||||
|
fetchTableData();
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -119,22 +193,14 @@
|
|||||||
const doFetchTableData = async (request: OperatorLogQueryRequest) => {
|
const doFetchTableData = async (request: OperatorLogQueryRequest) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let rows;
|
const { data } = await getOperatorLogPage(request);
|
||||||
if (props.current) {
|
tableRenderData.value = data.rows.map(s => {
|
||||||
// 查询当前用户
|
|
||||||
const { data } = await getCurrentUserOperatorLog(request);
|
|
||||||
rows = data;
|
|
||||||
} else {
|
|
||||||
// 查询所有
|
|
||||||
const { data } = await getOperatorLogPage({ ...request, ...props.baseParams });
|
|
||||||
rows = data;
|
|
||||||
}
|
|
||||||
tableRenderData.value = rows.rows.map(s => {
|
|
||||||
return { ...s, originLogInfo: clearHtmlTag(s.logInfo) };
|
return { ...s, originLogInfo: clearHtmlTag(s.logInfo) };
|
||||||
});
|
});
|
||||||
pagination.total = rows.total;
|
pagination.total = data.total;
|
||||||
pagination.current = request.page;
|
pagination.current = request.page;
|
||||||
pagination.pageSize = request.limit;
|
pagination.pageSize = request.limit;
|
||||||
|
selectedKeys.value = [];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -146,31 +212,11 @@
|
|||||||
doFetchTableData({ page, limit, ...form });
|
doFetchTableData({ page, limit, ...form });
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载字典值
|
|
||||||
onBeforeMount(async () => {
|
|
||||||
const dictStore = useDictStore();
|
|
||||||
await dictStore.loadKeys(dictKeys);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let cols = columns;
|
|
||||||
// 不显示用户
|
|
||||||
if (!props.visibleUser) {
|
|
||||||
cols = cols.filter(s => s.dataIndex !== 'username');
|
|
||||||
}
|
|
||||||
// 不显示操作
|
|
||||||
if (!props.visibleHandle) {
|
|
||||||
cols = cols.filter(s => s.dataIndex !== 'handle');
|
|
||||||
}
|
|
||||||
tableColumns.value = cols;
|
|
||||||
fetchTableData();
|
fetchTableData();
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
fetchTableData
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -1,26 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-container">
|
<div class="layout-container" v-if="render">
|
||||||
<!-- 查询头 -->
|
<operator-log-table />
|
||||||
<a-card class="general-card table-search-card">
|
|
||||||
<!-- 查询头组件 -->
|
|
||||||
<operator-log-query-header @submit="(e) => table.fetchTableData(undefined, undefined, e)" />
|
|
||||||
</a-card>
|
|
||||||
<!-- 表格 -->
|
|
||||||
<a-card class="general-card table-card">
|
|
||||||
<template #title>
|
|
||||||
<!-- 左侧操作 -->
|
|
||||||
<div class="table-left-bar-handle">
|
|
||||||
<!-- 标题 -->
|
|
||||||
<div class="table-title">
|
|
||||||
操作日志
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<!-- 表格组件 -->
|
|
||||||
<operator-log-table ref="table" @viewDetail="(e) => view.open(e)" />
|
|
||||||
</a-card>
|
|
||||||
<!-- json 查看器模态框 -->
|
|
||||||
<json-editor-modal ref="view" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -31,16 +11,21 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onUnmounted } from 'vue';
|
import { ref, onUnmounted, onBeforeMount } from 'vue';
|
||||||
import { useCacheStore } from '@/store';
|
import { useCacheStore, useDictStore } from '@/store';
|
||||||
import OperatorLogQueryHeader from './components/operator-log-query-header.vue';
|
import { dictKeys } from './types/const';
|
||||||
import OperatorLogTable from './components/operator-log-table.vue';
|
import OperatorLogTable from './components/operator-log-table.vue';
|
||||||
import JsonEditorModal from '@/components/view/json-editor/json-editor-modal.vue';
|
|
||||||
|
|
||||||
const cacheStore = useCacheStore();
|
const cacheStore = useCacheStore();
|
||||||
|
|
||||||
const table = ref();
|
const render = ref(false);
|
||||||
const view = ref();
|
|
||||||
|
// 加载字典值
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
const dictStore = useDictStore();
|
||||||
|
await dictStore.loadKeys(dictKeys);
|
||||||
|
render.value = true;
|
||||||
|
});
|
||||||
|
|
||||||
// 卸载时清除 cache
|
// 卸载时清除 cache
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const columns = [
|
|||||||
title: '操作',
|
title: '操作',
|
||||||
dataIndex: 'handle',
|
dataIndex: 'handle',
|
||||||
slotName: 'handle',
|
slotName: 'handle',
|
||||||
width: 138,
|
width: 128,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user