站内消息.

This commit is contained in:
lijiahang
2024-05-14 19:17:12 +08:00
parent 2fa3eb2251
commit 4fe6208d0e
19 changed files with 385 additions and 52 deletions

View File

@@ -12,6 +12,8 @@
`2024-05-15` `release` `2024-05-15` `release`
* 🌈 新增 站内信模块 * 🌈 新增 站内信模块
* 🔨 优化 执行命令日志跳转逻辑
* 🔨 修改 `exitStatus` 改为 `exitCode`
[如何升级](/update/v1.0.8.md) [如何升级](/update/v1.0.8.md)

View File

@@ -55,6 +55,10 @@ public interface FieldConst {
String COUNT = "count"; String COUNT = "count";
String DATE = "date";
String TIME = "time";
String LOCATION = "location"; String LOCATION = "location";
String USER_AGENT = "userAgent"; String USER_AGENT = "userAgent";

View File

@@ -0,0 +1,53 @@
package com.orion.ops.module.asset.define.message;
import com.orion.ops.module.infra.define.SystemMessageDefine;
import com.orion.ops.module.infra.enums.MessageClassifyEnum;
import lombok.Getter;
/**
* 命令执行 系统消息定义
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/5/14 17:23
*/
@Getter
public enum ExecMessageDefine implements SystemMessageDefine {
/**
* 命令执行部分失败
*/
EXEC_FAILED(MessageClassifyEnum.NOTICE,
"命令执行失败",
"您在 <sb>${time}</sb> 执行的命令部分失败, 或者返回了非零的 exitCode。点击查看详情 <sb>#${id}</sb> >>>"),
;
ExecMessageDefine(MessageClassifyEnum classify, String title, String content) {
this.classify = classify;
this.type = this.name();
this.title = title;
this.content = content;
}
/**
* 消息分类
*/
private final MessageClassifyEnum classify;
/**
* 消息类型
*/
private final String type;
/**
* 标题
*/
private final String title;
/**
* 内容
*/
private final String content;
}

View File

@@ -0,0 +1,53 @@
package com.orion.ops.module.asset.define.message;
import com.orion.ops.module.infra.define.SystemMessageDefine;
import com.orion.ops.module.infra.enums.MessageClassifyEnum;
import lombok.Getter;
/**
* 上传任务 系统消息定义
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/5/14 17:23
*/
@Getter
public enum UploadMessageDefine implements SystemMessageDefine {
/**
* 上传任务部分失败
*/
UPLOAD_FAILED(MessageClassifyEnum.NOTICE,
"批量上传失败",
"您在 <sb>${time}</sb> 提交的上传任务中, 有部分主机文件上传失败。点击查看详情 <sb>#${id}</sb> >>>"),
;
UploadMessageDefine(MessageClassifyEnum classify, String title, String content) {
this.classify = classify;
this.type = this.name();
this.title = title;
this.content = content;
}
/**
* 消息分类
*/
private final MessageClassifyEnum classify;
/**
* 消息类型
*/
private final String type;
/**
* 标题
*/
private final String title;
/**
* 内容
*/
private final String content;
}

View File

@@ -22,7 +22,7 @@ import java.util.List;
@Schema(name = "ExecCommandDTO", description = "批量执行启动对象") @Schema(name = "ExecCommandDTO", description = "批量执行启动对象")
public class ExecCommandDTO { public class ExecCommandDTO {
@Schema(description = "hostId") @Schema(description = "logId")
private Long logId; private Long logId;
@Schema(description = "用户id") @Schema(description = "用户id")

View File

@@ -67,6 +67,9 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
private CommandExecutor executor; private CommandExecutor executor;
@Getter
private Integer exitCode;
private volatile boolean closed; private volatile boolean closed;
private volatile boolean interrupted; private volatile boolean interrupted;
@@ -228,6 +231,7 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
// 完成 // 完成
updateRecord.setFinishTime(new Date()); updateRecord.setFinishTime(new Date());
updateRecord.setExitStatus(executor.getExitCode()); updateRecord.setExitStatus(executor.getExitCode());
this.exitCode = executor.getExitCode();
} else if (ExecHostStatusEnum.FAILED.equals(status)) { } else if (ExecHostStatusEnum.FAILED.equals(status)) {
// 失败 // 失败
updateRecord.setFinishTime(new Date()); updateRecord.setFinishTime(new Date());

View File

@@ -7,20 +7,29 @@ import com.orion.lang.utils.Booleans;
import com.orion.lang.utils.Threads; import com.orion.lang.utils.Threads;
import com.orion.lang.utils.collect.Lists; import com.orion.lang.utils.collect.Lists;
import com.orion.lang.utils.io.Streams; import com.orion.lang.utils.io.Streams;
import com.orion.lang.utils.time.Dates;
import com.orion.net.host.ssh.ExitCode;
import com.orion.ops.framework.common.constant.ExtraFieldConst;
import com.orion.ops.module.asset.dao.ExecLogDAO; import com.orion.ops.module.asset.dao.ExecLogDAO;
import com.orion.ops.module.asset.define.AssetThreadPools; import com.orion.ops.module.asset.define.AssetThreadPools;
import com.orion.ops.module.asset.define.config.AppExecLogConfig; import com.orion.ops.module.asset.define.config.AppExecLogConfig;
import com.orion.ops.module.asset.define.message.ExecMessageDefine;
import com.orion.ops.module.asset.entity.domain.ExecLogDO; import com.orion.ops.module.asset.entity.domain.ExecLogDO;
import com.orion.ops.module.asset.enums.ExecHostStatusEnum;
import com.orion.ops.module.asset.enums.ExecStatusEnum; import com.orion.ops.module.asset.enums.ExecStatusEnum;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO; import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO;
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandHostDTO; import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandHostDTO;
import com.orion.ops.module.asset.handler.host.exec.command.manager.ExecTaskManager; import com.orion.ops.module.asset.handler.host.exec.command.manager.ExecTaskManager;
import com.orion.ops.module.infra.api.SystemMessageApi;
import com.orion.ops.module.infra.entity.dto.message.SystemMessageDTO;
import com.orion.spring.SpringHolder; import com.orion.spring.SpringHolder;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 命令执行任务 * 命令执行任务
@@ -38,6 +47,8 @@ public class ExecTaskHandler implements IExecTaskHandler {
private static final AppExecLogConfig appExecLogConfig = SpringHolder.getBean(AppExecLogConfig.class); private static final AppExecLogConfig appExecLogConfig = SpringHolder.getBean(AppExecLogConfig.class);
private static final SystemMessageApi systemMessageApi = SpringHolder.getBean(SystemMessageApi.class);
private final ExecCommandDTO execCommand; private final ExecCommandDTO execCommand;
private TimeoutChecker<TimeoutEndpoint> timeoutChecker; private TimeoutChecker<TimeoutEndpoint> timeoutChecker;
@@ -45,6 +56,8 @@ public class ExecTaskHandler implements IExecTaskHandler {
@Getter @Getter
private final List<IExecCommandHandler> handlers; private final List<IExecCommandHandler> handlers;
private Date startTime;
public ExecTaskHandler(ExecCommandDTO execCommand) { public ExecTaskHandler(ExecCommandDTO execCommand) {
this.execCommand = execCommand; this.execCommand = execCommand;
this.handlers = Lists.newList(); this.handlers = Lists.newList();
@@ -56,9 +69,9 @@ public class ExecTaskHandler implements IExecTaskHandler {
// 添加任务 // 添加任务
execTaskManager.addTask(id, this); execTaskManager.addTask(id, this);
log.info("ExecTaskHandler.run start id: {}", id); log.info("ExecTaskHandler.run start id: {}", id);
// 更新状态
this.updateStatus(ExecStatusEnum.RUNNING);
try { try {
// 更新状态
this.updateStatus(ExecStatusEnum.RUNNING);
// 执行命令 // 执行命令
this.runHostCommand(); this.runHostCommand();
// 更新状态-执行完成 // 更新状态-执行完成
@@ -69,10 +82,12 @@ public class ExecTaskHandler implements IExecTaskHandler {
this.updateStatus(ExecStatusEnum.FAILED); this.updateStatus(ExecStatusEnum.FAILED);
log.error("ExecTaskHandler.run error id: {}", id, e); log.error("ExecTaskHandler.run error id: {}", id, e);
} finally { } finally {
// 释放资源 // 检查是否发送消息
Streams.close(this); this.checkSendMessage();
// 移除任务 // 移除任务
execTaskManager.removeTask(id); execTaskManager.removeTask(id);
// 释放资源
this.close();
} }
} }
@@ -82,6 +97,13 @@ public class ExecTaskHandler implements IExecTaskHandler {
handlers.forEach(IExecCommandHandler::interrupt); handlers.forEach(IExecCommandHandler::interrupt);
} }
@Override
public void close() {
log.info("ExecTaskHandler-close id: {}", execCommand.getLogId());
Streams.close(timeoutChecker);
this.handlers.forEach(Streams::close);
}
/** /**
* 执行主机命令 * 执行主机命令
* *
@@ -139,6 +161,7 @@ public class ExecTaskHandler implements IExecTaskHandler {
update.setStatus(statusName); update.setStatus(statusName);
if (ExecStatusEnum.RUNNING.equals(status)) { if (ExecStatusEnum.RUNNING.equals(status)) {
// 执行中 // 执行中
this.startTime = new Date();
update.setStartTime(new Date()); update.setStartTime(new Date());
} else if (ExecStatusEnum.COMPLETED.equals(status)) { } else if (ExecStatusEnum.COMPLETED.equals(status)) {
// 执行完成 // 执行完成
@@ -151,11 +174,30 @@ public class ExecTaskHandler implements IExecTaskHandler {
log.info("ExecTaskHandler-updateStatus finish id: {}, effect: {}", id, effect); log.info("ExecTaskHandler-updateStatus finish id: {}, effect: {}", id, effect);
} }
@Override /**
public void close() { * 检查是否发送消息
log.info("ExecTaskHandler-close id: {}", execCommand.getLogId()); */
Streams.close(timeoutChecker); private void checkSendMessage() {
this.handlers.forEach(Streams::close); // 检查是否执行失败/exitCode
boolean hasError = handlers.stream().anyMatch(s ->
ExecHostStatusEnum.FAILED.equals(s.getStatus())
|| ExecHostStatusEnum.TIMEOUT.equals(s.getStatus())
|| !ExitCode.isSuccess(s.getExitCode()));
if (!hasError) {
return;
}
// 参数
Map<String, Object> params = new HashMap<>();
params.put(ExtraFieldConst.ID, execCommand.getLogId());
params.put(ExtraFieldConst.TIME, Dates.format(this.startTime, Dates.MD_HM));
SystemMessageDTO message = SystemMessageDTO.builder()
.receiverId(execCommand.getUserId())
.receiverUsername(execCommand.getUsername())
.relKey(String.valueOf(execCommand.getLogId()))
.params(params)
.build();
// 发送
systemMessageApi.create(ExecMessageDefine.EXEC_FAILED, message);
} }
} }

View File

@@ -31,6 +31,13 @@ public interface IExecCommandHandler extends Runnable, SafeCloseable {
*/ */
ExecHostStatusEnum getStatus(); ExecHostStatusEnum getStatus();
/**
* 获取退出码
*
* @return exit code
*/
Integer getExitCode();
/** /**
* 获取主机 id * 获取主机 id
* *

View File

@@ -3,10 +3,13 @@ package com.orion.ops.module.asset.handler.host.upload.task;
import com.orion.lang.utils.Threads; import com.orion.lang.utils.Threads;
import com.orion.lang.utils.io.Files1; import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.io.Streams; import com.orion.lang.utils.io.Streams;
import com.orion.lang.utils.time.Dates;
import com.orion.ops.framework.common.constant.Const; import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.constant.ExtraFieldConst;
import com.orion.ops.module.asset.dao.UploadTaskDAO; import com.orion.ops.module.asset.dao.UploadTaskDAO;
import com.orion.ops.module.asset.dao.UploadTaskFileDAO; import com.orion.ops.module.asset.dao.UploadTaskFileDAO;
import com.orion.ops.module.asset.define.AssetThreadPools; import com.orion.ops.module.asset.define.AssetThreadPools;
import com.orion.ops.module.asset.define.message.UploadMessageDefine;
import com.orion.ops.module.asset.entity.domain.UploadTaskDO; import com.orion.ops.module.asset.entity.domain.UploadTaskDO;
import com.orion.ops.module.asset.entity.domain.UploadTaskFileDO; import com.orion.ops.module.asset.entity.domain.UploadTaskFileDO;
import com.orion.ops.module.asset.enums.UploadTaskFileStatusEnum; import com.orion.ops.module.asset.enums.UploadTaskFileStatusEnum;
@@ -16,14 +19,13 @@ import com.orion.ops.module.asset.handler.host.upload.manager.FileUploadTaskMana
import com.orion.ops.module.asset.handler.host.upload.uploader.FileUploader; import com.orion.ops.module.asset.handler.host.upload.uploader.FileUploader;
import com.orion.ops.module.asset.handler.host.upload.uploader.IFileUploader; import com.orion.ops.module.asset.handler.host.upload.uploader.IFileUploader;
import com.orion.ops.module.asset.service.UploadTaskService; import com.orion.ops.module.asset.service.UploadTaskService;
import com.orion.ops.module.infra.api.SystemMessageApi;
import com.orion.ops.module.infra.entity.dto.message.SystemMessageDTO;
import com.orion.spring.SpringHolder; import com.orion.spring.SpringHolder;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList; import java.util.*;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -42,6 +44,8 @@ public class FileUploadTask implements IFileUploadTask {
private static final UploadTaskService uploadTaskService = SpringHolder.getBean(UploadTaskService.class); private static final UploadTaskService uploadTaskService = SpringHolder.getBean(UploadTaskService.class);
private static final SystemMessageApi systemMessageApi = SpringHolder.getBean(SystemMessageApi.class);
private static final FileUploadTaskManager fileUploadTaskManager = SpringHolder.getBean(FileUploadTaskManager.class); private static final FileUploadTaskManager fileUploadTaskManager = SpringHolder.getBean(FileUploadTaskManager.class);
private final Long id; private final Long id;
@@ -91,6 +95,8 @@ public class FileUploadTask implements IFileUploadTask {
} else { } else {
this.updateStatus(UploadTaskStatusEnum.FINISHED); this.updateStatus(UploadTaskStatusEnum.FINISHED);
} }
// 检查是否发送消息
this.checkSendMessage();
// 移除任务 // 移除任务
fileUploadTaskManager.removeTask(id); fileUploadTaskManager.removeTask(id);
// 释放资源 // 释放资源
@@ -187,4 +193,33 @@ public class FileUploadTask implements IFileUploadTask {
uploadTaskDAO.updateById(update); uploadTaskDAO.updateById(update);
} }
/**
* 检查是否发送消息
*/
private void checkSendMessage() {
if (canceled) {
return;
}
// 检查是否上传失败
boolean hasError = uploaderList.stream()
.map(IFileUploader::getFiles)
.flatMap(Collection::stream)
.anyMatch(s -> UploadTaskFileStatusEnum.FAILED.name().equals(s.getStatus()));
if (!hasError) {
return;
}
// 参数
Map<String, Object> params = new HashMap<>();
params.put(ExtraFieldConst.ID, record.getId());
params.put(ExtraFieldConst.TIME, Dates.format(record.getCreateTime(), Dates.MD_HM));
SystemMessageDTO message = SystemMessageDTO.builder()
.receiverId(record.getUserId())
.receiverUsername(record.getUsername())
.relKey(String.valueOf(record.getId()))
.params(params)
.build();
// 发送
systemMessageApi.create(UploadMessageDefine.UPLOAD_FAILED, message);
}
} }

View File

@@ -1,6 +1,8 @@
package com.orion.ops.module.infra.api; package com.orion.ops.module.infra.api;
import com.orion.ops.module.infra.define.SystemMessageDefine;
import com.orion.ops.module.infra.entity.dto.message.SystemMessageCreateDTO; import com.orion.ops.module.infra.entity.dto.message.SystemMessageCreateDTO;
import com.orion.ops.module.infra.entity.dto.message.SystemMessageDTO;
import com.orion.ops.module.infra.enums.MessageClassifyEnum; import com.orion.ops.module.infra.enums.MessageClassifyEnum;
/** /**
@@ -12,6 +14,15 @@ import com.orion.ops.module.infra.enums.MessageClassifyEnum;
*/ */
public interface SystemMessageApi { public interface SystemMessageApi {
/**
* 创建系统消息
*
* @param define define
* @param dto dto
* @return id
*/
Long create(SystemMessageDefine define, SystemMessageDTO dto);
/** /**
* 创建系统消息 * 创建系统消息
* *
@@ -19,6 +30,6 @@ public interface SystemMessageApi {
* @param dto dto * @param dto dto
* @return id * @return id
*/ */
Long createSystemMessage(MessageClassifyEnum classify, SystemMessageCreateDTO dto); Long create(MessageClassifyEnum classify, SystemMessageCreateDTO dto);
} }

View File

@@ -0,0 +1,47 @@
package com.orion.ops.module.infra.define;
import com.orion.lang.utils.Strings;
import com.orion.ops.module.infra.enums.MessageClassifyEnum;
import java.util.Map;
/**
* 系统消息定义
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/5/14 17:06
*/
public interface SystemMessageDefine {
/**
* @return 消息分类
*/
MessageClassifyEnum getClassify();
/**
* @return 消息类型
*/
String getType();
/**
* @return 标题
*/
String getTitle();
/**
* @return 内容
*/
String getContent();
/**
* 格式化内容
*
* @param params params
* @return content
*/
default String formatContent(Map<String, Object> params) {
return Strings.format(this.getContent(), params);
}
}

View File

@@ -27,6 +27,11 @@ public class SystemMessageCreateDTO implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@NotBlank
@Size(max = 10)
@Schema(description = "消息分类")
private String classify;
@NotBlank @NotBlank
@Size(max = 32) @Size(max = 32)
@Schema(description = "消息类型") @Schema(description = "消息类型")

View File

@@ -0,0 +1,46 @@
package com.orion.ops.module.infra.entity.dto.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Map;
/**
* 系统消息 请求业务对象
*
* @author Jiahang Li
* @version 1.0.8
* @since 2024-5-11 16:29
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "SystemMessageDTO", description = "系统消息 请求业务对象")
public class SystemMessageDTO implements Serializable {
private static final long serialVersionUID = 1L;
@NotBlank
@Size(max = 64)
@Schema(description = "消息关联")
private String relKey;
@NotNull
@Schema(description = "接收人id")
private Long receiverId;
@Schema(description = "接收人用户名")
private String receiverUsername;
@Schema(description = "参数")
private Map<String, Object> params;
}

View File

@@ -1,10 +1,11 @@
package com.orion.ops.module.infra.api.impl; package com.orion.ops.module.infra.api.impl;
import com.alibaba.fastjson.JSON;
import com.orion.ops.framework.common.utils.Valid; import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.module.infra.api.SystemMessageApi; import com.orion.ops.module.infra.api.SystemMessageApi;
import com.orion.ops.module.infra.convert.SystemMessageProviderConvert; import com.orion.ops.module.infra.convert.SystemMessageProviderConvert;
import com.orion.ops.module.infra.define.SystemMessageDefine;
import com.orion.ops.module.infra.entity.dto.message.SystemMessageCreateDTO; import com.orion.ops.module.infra.entity.dto.message.SystemMessageCreateDTO;
import com.orion.ops.module.infra.entity.dto.message.SystemMessageDTO;
import com.orion.ops.module.infra.entity.request.message.SystemMessageCreateRequest; import com.orion.ops.module.infra.entity.request.message.SystemMessageCreateRequest;
import com.orion.ops.module.infra.enums.MessageClassifyEnum; import com.orion.ops.module.infra.enums.MessageClassifyEnum;
import com.orion.ops.module.infra.service.SystemMessageService; import com.orion.ops.module.infra.service.SystemMessageService;
@@ -28,12 +29,28 @@ public class SystemMessageApiImpl implements SystemMessageApi {
private SystemMessageService systemMessageService; private SystemMessageService systemMessageService;
@Override @Override
public Long createSystemMessage(MessageClassifyEnum classify, SystemMessageCreateDTO dto) { public Long create(SystemMessageDefine define, SystemMessageDTO dto) {
log.info("SystemMessageApi.createSystemMessage dto: {}", JSON.toJSONString(dto)); Valid.valid(dto);
// 转换
SystemMessageCreateRequest request = SystemMessageCreateRequest.builder()
.classify(define.getClassify().name())
.type(define.getType())
.title(define.getTitle())
.content(define.formatContent(dto.getParams()))
.relKey(dto.getRelKey())
.receiverId(dto.getReceiverId())
.receiverUsername(dto.getReceiverUsername())
.build();
// 创建
return systemMessageService.createSystemMessage(request);
}
@Override
public Long create(MessageClassifyEnum classify, SystemMessageCreateDTO dto) {
dto.setClassify(classify.name());
Valid.valid(dto); Valid.valid(dto);
// 转换 // 转换
SystemMessageCreateRequest request = SystemMessageProviderConvert.MAPPER.toRequest(dto); SystemMessageCreateRequest request = SystemMessageProviderConvert.MAPPER.toRequest(dto);
request.setClassify(classify.name());
// 创建 // 创建
return systemMessageService.createSystemMessage(request); return systemMessageService.createSystemMessage(request);
} }

View File

@@ -1,5 +1,6 @@
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.function.Functions; import com.orion.lang.function.Functions;
import com.orion.lang.utils.Booleans; import com.orion.lang.utils.Booleans;
@@ -44,6 +45,7 @@ public class SystemMessageServiceImpl implements SystemMessageService {
@Override @Override
public Long createSystemMessage(SystemMessageCreateRequest request) { public Long createSystemMessage(SystemMessageCreateRequest request) {
log.info("SystemMessageService.createSystemMessage request: {}", JSON.toJSONString(request));
// 设置接收人用户名 // 设置接收人用户名
if (request.getReceiverUsername() == null) { if (request.getReceiverUsername() == null) {
Optional.ofNullable(request.getReceiverId()) Optional.ofNullable(request.getReceiverId())

View File

@@ -99,7 +99,8 @@
position="br" position="br"
:show-arrow="false" :show-arrow="false"
:popup-style="{ marginLeft: '198px' }" :popup-style="{ marginLeft: '198px' }"
:content-style="{ padding: 0, width: '498px' }"> :content-style="{ padding: 0, width: '428px' }"
@hide="pullHasUnreadMessage">
<div ref="messageRef" class="ref-btn" /> <div ref="messageRef" class="ref-btn" />
<template #content> <template #content>
<message-box /> <message-box />
@@ -310,10 +311,6 @@
// 获取是否有未读的消息 // 获取是否有未读的消息
const pullHasUnreadMessage = () => { const pullHasUnreadMessage = () => {
// 有未读的消息直接返回
if (messageCount.value) {
return;
}
// 查询 // 查询
checkHasUnreadMessage().then(({ data }) => { checkHasUnreadMessage().then(({ data }) => {
messageCount.value = data ? 1 : 0; messageCount.value = data ? 1 : 0;

View File

@@ -26,13 +26,6 @@
checked-text="未读" checked-text="未读"
unchecked-text="全部" unchecked-text="全部"
@change="reloadAllMessage" /> @change="reloadAllMessage" />
<!-- 全部已读 -->
<a-button class="header-button"
type="text"
size="small"
@click="setAllRead">
全部已读
</a-button>
<!-- 清空 --> <!-- 清空 -->
<a-button class="header-button" <a-button class="header-button"
type="text" type="text"
@@ -40,6 +33,13 @@
@click="clearAllMessage"> @click="clearAllMessage">
清空 清空
</a-button> </a-button>
<!-- 全部已读 -->
<a-button class="header-button"
type="text"
size="small"
@click="setAllRead">
全部已读
</a-button>
</a-space> </a-space>
</template> </template>
</a-tabs> </a-tabs>

View File

@@ -6,7 +6,7 @@
<!-- 加载中 --> <!-- 加载中 -->
<a-skeleton class="skeleton-wrapper" :animation="true"> <a-skeleton class="skeleton-wrapper" :animation="true">
<a-skeleton-line :rows="3" <a-skeleton-line :rows="3"
:line-height="86" :line-height="96"
:line-spacing="8" /> :line-spacing="8" />
</a-skeleton> </a-skeleton>
</div> </div>
@@ -56,17 +56,21 @@
</a-button> </a-button>
</div> </div>
</div> </div>
<!-- 文本 --> <!-- 内容 -->
<div v-html="message.contentHtml" <div v-html="message.contentHtml"
class="message-item-content text-ellipsis" class="message-item-content"
:title="message.content" /> :title="message.content" />
<!-- 时间 -->
<div class="message-item-time">
{{ dateFormat(new Date(message.createTime))}}
</div>
</div> </div>
<!-- 加载中 --> <!-- 加载中 -->
<a-skeleton v-if="fetchLoading" <a-skeleton v-if="fetchLoading"
class="skeleton-wrapper" class="skeleton-wrapper"
:animation="true"> :animation="true">
<a-skeleton-line :rows="3" <a-skeleton-line :rows="3"
:line-height="86" :line-height="96"
:line-spacing="8" /> :line-spacing="8" />
</a-skeleton> </a-skeleton>
<!-- 加载更多 --> <!-- 加载更多 -->
@@ -92,6 +96,7 @@
import type { MessageRecordResponse } from '@/api/system/message'; import type { MessageRecordResponse } from '@/api/system/message';
import { MessageStatus, messageTypeKey } from './const'; import { MessageStatus, messageTypeKey } from './const';
import { useDictStore } from '@/store'; import { useDictStore } from '@/store';
import { dateFormat } from '@/utils';
const emits = defineEmits(['load', 'click', 'view', 'delete']); const emits = defineEmits(['load', 'click', 'view', 'delete']);
const props = defineProps<{ const props = defineProps<{
@@ -107,7 +112,6 @@
<style lang="less" scoped> <style lang="less" scoped>
@gap: 8px; @gap: 8px;
@message-height: 86px;
@actions-width: 82px; @actions-width: 82px;
.skeleton-wrapper { .skeleton-wrapper {
@@ -116,7 +120,7 @@
.message-list-container { .message-list-container {
width: 100%; width: 100%;
height: 344px; height: 338px;
display: block; display: block;
.message-list-wrapper { .message-list-wrapper {
@@ -133,17 +137,17 @@
} }
.message-item { .message-item {
height: @message-height; padding: 12px 20px;
padding: 16px 20px;
border-bottom: 1px solid var(--color-neutral-3); border-bottom: 1px solid var(--color-neutral-3);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
font-size: 14px; font-size: 14px;
color: var(--color-text-1);
cursor: pointer; cursor: pointer;
transition: all .2s;
&-title { &-title {
height: 22px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
@@ -151,9 +155,9 @@
&-text { &-text {
width: calc(100% - @actions-width - @gap); width: calc(100% - @actions-width - @gap);
display: block; display: block;
font-size: 15px; font-size: 14px;
font-weight: 600;
text-overflow: clip; text-overflow: clip;
color: var(--color-text-1);
} }
&-status { &-status {
@@ -181,9 +185,17 @@
&-content { &-content {
display: block; display: block;
padding-bottom: 2px; margin-top: 4px;
color: var(--color-text-1); font-size: 12px;
text-overflow: clip; color: var(--color-text-2);
}
&-time {
height: 18px;
margin-top: 4px;
display: block;
font-size: 12px;
color: var(--color-text-2);
} }
} }
@@ -201,7 +213,7 @@
} }
.message-item-read { .message-item-read {
.message-item-title-text, .message-item-title-status, .message-item-content { .message-item-title-text, .message-item-title-status, .message-item-content, .message-item-time {
opacity: .65; opacity: .65;
} }
} }
@@ -210,10 +222,6 @@
position: absolute; position: absolute;
height: 100%; height: 100%;
width: 100%; width: 100%;
.arco-scrollbar-track-direction-horizontal {
display: none;
}
} }
</style> </style>

View File

@@ -3,12 +3,11 @@
title-align="start" title-align="start"
:title="record.title" :title="record.title"
:top="80" :top="80"
:width="720"
:align-center="false" :align-center="false"
:unmount-on-close="true" :unmount-on-close="true"
ok-text="删除" ok-text="删除"
:hide-cancel="true" :hide-cancel="true"
:ok-button-props="{ status: 'danger' }" :ok-button-props="{ status: 'danger', size: 'small' }"
:body-style="{ padding: '20px' }" :body-style="{ padding: '20px' }"
@ok="emits('delete', record)"> @ok="emits('delete', record)">
<div class="content" v-html="record.contentHtml" /> <div class="content" v-html="record.contentHtml" />
@@ -45,5 +44,6 @@
<style lang="less" scoped> <style lang="less" scoped>
.content { .content {
font-size: 16px; font-size: 16px;
color: var(--color-text-2);
} }
</style> </style>