mirror of
				https://gitee.com/lab1024/smart-admin.git
				synced 2025-11-04 18:33:43 +08:00 
			
		
		
		
	v3.28.0 【优化】优化 Long、BigInteger、BigDecimal 的 JSON 序列化【优化】个人中心样式【优化】Spin加载
This commit is contained in:
		@@ -1,13 +1,14 @@
 | 
			
		||||
package net.lab1024.sa.base.common.json.serializer;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.core.JsonGenerator;
 | 
			
		||||
import com.fasterxml.jackson.core.JsonProcessingException;
 | 
			
		||||
import com.fasterxml.jackson.databind.JsonSerializer;
 | 
			
		||||
import com.fasterxml.jackson.databind.SerializerProvider;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Long类型序列化 超出 JS 最大最小值 处理
 | 
			
		||||
 * Long类型序列化
 | 
			
		||||
 *
 | 
			
		||||
 * @Author 1024创新实验室-主任: 卓大
 | 
			
		||||
 * @Date 2020-06-02 22:55:07
 | 
			
		||||
@@ -26,8 +27,13 @@ public class LongJsonSerializer extends JsonSerializer<Long> {
 | 
			
		||||
    private static final long JS_MIN_SAFE_INTEGER = -9007199254740991L;
 | 
			
		||||
    private static final long JS_MAX_SAFE_INTEGER = 9007199254740991L;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void serialize(Long value, JsonGenerator gen, SerializerProvider provider) throws IOException {
 | 
			
		||||
    public void serialize(Long value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
 | 
			
		||||
        if (null == value) {
 | 
			
		||||
            gen.writeNull();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // 如果超出了 JavaScript 安全整数范围,则序列化为字符串
 | 
			
		||||
        if (value < JS_MIN_SAFE_INTEGER || value > JS_MAX_SAFE_INTEGER) {
 | 
			
		||||
            gen.writeString(Long.toString(value));
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ import org.springframework.data.redis.cache.RedisCacheManager;
 | 
			
		||||
import org.springframework.data.redis.connection.RedisConnection;
 | 
			
		||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
 | 
			
		||||
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
@@ -54,7 +53,7 @@ public class RedisCacheServiceImpl implements CacheService {
 | 
			
		||||
 | 
			
		||||
        if (keys != null) {
 | 
			
		||||
            return keys.stream().map(key -> {
 | 
			
		||||
                String redisKey = StrUtil.str(key, StandardCharsets.UTF_8);
 | 
			
		||||
                String redisKey = StrUtil.str(key, "utf-8");
 | 
			
		||||
                // 从 Redis 键中提取出最后一个冒号后面的字符串作为真正的键
 | 
			
		||||
                return redisKey.substring(redisKey.lastIndexOf(":") + 1);
 | 
			
		||||
            }).collect(Collectors.toList());
 | 
			
		||||
 
 | 
			
		||||
@@ -1,144 +0,0 @@
 | 
			
		||||
package net.lab1024.sa.base.module.support.cache.manager;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.boot.convert.DurationStyle;
 | 
			
		||||
import org.springframework.data.redis.cache.*;
 | 
			
		||||
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
 | 
			
		||||
import static net.lab1024.sa.base.common.constant.StringConst.COLON;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 自定义 RedisCacheManager,支持在 cacheName 中通过 '#' 指定 TTL(过期时间)。
 | 
			
		||||
 * @Author CoderKK
 | 
			
		||||
 * @Date 2025-08-15 13:01:01
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 支持格式:{@code cacheName#ttl},其中 ttl 支持 Spring 的 Duration 格式。
 | 
			
		||||
 * 特殊值:{@code -1} 表示永久缓存(永不过期)。
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * <h3>使用示例:</h3>
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * // 10 秒后过期
 | 
			
		||||
 * @Cacheable(value = "user#10s", key = "#id")
 | 
			
		||||
 * // 2 小时后过期
 | 
			
		||||
 * @Cacheable(value = "report#2h", key = "#date")
 | 
			
		||||
 * // 30 分钟后过期
 | 
			
		||||
 * @Cacheable(value = "session#30m", key = "#token")
 | 
			
		||||
 * // 永不过期(永久缓存),适用于极少变化的配置数据
 | 
			
		||||
 * @Cacheable(value = "appConfig#-1", key = "'globalSettings'")
 | 
			
		||||
 * // 无 TTL,使用全局默认过期时间(如 7 天)
 | 
			
		||||
 * @Cacheable(value = "product", key = "#productId")
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * <h3>生成的 Redis Key 格式:</h3>
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * cache:cacheName:key
 | 
			
		||||
 * 例如:cache:user:123
 | 
			
		||||
 *      cache:appConfig:globalSettings
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * <h3>支持的 TTL 单位:</h3>
 | 
			
		||||
 * <ul>
 | 
			
		||||
 *   <li>{@code ms} / {@code millis} / {@code milliseconds} - 毫秒</li>
 | 
			
		||||
 *   <li>{@code s} / {@code secs} / {@code seconds} - 秒</li>
 | 
			
		||||
 *   <li>{@code m} / {@code mins} / {@code minutes} - 分钟</li>
 | 
			
		||||
 *   <li>{@code h} / {@code hrs} / {@code hours} - 小时</li>
 | 
			
		||||
 *   <li>{@code d} / {@code days} - 天</li>
 | 
			
		||||
 * </ul>
 | 
			
		||||
 *
 | 
			
		||||
 * <h3>注意事项:</h3>
 | 
			
		||||
 * <ul>
 | 
			
		||||
 *   <li>不写单位默认为毫秒</li>
 | 
			
		||||
 *   <li>永久缓存(#-1)不会自动过期,请配合 @CacheEvict 手动清理。</li>
 | 
			
		||||
 *   <li>避免对频繁更新的数据使用永久缓存,防止数据陈旧。</li>
 | 
			
		||||
 *   <li>cacheName 中的 '#' 只解析第一个,后续字符将作为 TTL 处理。</li>
 | 
			
		||||
 * </ul>
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class CustomRedisCacheManager extends RedisCacheManager {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存全局前缀
 | 
			
		||||
     */
 | 
			
		||||
    private static final String CACHE_PREFIX = "cache";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 自定义 TTL 分隔符,用于在 cacheName 后附加过期时间
 | 
			
		||||
     */
 | 
			
		||||
    private static final String CUSTOM_TTL_SEPARATOR = "#";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 默认缓存过期时间:7 天
 | 
			
		||||
     */
 | 
			
		||||
    private static final Duration DEFAULT_TTL = Duration.ofDays(7);
 | 
			
		||||
 | 
			
		||||
    public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
 | 
			
		||||
        super(cacheWriter, defaultCacheConfiguration);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建 RedisCache 实例,支持从 cacheName 解析 TTL
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        缓存名称(支持 name#ttl 格式)
 | 
			
		||||
     * @param cacheConfig 默认缓存配置
 | 
			
		||||
     * @return RedisCache
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
 | 
			
		||||
        Duration ttl = parseTtlFromCacheName(name);
 | 
			
		||||
        if (ttl == null) {
 | 
			
		||||
            ttl = DEFAULT_TTL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CacheKeyPrefix keyPrefix = cacheName -> {
 | 
			
		||||
            if (StrUtil.isBlank(cacheName)) {
 | 
			
		||||
                return CACHE_PREFIX + COLON;
 | 
			
		||||
            }
 | 
			
		||||
            String[] parts = cacheName.split(CUSTOM_TTL_SEPARATOR, 2);
 | 
			
		||||
            String cleanName = StrUtil.trim(parts[0]);
 | 
			
		||||
            return CACHE_PREFIX + COLON + cleanName + COLON;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // 构建最终缓存配置:设置 key 前缀 + TTL
 | 
			
		||||
        RedisCacheConfiguration config = cacheConfig.computePrefixWith(keyPrefix).entryTtl(ttl);
 | 
			
		||||
 | 
			
		||||
        return super.createRedisCache(name, config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 从 cacheName 中解析 TTL
 | 
			
		||||
     *
 | 
			
		||||
     * @param name 缓存名称,格式如:users#10m, products#2h, config#-1(永久)
 | 
			
		||||
     * @return 解析出的 Duration若无效则返回 null;若为 -1,则返回 Duration.ofMillis(-1) 表示永久缓存
 | 
			
		||||
     */
 | 
			
		||||
    private Duration parseTtlFromCacheName(String name) {
 | 
			
		||||
        if (StrUtil.isBlank(name)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String[] parts = name.split(CUSTOM_TTL_SEPARATOR, 2);
 | 
			
		||||
        if (parts.length < 2) {
 | 
			
		||||
            return null; // 无 TTL 部分
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String ttlStr = StrUtil.trim(parts[1]);
 | 
			
		||||
        if (StrUtil.isBlank(ttlStr)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 特殊处理:-1 表示永久缓存
 | 
			
		||||
        if ("-1".equals(ttlStr)) {
 | 
			
		||||
            return Duration.ofMillis(-1); // Spring Redis 中负数 Duration 表示永不过期
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            Duration ttl = DurationStyle.detectAndParse(ttlStr);
 | 
			
		||||
            return ttl.toSeconds() > 0 ? ttl : null;
 | 
			
		||||
        } catch (IllegalArgumentException e) {
 | 
			
		||||
            log.debug("解析缓存 TTL 失败,cacheName='{}', ttl='{}', 错误: {}", name, ttlStr, e.getMessage());
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -20,17 +20,25 @@ public class LongJsonSerializer extends JsonSerializer<Long> {
 | 
			
		||||
 | 
			
		||||
    public static final LongJsonSerializer INSTANCE = new LongJsonSerializer();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * JS 安全整数范围
 | 
			
		||||
     * 根据 JS Number.MIN_SAFE_INTEGER 与 Number.MAX_SAFE_INTEGER 得来
 | 
			
		||||
     */
 | 
			
		||||
    private static final long JS_MIN_SAFE_INTEGER = -9007199254740991L;
 | 
			
		||||
    private static final long JS_MAX_SAFE_INTEGER = 9007199254740991L;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void serialize(Long value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
 | 
			
		||||
        if (null == value) {
 | 
			
		||||
            gen.writeNull();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // js中最大安全整数16位 Number.MAX_SAFE_INTEGER
 | 
			
		||||
        String longStr = String.valueOf(value);
 | 
			
		||||
        if (longStr.length() > 16) {
 | 
			
		||||
            gen.writeString(longStr);
 | 
			
		||||
        // 如果超出了 JavaScript 安全整数范围,则序列化为字符串
 | 
			
		||||
        if (value < JS_MIN_SAFE_INTEGER || value > JS_MAX_SAFE_INTEGER) {
 | 
			
		||||
            gen.writeString(Long.toString(value));
 | 
			
		||||
        } else {
 | 
			
		||||
            // 否则,序列化为数字
 | 
			
		||||
            gen.writeNumber(value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package net.lab1024.sa.base.config;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.date.DatePattern;
 | 
			
		||||
import cn.hutool.core.date.LocalDateTimeUtil;
 | 
			
		||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 | 
			
		||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
 | 
			
		||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
 | 
			
		||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
 | 
			
		||||
@@ -13,6 +14,8 @@ import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import org.springframework.core.convert.converter.Converter;
 | 
			
		||||
 | 
			
		||||
import java.math.BigDecimal;
 | 
			
		||||
import java.math.BigInteger;
 | 
			
		||||
import java.time.LocalDate;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.time.format.DateTimeParseException;
 | 
			
		||||
@@ -37,6 +40,9 @@ public class JsonConfig {
 | 
			
		||||
            builder.serializers(new LocalDateSerializer(DatePattern.NORM_DATE_FORMAT.getDateTimeFormatter()));
 | 
			
		||||
            builder.serializers(new LocalDateTimeSerializer(DatePattern.NORM_DATETIME_FORMAT.getDateTimeFormatter()));
 | 
			
		||||
            builder.serializerByType(Long.class, LongJsonSerializer.INSTANCE);
 | 
			
		||||
            builder.serializerByType(Long.TYPE, LongJsonSerializer.INSTANCE);
 | 
			
		||||
            builder.serializerByType(BigInteger.class, ToStringSerializer.instance);
 | 
			
		||||
            builder.serializerByType(BigDecimal.class, ToStringSerializer.instance);
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
    "axios": "1.6.8",
 | 
			
		||||
    "clipboard": "2.0.11",
 | 
			
		||||
    "crypto-js": "4.1.1",
 | 
			
		||||
    "dayjs": "1.10.5",
 | 
			
		||||
    "dayjs": "1.11.13",
 | 
			
		||||
    "decimal.js": "10.3.1",
 | 
			
		||||
    "diff": "5.2.0",
 | 
			
		||||
    "diff2html": "3.4.47",
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
 * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
 | 
			
		||||
 */
 | 
			
		||||
import { defineStore } from 'pinia';
 | 
			
		||||
import { smartSentry } from '/@/lib/smart-sentry.js';
 | 
			
		||||
 | 
			
		||||
export const useSpinStore = defineStore({
 | 
			
		||||
  id: 'spin',
 | 
			
		||||
@@ -18,13 +19,27 @@ export const useSpinStore = defineStore({
 | 
			
		||||
  actions: {
 | 
			
		||||
    hide() {
 | 
			
		||||
      this.loading = false;
 | 
			
		||||
      let spins = document.querySelector('.ant-spin-nested-loading');
 | 
			
		||||
      spins.style.zIndex = 999;
 | 
			
		||||
      // 安全的DOM操作,避免null引用错误
 | 
			
		||||
      try {
 | 
			
		||||
        const spins = document.querySelector('.ant-spin-nested-loading');
 | 
			
		||||
        if (spins) {
 | 
			
		||||
          spins.style.zIndex = '999';
 | 
			
		||||
        }
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        smartSentry.captureError('Spin hide操作失败:', error);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    show() {
 | 
			
		||||
      this.loading = true;
 | 
			
		||||
      let spins = document.querySelector('.ant-spin-nested-loading');
 | 
			
		||||
      spins.style.zIndex = 1001;
 | 
			
		||||
      // 安全的DOM操作,避免null引用错误
 | 
			
		||||
      try {
 | 
			
		||||
        const spins = document.querySelector('.ant-spin-nested-loading');
 | 
			
		||||
        if (spins) {
 | 
			
		||||
          spins.style.zIndex = '1001';
 | 
			
		||||
        }
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        smartSentry.captureError('Spin hide操作失败:', error);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -234,12 +234,21 @@
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
  .center-container {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
 | 
			
		||||
    .header-title {
 | 
			
		||||
      font-size: 20px;
 | 
			
		||||
      flex-shrink: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .center-form-area {
 | 
			
		||||
      margin-top: 20px;
 | 
			
		||||
      flex: 1;
 | 
			
		||||
      overflow-y: auto;
 | 
			
		||||
      min-height: 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      .avatar-container {
 | 
			
		||||
        position: relative;
 | 
			
		||||
 
 | 
			
		||||
@@ -54,11 +54,11 @@
 | 
			
		||||
        </a-col>
 | 
			
		||||
        <!--更新日志-->
 | 
			
		||||
        <a-col :span="24">
 | 
			
		||||
          <ToBeDoneCard />
 | 
			
		||||
          <ChangelogCard />
 | 
			
		||||
        </a-col>
 | 
			
		||||
        <!--待办、已办-->
 | 
			
		||||
        <a-col :span="24">
 | 
			
		||||
          <ChangelogCard />
 | 
			
		||||
          <ToBeDoneCard />
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-row>
 | 
			
		||||
    </a-col>
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
    "axios": "1.6.8",
 | 
			
		||||
    "clipboard": "2.0.11",
 | 
			
		||||
    "crypto-js": "4.1.1",
 | 
			
		||||
    "dayjs": "1.10.5",
 | 
			
		||||
    "dayjs": "1.11.13",
 | 
			
		||||
    "decimal.js": "10.3.1",
 | 
			
		||||
    "default-passive-events": "^2.0.0",
 | 
			
		||||
    "diff": "5.2.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
 * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
 | 
			
		||||
 */
 | 
			
		||||
import { defineStore } from 'pinia';
 | 
			
		||||
import { smartSentry } from '/@/lib/smart-sentry.js';
 | 
			
		||||
 | 
			
		||||
export const useSpinStore = defineStore({
 | 
			
		||||
  id: 'spin',
 | 
			
		||||
@@ -18,13 +19,27 @@ export const useSpinStore = defineStore({
 | 
			
		||||
  actions: {
 | 
			
		||||
    hide() {
 | 
			
		||||
      this.loading = false;
 | 
			
		||||
      let spins = document.querySelector('.ant-spin-nested-loading');
 | 
			
		||||
      spins.style.zIndex = 999;
 | 
			
		||||
      // 安全的DOM操作,避免null引用错误
 | 
			
		||||
      try {
 | 
			
		||||
        const spins = document.querySelector('.ant-spin-nested-loading');
 | 
			
		||||
        if (spins) {
 | 
			
		||||
          spins.style.zIndex = '999';
 | 
			
		||||
        }
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        smartSentry.captureError('Spin hide操作失败:', error);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    show() {
 | 
			
		||||
      this.loading = true;
 | 
			
		||||
      let spins = document.querySelector('.ant-spin-nested-loading');
 | 
			
		||||
      spins.style.zIndex = 1001;
 | 
			
		||||
      // 安全的DOM操作,避免null引用错误
 | 
			
		||||
      try {
 | 
			
		||||
        const spins = document.querySelector('.ant-spin-nested-loading');
 | 
			
		||||
        if (spins) {
 | 
			
		||||
          spins.style.zIndex = '1001';
 | 
			
		||||
        }
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        smartSentry.captureError('Spin hide操作失败:', error);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -234,12 +234,21 @@
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
  .center-container {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
 | 
			
		||||
    .header-title {
 | 
			
		||||
      font-size: 20px;
 | 
			
		||||
      flex-shrink: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .center-form-area {
 | 
			
		||||
      margin-top: 20px;
 | 
			
		||||
      flex: 1;
 | 
			
		||||
      overflow-y: auto;
 | 
			
		||||
      min-height: 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      .avatar-container {
 | 
			
		||||
        position: relative;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user