Files
sweet-hut-ui/src/utils/blog/blogUtil.ts
2025-06-22 21:01:49 +08:00

195 lines
4.8 KiB
TypeScript

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<number, IBlogComment>()
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
}