committed by
Gitee
65 changed files with 1177 additions and 728 deletions
@ -0,0 +1,28 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import jakarta.validation.constraints.Size; |
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author Blue |
|||
* @version 1.0 |
|||
* @date 2025-02-23 |
|||
*/ |
|||
@Data |
|||
@Schema(description = "移除群聊成员") |
|||
public class GroupMemberRemoveDTO { |
|||
|
|||
@NotNull(message = "群id不可为空") |
|||
@Schema(description = "群组id") |
|||
private Long groupId; |
|||
|
|||
@Size(max = 50, message = "一次最多只能选择50位用户") |
|||
@NotEmpty(message = "成员用户id不可为空") |
|||
@Schema(description = "成员用户id") |
|||
private List<Long> userIds; |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
package com.bx.implatform.entity; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.TableId; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import lombok.Data; |
|||
|
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* @author Blue |
|||
* @version 1.0 |
|||
*/ |
|||
@Data |
|||
@TableName("im_file_info") |
|||
public class FileInfo { |
|||
|
|||
/** |
|||
* 文件ID |
|||
*/ |
|||
@TableId |
|||
private Long id; |
|||
|
|||
/** |
|||
* 文件名 |
|||
*/ |
|||
private String fileName; |
|||
|
|||
/** |
|||
* 原始文件存储路径 |
|||
*/ |
|||
private String filePath; |
|||
|
|||
/** |
|||
* 压缩文件存储路径 |
|||
*/ |
|||
private String compressedPath; |
|||
|
|||
/** |
|||
* 封面文件路径 |
|||
*/ |
|||
private String coverPath; |
|||
|
|||
/** |
|||
* 原始文件大小(字节) |
|||
*/ |
|||
private Long fileSize; |
|||
|
|||
/** |
|||
* 上传时间 |
|||
*/ |
|||
private Date uploadTime; |
|||
|
|||
/** |
|||
* 文件类型,枚举: FileType |
|||
*/ |
|||
private Integer fileType; |
|||
|
|||
/** |
|||
* 是否永久存储 |
|||
*/ |
|||
private Boolean isPermanent; |
|||
|
|||
/** |
|||
* 文件MD5哈希值 |
|||
*/ |
|||
private String md5; |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
package com.bx.implatform.mapper; |
|||
|
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import com.bx.implatform.entity.FileInfo; |
|||
|
|||
public interface FileInfoMapper extends BaseMapper<FileInfo> { |
|||
|
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
package com.bx.implatform.service; |
|||
|
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
import com.bx.implatform.entity.FileInfo; |
|||
import com.bx.implatform.vo.UploadImageVO; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
public interface FileService extends IService<FileInfo> { |
|||
|
|||
String uploadFile(MultipartFile file); |
|||
|
|||
UploadImageVO uploadImage(MultipartFile file,Boolean isPermanent); |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,191 @@ |
|||
package com.bx.implatform.service; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|||
import com.bx.implatform.config.props.MinioProperties; |
|||
import com.bx.implatform.contant.Constant; |
|||
import com.bx.implatform.entity.FileInfo; |
|||
import com.bx.implatform.enums.FileType; |
|||
import com.bx.implatform.enums.ResultCode; |
|||
import com.bx.implatform.exception.GlobalException; |
|||
import com.bx.implatform.mapper.FileInfoMapper; |
|||
import com.bx.implatform.session.SessionContext; |
|||
import com.bx.implatform.thirdparty.MinioService; |
|||
import com.bx.implatform.util.FileUtil; |
|||
import com.bx.implatform.util.ImageUtil; |
|||
import com.bx.implatform.vo.UploadImageVO; |
|||
import jakarta.annotation.PostConstruct; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
import org.springframework.util.DigestUtils; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
import java.io.IOException; |
|||
import java.util.Date; |
|||
import java.util.Objects; |
|||
|
|||
/** |
|||
* 文件上传服务 |
|||
* |
|||
* author: Blue date: 2024-09-28 version: 1.0 |
|||
*/ |
|||
@Slf4j |
|||
@Service |
|||
@RequiredArgsConstructor |
|||
public class FileServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements FileService { |
|||
|
|||
private final MinioService minioSerivce; |
|||
|
|||
private final MinioProperties minioProps; |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
if (!minioSerivce.bucketExists(minioProps.getBucketName())) { |
|||
// 创建bucket
|
|||
minioSerivce.makeBucket(minioProps.getBucketName()); |
|||
// 公开bucket
|
|||
minioSerivce.setBucketPublic(minioProps.getBucketName()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public String uploadFile(MultipartFile file) { |
|||
try { |
|||
Long userId = SessionContext.getSession().getUserId(); |
|||
// 大小校验
|
|||
if (file.getSize() > Constant.MAX_FILE_SIZE) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "文件大小不能超过20M"); |
|||
} |
|||
// 如果文件已存在,直接复用
|
|||
String md5 = DigestUtils.md5DigestAsHex(file.getInputStream()); |
|||
FileInfo fileInfo = findByMd5(md5); |
|||
if (!Objects.isNull(fileInfo)) { |
|||
// 更新上传时间
|
|||
fileInfo.setUploadTime(new Date()); |
|||
this.updateById(fileInfo); |
|||
// 返回
|
|||
return fileInfo.getFilePath(); |
|||
} |
|||
// 上传
|
|||
String fileName = minioSerivce.upload(minioProps.getBucketName(), minioProps.getFilePath(), file); |
|||
if (StringUtils.isEmpty(fileName)) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "文件上传失败"); |
|||
} |
|||
String url = generUrl(FileType.FILE, fileName); |
|||
// 保存文件
|
|||
saveFileInfo(file, md5, url); |
|||
log.info("文件文件成功,用户id:{},url:{}", userId, url); |
|||
return url; |
|||
} catch (IOException e) { |
|||
log.error("上传图片失败,{}", e.getMessage(), e); |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "上传图片失败"); |
|||
} |
|||
} |
|||
|
|||
@Transactional |
|||
@Override |
|||
public UploadImageVO uploadImage(MultipartFile file, Boolean isPermanent) { |
|||
try { |
|||
Long userId = SessionContext.getSession().getUserId(); |
|||
// 大小校验
|
|||
if (file.getSize() > Constant.MAX_IMAGE_SIZE) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片大小不能超过20M"); |
|||
} |
|||
// 图片格式校验
|
|||
if (!FileUtil.isImage(file.getOriginalFilename())) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片格式不合法"); |
|||
} |
|||
UploadImageVO vo = new UploadImageVO(); |
|||
// 如果文件已存在,直接复用
|
|||
String md5 = DigestUtils.md5DigestAsHex(file.getInputStream()); |
|||
FileInfo fileInfo = findByMd5(md5); |
|||
if (!Objects.isNull(fileInfo)) { |
|||
// 更新上传时间和持久化标记
|
|||
fileInfo.setIsPermanent(isPermanent || fileInfo.getIsPermanent()); |
|||
fileInfo.setUploadTime(new Date()); |
|||
this.updateById(fileInfo); |
|||
// 返回
|
|||
vo.setOriginUrl(fileInfo.getFilePath()); |
|||
vo.setThumbUrl(fileInfo.getCompressedPath()); |
|||
return vo; |
|||
} |
|||
// 上传原图
|
|||
String fileName = minioSerivce.upload(minioProps.getBucketName(), minioProps.getImagePath(), file); |
|||
if (StringUtils.isEmpty(fileName)) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败"); |
|||
} |
|||
vo.setOriginUrl(generUrl(FileType.IMAGE, fileName)); |
|||
if (file.getSize() > 50 * 1024) { |
|||
// 大于50K的文件需上传缩略图
|
|||
byte[] imageByte = ImageUtil.compressForScale(file.getBytes(), 30); |
|||
String thumbFileName = minioSerivce.upload(minioProps.getBucketName(), minioProps.getImagePath(), |
|||
file.getOriginalFilename(), imageByte, file.getContentType()); |
|||
if (StringUtils.isEmpty(thumbFileName)) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败"); |
|||
} |
|||
vo.setThumbUrl(generUrl(FileType.IMAGE, thumbFileName)); |
|||
// 保存文件信息
|
|||
saveImageFileInfo(file, md5, vo.getOriginUrl(), vo.getThumbUrl(), isPermanent); |
|||
}else{ |
|||
// 小于50k,用原图充当缩略图
|
|||
vo.setThumbUrl(generUrl(FileType.IMAGE, fileName)); |
|||
// 保存文件信息,由于缩略图不允许删除,此时原图也不允许删除
|
|||
saveImageFileInfo(file, md5, vo.getOriginUrl(), vo.getThumbUrl(), true); |
|||
} |
|||
log.info("文件图片成功,用户id:{},url:{}", userId, vo.getOriginUrl()); |
|||
return vo; |
|||
} catch (IOException e) { |
|||
log.error("上传图片失败,{}", e.getMessage(), e); |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败"); |
|||
} |
|||
} |
|||
|
|||
private String generUrl(FileType fileType, String fileName) { |
|||
return StrUtil.join("/", minioProps.getDomain(), minioProps.getBucketName(), getBucketPath(fileType), fileName); |
|||
} |
|||
|
|||
private String getBucketPath(FileType fileType) { |
|||
return switch (fileType) { |
|||
case FILE -> minioProps.getFilePath(); |
|||
case IMAGE -> minioProps.getImagePath(); |
|||
case VIDEO -> minioProps.getVideoPath(); |
|||
}; |
|||
} |
|||
|
|||
private FileInfo findByMd5(String md5) { |
|||
LambdaQueryWrapper<FileInfo> wrapper = Wrappers.lambdaQuery(); |
|||
wrapper.eq(FileInfo::getMd5, md5); |
|||
return getOne(wrapper); |
|||
} |
|||
|
|||
private void saveImageFileInfo(MultipartFile file, String md5, String filePath, String compressedPath, |
|||
Boolean isPermanent) throws IOException { |
|||
FileInfo fileInfo = new FileInfo(); |
|||
fileInfo.setFileName(file.getOriginalFilename()); |
|||
fileInfo.setFileSize(file.getSize()); |
|||
fileInfo.setFileType(FileType.IMAGE.code()); |
|||
fileInfo.setFilePath(filePath); |
|||
fileInfo.setCompressedPath(compressedPath); |
|||
fileInfo.setMd5(md5); |
|||
fileInfo.setIsPermanent(isPermanent); |
|||
fileInfo.setUploadTime(new Date()); |
|||
this.save(fileInfo); |
|||
} |
|||
|
|||
private void saveFileInfo(MultipartFile file, String md5, String filePath) throws IOException { |
|||
FileInfo fileInfo = new FileInfo(); |
|||
fileInfo.setFileName(file.getOriginalFilename()); |
|||
fileInfo.setFileSize(file.getSize()); |
|||
fileInfo.setFileType(FileType.FILE.code()); |
|||
fileInfo.setFilePath(filePath); |
|||
fileInfo.setMd5(md5); |
|||
fileInfo.setIsPermanent(false); |
|||
fileInfo.setUploadTime(new Date()); |
|||
this.save(fileInfo); |
|||
} |
|||
|
|||
} |
|||
@ -1,121 +0,0 @@ |
|||
package com.bx.implatform.service.thirdparty; |
|||
|
|||
import com.bx.implatform.config.props.MinioProperties; |
|||
import com.bx.implatform.contant.Constant; |
|||
import com.bx.implatform.enums.FileType; |
|||
import com.bx.implatform.enums.ResultCode; |
|||
import com.bx.implatform.exception.GlobalException; |
|||
import com.bx.implatform.session.SessionContext; |
|||
import com.bx.implatform.util.FileUtil; |
|||
import com.bx.implatform.util.ImageUtil; |
|||
import com.bx.implatform.util.MinioUtil; |
|||
import com.bx.implatform.vo.UploadImageVO; |
|||
import jakarta.annotation.PostConstruct; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
import java.io.IOException; |
|||
import java.util.Objects; |
|||
|
|||
/** |
|||
* todo 通过校验文件MD5实现重复文件秒传 |
|||
* 文件上传服务 |
|||
* |
|||
* @author Blue |
|||
* @date 2022/10/28 |
|||
*/ |
|||
@Slf4j |
|||
@Service |
|||
@RequiredArgsConstructor |
|||
public class FileService { |
|||
private final MinioUtil minioUtil; |
|||
|
|||
private final MinioProperties minioProps; |
|||
|
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
if (!minioUtil.bucketExists(minioProps.getBucketName())) { |
|||
// 创建bucket
|
|||
minioUtil.makeBucket(minioProps.getBucketName()); |
|||
// 公开bucket
|
|||
minioUtil.setBucketPublic(minioProps.getBucketName()); |
|||
} |
|||
} |
|||
|
|||
|
|||
public String uploadFile(MultipartFile file) { |
|||
Long userId = SessionContext.getSession().getUserId(); |
|||
// 大小校验
|
|||
if (file.getSize() > Constant.MAX_FILE_SIZE) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "文件大小不能超过20M"); |
|||
} |
|||
// 上传
|
|||
String fileName = minioUtil.upload(minioProps.getBucketName(), minioProps.getFilePath(), file); |
|||
if (StringUtils.isEmpty(fileName)) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "文件上传失败"); |
|||
} |
|||
String url = generUrl(FileType.FILE, fileName); |
|||
log.info("文件文件成功,用户id:{},url:{}", userId, url); |
|||
return url; |
|||
} |
|||
|
|||
public UploadImageVO uploadImage(MultipartFile file) { |
|||
try { |
|||
Long userId = SessionContext.getSession().getUserId(); |
|||
// 大小校验
|
|||
if (file.getSize() > Constant.MAX_IMAGE_SIZE) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片大小不能超过20M"); |
|||
} |
|||
// 图片格式校验
|
|||
if (!FileUtil.isImage(file.getOriginalFilename())) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片格式不合法"); |
|||
} |
|||
// 上传原图
|
|||
UploadImageVO vo = new UploadImageVO(); |
|||
String fileName = minioUtil.upload(minioProps.getBucketName(), minioProps.getImagePath(), file); |
|||
if (StringUtils.isEmpty(fileName)) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败"); |
|||
} |
|||
vo.setOriginUrl(generUrl(FileType.IMAGE, fileName)); |
|||
// 大于30K的文件需上传缩略图
|
|||
if (file.getSize() > 30 * 1024) { |
|||
byte[] imageByte = ImageUtil.compressForScale(file.getBytes(), 30); |
|||
fileName = minioUtil.upload(minioProps.getBucketName(), minioProps.getImagePath(), Objects.requireNonNull(file.getOriginalFilename()), imageByte, file.getContentType()); |
|||
if (StringUtils.isEmpty(fileName)) { |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败"); |
|||
} |
|||
} |
|||
vo.setThumbUrl(generUrl(FileType.IMAGE, fileName)); |
|||
log.info("文件图片成功,用户id:{},url:{}", userId, vo.getOriginUrl()); |
|||
return vo; |
|||
} catch (IOException e) { |
|||
log.error("上传图片失败,{}", e.getMessage(), e); |
|||
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败"); |
|||
} |
|||
} |
|||
|
|||
|
|||
public String generUrl(FileType fileTypeEnum, String fileName) { |
|||
String url = minioProps.getDomain() + "/" + minioProps.getBucketName(); |
|||
switch (fileTypeEnum) { |
|||
case FILE: |
|||
url += "/" + minioProps.getFilePath() + "/"; |
|||
break; |
|||
case IMAGE: |
|||
url += "/" + minioProps.getImagePath() + "/"; |
|||
break; |
|||
case VIDEO: |
|||
url += "/" + minioProps.getVideoPath() + "/"; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
url += fileName; |
|||
return url; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
package com.bx.implatform.task.schedule; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
|||
import com.bx.implatform.annotation.RedisLock; |
|||
import com.bx.implatform.config.props.MinioProperties; |
|||
import com.bx.implatform.contant.RedisKey; |
|||
import com.bx.implatform.entity.FileInfo; |
|||
import com.bx.implatform.service.FileService; |
|||
import com.bx.implatform.thirdparty.MinioService; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.time.DateUtils; |
|||
import org.springframework.scheduling.annotation.Scheduled; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.Date; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 过期文件清理任务 |
|||
* |
|||
* @author Blue |
|||
* @version 1.0 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class FileExpireTask { |
|||
|
|||
private final FileService fileService; |
|||
private final MinioService minioService; |
|||
private final MinioProperties minioProps; |
|||
|
|||
@RedisLock(prefixKey = RedisKey.IM_LOCK_FILE_TASK) |
|||
@Scheduled(cron = "0 * * * * ?") |
|||
public void run() { |
|||
log.info("【定时任务】过期文件处理..."); |
|||
int batchSize = 100; |
|||
List<FileInfo> files = loadBatch(batchSize); |
|||
while (true) { |
|||
for (FileInfo fileInfo : files) { |
|||
String url = fileInfo.getFilePath(); |
|||
String relativePath = url.substring(fileInfo.getFilePath().indexOf(minioProps.getBucketName())); |
|||
String[] arr = relativePath.split("/"); |
|||
String bucket = minioProps.getBucketName(); |
|||
String path = arr[1]; |
|||
String fileNme = StrUtil.join("/", arr[2], arr[3]); |
|||
if (minioService.isExist(bucket, path, fileNme)) { |
|||
if (!minioService.remove(bucket, path, fileNme)) { |
|||
// 删除失败,不再往下执行
|
|||
log.error("删除过期文件异常, id:{},文件名:{}", fileInfo.getId(), fileInfo.getFileName()); |
|||
return; |
|||
} |
|||
// 删除文件信息
|
|||
fileService.removeById(fileInfo.getId()); |
|||
} |
|||
} |
|||
if (files.size() < batchSize) { |
|||
break; |
|||
} |
|||
// 下一批
|
|||
files = loadBatch(batchSize); |
|||
} |
|||
} |
|||
|
|||
List<FileInfo> loadBatch(int size) { |
|||
Date minDate = DateUtils.addDays(new Date(), -minioProps.getExpireIn()); |
|||
LambdaQueryWrapper<FileInfo> wrapper = Wrappers.lambdaQuery(); |
|||
wrapper.eq(FileInfo::getIsPermanent, false); |
|||
wrapper.le(FileInfo::getUploadTime, minDate); |
|||
wrapper.orderByAsc(FileInfo::getId); |
|||
wrapper.last("limit " + size); |
|||
return fileService.list(wrapper); |
|||
} |
|||
|
|||
} |
|||
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 22 KiB |
Binary file not shown.
Binary file not shown.
@ -1,60 +1,64 @@ |
|||
<template> |
|||
<div class="chat-group-member" :class="active ? 'active' : ''" :style="{ 'height': height + 'px' }"> |
|||
<div class="member-avatar"> |
|||
<head-image :size="headImageSize" :name="member.showNickName" :url="member.headImage"> </head-image> |
|||
</div> |
|||
<div class="member-name" :style="{ 'line-height': height + 'px' }"> |
|||
<div>{{ member.showNickName }}</div> |
|||
</div> |
|||
</div> |
|||
<div class="chat-group-member" :class="active ? 'active' : ''" :style="{ 'height': height + 'px' }"> |
|||
<div class="member-avatar"> |
|||
<head-image :size="headImageSize" :name="member.showNickName" :url="member.headImage"> </head-image> |
|||
</div> |
|||
<div class="member-name" :style="{ 'line-height': height + 'px' }"> |
|||
<div>{{ member.showNickName }}</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import HeadImage from "../common/HeadImage.vue"; |
|||
export default { |
|||
name: "groupMember", |
|||
components: { HeadImage }, |
|||
data() { |
|||
return {}; |
|||
}, |
|||
props: { |
|||
member: { |
|||
type: Object, |
|||
required: true |
|||
}, |
|||
height: { |
|||
type: Number, |
|||
default: 50 |
|||
}, |
|||
active: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
}, |
|||
computed: { |
|||
headImageSize() { |
|||
return Math.ceil(this.height * 0.75) |
|||
} |
|||
} |
|||
name: "groupMember", |
|||
components: { HeadImage }, |
|||
data() { |
|||
return {}; |
|||
}, |
|||
props: { |
|||
member: { |
|||
type: Object, |
|||
required: true |
|||
}, |
|||
height: { |
|||
type: Number, |
|||
default: 50 |
|||
}, |
|||
active: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
}, |
|||
computed: { |
|||
headImageSize() { |
|||
return Math.ceil(this.height * 0.75) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.chat-group-member { |
|||
display: flex; |
|||
position: relative; |
|||
padding: 0 5px; |
|||
align-items: center; |
|||
white-space: nowrap; |
|||
box-sizing: border-box; |
|||
display: flex; |
|||
position: relative; |
|||
padding: 0 5px; |
|||
align-items: center; |
|||
white-space: nowrap; |
|||
box-sizing: border-box; |
|||
|
|||
.member-name { |
|||
padding-left: 10px; |
|||
height: 100%; |
|||
text-align: left; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
font-size: var(--im-font-size); |
|||
} |
|||
&.active { |
|||
background: #E1EAF7; |
|||
} |
|||
|
|||
.member-name { |
|||
padding-left: 10px; |
|||
height: 100%; |
|||
text-align: left; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
font-size: var(--im-font-size); |
|||
} |
|||
} |
|||
</style> |
|||
</style> |
|||
@ -1,140 +1,141 @@ |
|||
<template> |
|||
<el-dialog class="setting" title="设置" :visible.sync="visible" width="420px" :before-close="onClose"> |
|||
<el-form :model="userInfo" label-width="80px" :rules="rules" ref="settingForm" size="small"> |
|||
<el-form-item label="头像" style="margin-bottom: 0 !important;"> |
|||
<file-upload class="avatar-uploader" :action="imageAction" :showLoading="true" :maxSize="maxSize" |
|||
@success="onUploadSuccess" :fileTypes="['image/jpeg', 'image/png', 'image/jpg', 'image/webp']"> |
|||
<img v-if="userInfo.headImage" :src="userInfo.headImage" class="avatar"> |
|||
<i v-else class="el-icon-plus avatar-uploader-icon"></i> |
|||
</file-upload> |
|||
</el-form-item> |
|||
<el-form-item label="用户名"> |
|||
<el-input disabled v-model="userInfo.userName" autocomplete="off" size="small"></el-input> |
|||
</el-form-item> |
|||
<el-form-item prop="nickName" label="昵称"> |
|||
<el-input v-model="userInfo.nickName" autocomplete="off" size="small"></el-input> |
|||
</el-form-item> |
|||
<el-form-item label="性别"> |
|||
<el-radio-group v-model="userInfo.sex"> |
|||
<el-radio :label="0">男</el-radio> |
|||
<el-radio :label="1">女</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="个性签名"> |
|||
<el-input type="textarea" v-model="userInfo.signature" :rows="3" maxlength="64"></el-input> |
|||
</el-form-item> |
|||
</el-form> |
|||
<el-dialog class="setting" title="设置" :visible.sync="visible" width="420px" :before-close="onClose"> |
|||
<el-form :model="userInfo" label-width="80px" :rules="rules" ref="settingForm" size="small"> |
|||
<el-form-item label="头像" style="margin-bottom: 0 !important;"> |
|||
<file-upload class="avatar-uploader" :action="imageAction" :showLoading="true" :maxSize="maxSize" |
|||
:isPermanent="true" @success="onUploadSuccess" |
|||
:fileTypes="['image/jpeg', 'image/png', 'image/jpg', 'image/webp']"> |
|||
<img v-if="userInfo.headImage" :src="userInfo.headImage" class="avatar"> |
|||
<i v-else class="el-icon-plus avatar-uploader-icon"></i> |
|||
</file-upload> |
|||
</el-form-item> |
|||
<el-form-item label="用户名"> |
|||
<el-input disabled v-model="userInfo.userName" autocomplete="off" size="small"></el-input> |
|||
</el-form-item> |
|||
<el-form-item prop="nickName" label="昵称"> |
|||
<el-input v-model="userInfo.nickName" autocomplete="off" size="small"></el-input> |
|||
</el-form-item> |
|||
<el-form-item label="性别"> |
|||
<el-radio-group v-model="userInfo.sex"> |
|||
<el-radio :label="0">男</el-radio> |
|||
<el-radio :label="1">女</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="个性签名"> |
|||
<el-input type="textarea" v-model="userInfo.signature" :rows="3" maxlength="64"></el-input> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<span slot="footer" class="dialog-footer"> |
|||
<el-button @click="onClose()">取 消</el-button> |
|||
<el-button type="primary" @click="onSubmit()">确 定</el-button> |
|||
</span> |
|||
</el-dialog> |
|||
<span slot="footer" class="dialog-footer"> |
|||
<el-button @click="onClose()">取 消</el-button> |
|||
<el-button type="primary" @click="onSubmit()">确 定</el-button> |
|||
</span> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<script> |
|||
import FileUpload from "../common/FileUpload.vue"; |
|||
|
|||
export default { |
|||
name: "setting", |
|||
components: { |
|||
FileUpload |
|||
}, |
|||
data() { |
|||
return { |
|||
userInfo: {}, |
|||
maxSize: 5 * 1024 * 1024, |
|||
action: "/image/upload", |
|||
rules: { |
|||
nickName: [{ |
|||
required: true, |
|||
message: '请输入昵称', |
|||
trigger: 'blur' |
|||
}] |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
name: "setting", |
|||
components: { |
|||
FileUpload |
|||
}, |
|||
data() { |
|||
return { |
|||
userInfo: {}, |
|||
maxSize: 5 * 1024 * 1024, |
|||
action: "/image/upload", |
|||
rules: { |
|||
nickName: [{ |
|||
required: true, |
|||
message: '请输入昵称', |
|||
trigger: 'blur' |
|||
}] |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
|
|||
onClose() { |
|||
this.$emit("close"); |
|||
}, |
|||
onSubmit() { |
|||
this.$refs['settingForm'].validate((valid) => { |
|||
if (!valid) { |
|||
return false; |
|||
} |
|||
this.$http({ |
|||
url: "/user/update", |
|||
method: "put", |
|||
data: this.userInfo |
|||
}).then(() => { |
|||
this.$store.commit("setUserInfo", this.userInfo); |
|||
this.$emit("close"); |
|||
this.$message.success("修改成功"); |
|||
}) |
|||
}); |
|||
}, |
|||
onUploadSuccess(data, file) { |
|||
this.userInfo.headImage = data.originUrl; |
|||
this.userInfo.headImageThumb = data.thumbUrl; |
|||
} |
|||
}, |
|||
props: { |
|||
visible: { |
|||
type: Boolean |
|||
} |
|||
}, |
|||
computed: { |
|||
imageAction() { |
|||
return `/image/upload`; |
|||
} |
|||
}, |
|||
watch: { |
|||
visible: function (newData, oldData) { |
|||
// 深拷贝 |
|||
let mine = this.$store.state.userStore.userInfo; |
|||
this.userInfo = JSON.parse(JSON.stringify(mine)); |
|||
} |
|||
} |
|||
onClose() { |
|||
this.$emit("close"); |
|||
}, |
|||
onSubmit() { |
|||
this.$refs['settingForm'].validate((valid) => { |
|||
if (!valid) { |
|||
return false; |
|||
} |
|||
this.$http({ |
|||
url: "/user/update", |
|||
method: "put", |
|||
data: this.userInfo |
|||
}).then(() => { |
|||
this.$store.commit("setUserInfo", this.userInfo); |
|||
this.$emit("close"); |
|||
this.$message.success("修改成功"); |
|||
}) |
|||
}); |
|||
}, |
|||
onUploadSuccess(data, file) { |
|||
this.userInfo.headImage = data.originUrl; |
|||
this.userInfo.headImageThumb = data.thumbUrl; |
|||
} |
|||
}, |
|||
props: { |
|||
visible: { |
|||
type: Boolean |
|||
} |
|||
}, |
|||
computed: { |
|||
imageAction() { |
|||
return `/image/upload`; |
|||
} |
|||
}, |
|||
watch: { |
|||
visible: function(newData, oldData) { |
|||
// 深拷贝 |
|||
let mine = this.$store.state.userStore.userInfo; |
|||
this.userInfo = JSON.parse(JSON.stringify(mine)); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.setting { |
|||
.el-form { |
|||
padding: 10px 0 0 10px; |
|||
} |
|||
.el-form { |
|||
padding: 10px 0 0 10px; |
|||
} |
|||
|
|||
.avatar-uploader { |
|||
--width: 112px; |
|||
.avatar-uploader { |
|||
--width: 112px; |
|||
|
|||
.el-upload { |
|||
border: 1px dashed #d9d9d9 !important; |
|||
border-radius: 6px; |
|||
cursor: pointer; |
|||
position: relative; |
|||
overflow: hidden; |
|||
} |
|||
.el-upload { |
|||
border: 1px dashed #d9d9d9 !important; |
|||
border-radius: 6px; |
|||
cursor: pointer; |
|||
position: relative; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.el-upload:hover { |
|||
border-color: #409EFF; |
|||
} |
|||
.el-upload:hover { |
|||
border-color: #409EFF; |
|||
} |
|||
|
|||
.avatar-uploader-icon { |
|||
font-size: 24px; |
|||
color: #8c939d; |
|||
width: var(--width); |
|||
height: var(--width); |
|||
line-height: var(--width); |
|||
text-align: center; |
|||
} |
|||
.avatar-uploader-icon { |
|||
font-size: 24px; |
|||
color: #8c939d; |
|||
width: var(--width); |
|||
height: var(--width); |
|||
line-height: var(--width); |
|||
text-align: center; |
|||
} |
|||
|
|||
.avatar { |
|||
width: var(--width); |
|||
height: var(--width); |
|||
display: block; |
|||
} |
|||
} |
|||
.avatar { |
|||
width: var(--width); |
|||
height: var(--width); |
|||
display: block; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
</style> |
|||
Loading…
Reference in new issue