import { ElMessage } from 'element-plus' import { IBasicBlog, IBlog, IBlogComment, IBlogNestedComment } from '@/types/blog.ts' import dayjs from 'dayjs' /** * 粒子特效 */ export const particlesOptions = { background: { color: {} }, fpsLimit: 60, interactivity: { events: { // 点击时事件 onClick: { enable: false, mode: 'push' // 可用的click模式有: "push", "remove", "repulse", "bubble"。 }, // 悬浮时事件 onHover: { enable: true, mode: 'grab' // 可用的hover模式有: "grab", "repulse", "bubble"。 }, resize: true }, modes: { bubble: { distance: 400, duration: 2, opacity: 0.8, size: 40 }, push: { quantity: 4 }, repulse: { distance: 200, duration: 0.4 } } }, particles: { color: { value: '#000000' }, links: { color: '#000000', // '#dedede'。线条颜色。 distance: 150, // 线条长度 enable: true, // 是否有线条 opacity: 0.5, // 线条透明度。 width: 1 // 线条宽度。 }, collisions: { enable: false }, move: { direction: 'none', enable: true, outMode: 'bounce', random: false, speed: 4, // 粒子运动速度。 straight: false }, number: { density: { enable: true, area: 800 }, value: 50 // 粒子数量。 }, opacity: { value: 0.1 // 粒子透明度。 }, shape: { type: 'edge' // 可用的粒子外观类型有:"circle","edge","triangle", "polygon","star" }, size: { random: true, value: 5 } }, detectRetina: true } /** * 创建鼠标点击爱心特效 * @param leftPosition 左侧位置 * @param topPosition 右侧位置 */ export const createHeartDiv = (leftPosition: number, topPosition: number) => { const heart = document.createElement('div') document.body.appendChild(heart) heart.classList.add('heart') heart.style.left = `${leftPosition}px` heart.style.top = `${topPosition}px` heart.style.animation = 'move 1s normal forwards' setTimeout(() => { document.body.removeChild(heart) }, 1000) heart.style.backgroundColor = `rgb(${Math.random() * 255},${Math.random() * 255},${Math.random() * 255})` } /** * 校验博客上传图片 * @param files 文件 */ export const validateBlogImage = (files: File[]): boolean => { if (files.length > 1) { ElMessage.error('请上传1张图片') return false } const uploadFile = files[0] const fileTypeList = ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp', 'psd', 'svg', 'tiff'] if (fileTypeList.indexOf(uploadFile.type.toLowerCase()) !== -1) { ElMessage.error('请上传图片文件') return false } const maxFileSize = 5 * 1024 * 1024 if (uploadFile.size > maxFileSize) { ElMessage.error('请上传5M以内的图片') return false } return true } export const formatBlogWordCount = (count: number): string => { if (!count) { return '' } if (count < 10000) return count.toString() const units = ['万', '亿', '兆'] let unitIndex = -1 while (count >= 10000 && unitIndex < units.length - 1) { count /= 10000 unitIndex++ } // 保留两位小数,并移除末尾可能的零 const formattedNum = count.toFixed(2).replace(/\.?0+$/, '') return `${formattedNum}${units[unitIndex]}` } export const getBlogRunTotalTime = (startDateStr: string):string => { const startDate = dayjs(startDateStr) const endDate = dayjs() // 当前时间 // 计算完整时间间隔(年、月、日) const years = endDate.diff(startDate, 'year') const months = endDate.diff(startDate.add(years, 'year'), 'month') const days = endDate.diff(startDate.add(years, 'year').add(months, 'month'), 'day') return `${years}年${months}月${days}日` } export const getBasicBlog = (blog: IBlog): IBasicBlog => { return { category: blog.category, isGreat: blog.isGreat, readDuration: blog.readDuration, topValue: blog.topValue, wordCount: blog.wordCount, visitCount: blog.visitCount, createTime: blog.createTime, updateTime: blog.updateTime } } // 扁平化数据转两层嵌套结构 export function flattenToTwoLevel(comments: IBlogComment[]): IBlogNestedComment[] { // 创建评论映射表,快速查找评论 const commentMap = new Map() comments.forEach((comment) => commentMap.set(comment.id, comment)) // 结果数组 const result: IBlogNestedComment[] = [] // 遍历所有评论,构建两层结构 comments.forEach((comment) => { if (comment.parentId === 0) { // 顶层评论 const replies = comments.filter((reply) => reply.parentId === comment.id) result.push({ comment, replies }) } }) return result }