195 lines
4.8 KiB
TypeScript
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
|
|
}
|