diff --git a/src/components/Blog/Archive/BlogArchive.vue b/src/components/Blog/Archive/BlogArchive.vue deleted file mode 100644 index 3cc5578..0000000 --- a/src/components/Blog/Archive/BlogArchive.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - - - diff --git a/src/components/Blog/Category/BlogCategory.vue b/src/components/Blog/Category/BlogCategory.vue deleted file mode 100644 index 8a437d0..0000000 --- a/src/components/Blog/Category/BlogCategory.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - - - diff --git a/src/components/Blog/Category/BlogCategoryDetail.vue b/src/components/Blog/Category/BlogCategoryDetail.vue deleted file mode 100644 index ed8a481..0000000 --- a/src/components/Blog/Category/BlogCategoryDetail.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - - - diff --git a/src/components/Blog/Comment/BlogComment.vue b/src/components/Blog/Comment/BlogComment.vue deleted file mode 100644 index 2bcafc3..0000000 --- a/src/components/Blog/Comment/BlogComment.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - - - diff --git a/src/components/Blog/Comment/BlogCommentEdit.vue b/src/components/Blog/Comment/BlogCommentEdit.vue deleted file mode 100644 index 7dc792a..0000000 --- a/src/components/Blog/Comment/BlogCommentEdit.vue +++ /dev/null @@ -1,125 +0,0 @@ - - - - - diff --git a/src/components/Blog/Comment/BlogCommentItem.vue b/src/components/Blog/Comment/BlogCommentItem.vue deleted file mode 100644 index 38e6e7f..0000000 --- a/src/components/Blog/Comment/BlogCommentItem.vue +++ /dev/null @@ -1,127 +0,0 @@ - - - - - diff --git a/src/components/Blog/Content/BlogDetail.vue b/src/components/Blog/Content/BlogDetail.vue deleted file mode 100644 index e814ad8..0000000 --- a/src/components/Blog/Content/BlogDetail.vue +++ /dev/null @@ -1,65 +0,0 @@ - - - - - diff --git a/src/components/Blog/Content/BlogLabel.vue b/src/components/Blog/Content/BlogLabel.vue deleted file mode 100644 index 5258ce0..0000000 --- a/src/components/Blog/Content/BlogLabel.vue +++ /dev/null @@ -1,92 +0,0 @@ - - - - - diff --git a/src/components/Blog/Content/BlogOverview.vue b/src/components/Blog/Content/BlogOverview.vue deleted file mode 100644 index 9001c4f..0000000 --- a/src/components/Blog/Content/BlogOverview.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - - - diff --git a/src/layout/blog/components/FooterBar/index.vue b/src/layout/blog/components/FooterBar/index.vue deleted file mode 100644 index 88afb38..0000000 --- a/src/layout/blog/components/FooterBar/index.vue +++ /dev/null @@ -1,20 +0,0 @@ - - - - - diff --git a/src/layout/blog/components/Sidebar/BlogMenu.vue b/src/layout/blog/components/Sidebar/BlogMenu.vue deleted file mode 100644 index 2dc1405..0000000 --- a/src/layout/blog/components/Sidebar/BlogMenu.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - diff --git a/src/layout/blog/components/Sidebar/BlogSiteBrief.vue b/src/layout/blog/components/Sidebar/BlogSiteBrief.vue deleted file mode 100644 index 508a4d0..0000000 --- a/src/layout/blog/components/Sidebar/BlogSiteBrief.vue +++ /dev/null @@ -1,82 +0,0 @@ - - - diff --git a/src/layout/blog/components/Sidebar/BlogSummary.vue b/src/layout/blog/components/Sidebar/BlogSummary.vue deleted file mode 100644 index ce610bf..0000000 --- a/src/layout/blog/components/Sidebar/BlogSummary.vue +++ /dev/null @@ -1,73 +0,0 @@ - - - diff --git a/src/layout/blog/components/Sidebar/BlogTitle.vue b/src/layout/blog/components/Sidebar/BlogTitle.vue deleted file mode 100644 index 899d58b..0000000 --- a/src/layout/blog/components/Sidebar/BlogTitle.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/src/layout/blog/components/Sidebar/index.vue b/src/layout/blog/components/Sidebar/index.vue deleted file mode 100644 index 63c073d..0000000 --- a/src/layout/blog/components/Sidebar/index.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - - - diff --git a/src/layout/blog/components/Tools/BlogCube.vue b/src/layout/blog/components/Tools/BlogCube.vue deleted file mode 100644 index c0f0b8d..0000000 --- a/src/layout/blog/components/Tools/BlogCube.vue +++ /dev/null @@ -1,123 +0,0 @@ - - - diff --git a/src/layout/blog/components/Tools/BlogTime.vue b/src/layout/blog/components/Tools/BlogTime.vue deleted file mode 100644 index c61c979..0000000 --- a/src/layout/blog/components/Tools/BlogTime.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - diff --git a/src/layout/blog/components/Tools/index.vue b/src/layout/blog/components/Tools/index.vue deleted file mode 100644 index e267734..0000000 --- a/src/layout/blog/components/Tools/index.vue +++ /dev/null @@ -1,196 +0,0 @@ - - - - - diff --git a/src/layout/blog/index.vue b/src/layout/blog/index.vue deleted file mode 100644 index 166f032..0000000 --- a/src/layout/blog/index.vue +++ /dev/null @@ -1,23 +0,0 @@ - - - - - diff --git a/src/router/blog.ts b/src/router/blog.ts deleted file mode 100644 index 69e5c78..0000000 --- a/src/router/blog.ts +++ /dev/null @@ -1,8 +0,0 @@ -import BlogLayout from '@/layout/blog/index.vue' -import { getRoutersByModules } from 'vue3-common/utils/routerUtil' -import { blogMeta } from '@/views/blog/meta' - - -const blogRoutes = getRoutersByModules(import.meta.glob('@/views/blog/**/*.vue'), BlogLayout, blogMeta) - -export default blogRoutes diff --git a/src/styles/blog/blog.archive.module.scss b/src/styles/blog/blog.archive.module.scss deleted file mode 100644 index 6a49064..0000000 --- a/src/styles/blog/blog.archive.module.scss +++ /dev/null @@ -1,51 +0,0 @@ -@use "blog.variable.module.scss" as blog; - -.blog-archive { - display: flex; - flex-direction: column; - gap: 3vh; - padding: 10vh 2vw; - - ::v-deep(.el-date-editor) { - align-self: center; - } - - .total { - align-self: center; - font-size: blog.$normal-font-size; - color: blog.$normal-text-color; - } - - .time-line { - height: 100%; - padding: 10px; - overflow: auto; - - .item { - height: 5vh; - - .date { - color: blog.$normal-text-color; - margin-right: 5px; - } - - .title { - font-size: blog.$normal-font-size; - color: blog.$normal-text-color; - cursor: pointer; - text-decoration: underline; - text-underline-offset: 0.2rem; - } - - .title:hover { - color: blog.$hover-text-color; - } - } - } -} - -@media screen and (max-width: 768px) { - .blog-archive { - padding: 20px 10px; - } -} diff --git a/src/styles/blog/blog.category.detail.module.scss b/src/styles/blog/blog.category.detail.module.scss deleted file mode 100644 index 81f4f3f..0000000 --- a/src/styles/blog/blog.category.detail.module.scss +++ /dev/null @@ -1,59 +0,0 @@ -@use "blog.variable.module.scss" as blog; - -.blog-category-detail { - padding: 10vh 2vw; - - display: flex; - flex-direction: column; - gap: 3vh; - - .name { - font-weight: bold; - text-align: center; - font-size: blog.$large-font-size; - } - - .category-detail-list { - height: 100%; - padding: 10px; - display: flex; - flex-direction: column; - gap: 2vh; - - .item { - display: grid; - grid-template-columns: 40px 150px 1fr; - justify-items: center; - align-items: center; - gap: 10px; - cursor: pointer; - color: blog.$normal-text-color; - - padding-bottom: 0.5vh; - border: none; - border-bottom: 1px dashed blog.$gray-text-color; - - .number, .date { - text-align: center; - } - - .title { - justify-self: start; - } - - span { - padding: 0.2vw; - } - } - - .item:hover { - color: blog.$hover-text-color; - } - } -} - -@media screen and (max-width: 768px) { - .blog-category-detail { - padding: 20px 10px; - } -} diff --git a/src/styles/blog/blog.category.module.scss b/src/styles/blog/blog.category.module.scss deleted file mode 100644 index 892c537..0000000 --- a/src/styles/blog/blog.category.module.scss +++ /dev/null @@ -1,63 +0,0 @@ -@use "blog.variable.module.scss" as blog; - -.blog-category { - display: flex; - flex-direction: column; - gap: 3vh; - padding: 10vh 2vw; - - .total { - text-align: center; - font-size: blog.$normal-font-size; - color: blog.$normal-text-color; - } - - .category-list { - height: 100%; - padding: 10px; - display: flex; - flex-direction: column; - gap: 15px; - overflow: auto; - - .item { - display: flex; - align-items: center; - gap: 5px; - padding-bottom: 5px; - border-bottom: 1px dashed blog.$gray-text-color; - - i { - font-size:28px; - color: #0f8bc7; - } - - .content { - display: flex; - flex-direction: column; - gap: 5px; - - .label { - cursor: pointer; - color: blog.$normal-text-color; - font-size: blog.$normal-font-size; - } - - .label:hover { - color: blog.$hover-text-color; - } - - .value { - font-size: blog.$normal-font-size; - color: blog.$gray-text-color; - } - } - } - } -} - -@media screen and (max-width: 768px) { - .blog-category { - padding: 20px 10px; - } -} diff --git a/src/styles/blog/blog.detail.module.scss b/src/styles/blog/blog.detail.module.scss deleted file mode 100644 index d9f6985..0000000 --- a/src/styles/blog/blog.detail.module.scss +++ /dev/null @@ -1,73 +0,0 @@ -@use "blog.variable.module.scss" as blog; - -.blog-detail { - min-height: 100vh; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 2vh; - padding: 10vh 2vw 3vh 2vw; - - .blog-md { - height: 100%; - width: 100%; - padding: 1vh 1vw; - border-radius: 10px; - box-shadow: 0 0 5px blog.$border-color; - overflow: auto; - - .md-editor { - height: 100%; - } - } -} - -.blog-title { - width: 100%; - display: grid; - grid-template-rows: 1fr 1fr; - align-items: center; - gap: 1vh; - - .title { - background-color: blog.$theme-color; - padding: 5px; - border: 2px solid blog.$theme-color; - border-radius: 10px; - text-align: center; - font-size: blog.$blog-title-font-size; - font-family: "华文楷体", serif; - } -} - -.blog-copyright { - width: 100%; - background-color: #F8F8F8; - padding: 1vh 1vw; - border-left: 3px solid #FD2C17; - font-size: blog.$small-font-size; - color: #666666; - - display: flex; - flex-direction: column; - gap: 1vh; - - strong { - margin-right: 0.2vw; - } -} - -.blog-ending { - span { - color: #CFCFCF; - } -} - -.blog-detail-mobile { - padding: 10px; - - display: flex; - flex-direction: column; - gap: 2vh; -} diff --git a/src/styles/blog/blog.footbar.module.scss b/src/styles/blog/blog.footbar.module.scss deleted file mode 100644 index 2a3f0ef..0000000 --- a/src/styles/blog/blog.footbar.module.scss +++ /dev/null @@ -1,15 +0,0 @@ -@use "blog.variable.module.scss" as blog; - -.blog-site-footer { - padding: 1vh 0; - - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 1vh; - - span { - font-size: blog.$small-font-size; - } -} diff --git a/src/styles/blog/blog.label.module.scss b/src/styles/blog/blog.label.module.scss deleted file mode 100644 index bdb1270..0000000 --- a/src/styles/blog/blog.label.module.scss +++ /dev/null @@ -1,57 +0,0 @@ -@use "blog.variable.module.scss" as blog; - -.label-list { - width: 100%; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 1vh; - - margin-bottom: 1vh; - - .container { - display: flex; - align-items: center; - gap: 5px; - - .item { - font-size: blog.$small-font-size; - color: blog.$gray-text-color; - display: flex; - align-items: center; - gap: 5px; - - span { - font-size: blog.$small-font-size; - font-family: "Times New Roman", serif; - } - } - } - - .top-value { - color: #7d26cd !important; - } - - .great { - color: #FFFFFF !important; - background-color: #7d26cd; - padding: 2px; - } - - .date { - text-decoration: underline dotted; - text-underline-offset: 0.2em; - } - - .category { - text-decoration: underline; - text-underline-offset: 0.2em; - color: blog.$normal-text-color; - cursor: pointer; - } - - .category:hover { - color: blog.$hover-text-color; - } -} diff --git a/src/styles/blog/blog.module.scss b/src/styles/blog/blog.module.scss deleted file mode 100644 index ac822ab..0000000 --- a/src/styles/blog/blog.module.scss +++ /dev/null @@ -1,66 +0,0 @@ -@use "blog.variable.module.scss" as blog; - -.blog-container { - // height: 100%; - background-color: blog.$bg-color; - padding: 0 5vw; - - display: grid; - grid-template-columns: minmax(13vw, 200px) 1fr; - column-gap: 2vw; - - aside { - display: grid; - grid-template-rows: 40vh 60vh; - row-gap: 2vh; - z-index: 2; - - .blog-summary { - // 滚动到顶部固定 - position: sticky; - top: 0; - } - } - - section { - z-index: 2; - - display: flex; - flex-direction: column; - gap: 2vh; - - .blog-section { - // height: 100%; - min-height: 100vh; - background-color: blog.$card-bg-color; - border: 1px solid blog.$border-color; - border-radius: blog.$border-radius; - box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), - 0 3px 1px -2px rgba(0,0,0,0.06), - 0 1px 5px 0 rgba(0,0,0,0.12), - 0 -1px 0.5px 0 rgba(0,0,0,0.09); - - .module-title { - background-color: blog.$theme-color; - padding: 5px; - border: 2px solid blog.$theme-color; - border-radius: 10px; - text-align: center; - font-size: blog.$large-font-size; - font-family: "华文楷体", serif; - } - } - } - - .card-container { - background-color: blog.$card-bg-color; - border: 1px solid blog.$border-color; - border-radius: blog.$border-radius; - box-shadow: 0 2px 2px 0 rgba(0,0,0,0.12), - 0 3px 1px -2px rgba(0,0,0,0.06), - 0 1px 5px 0 rgba(0,0,0,0.12), - 0 -1px 0.5px 0 rgba(0,0,0,0.09); - } -} - - diff --git a/src/styles/blog/blog.overview.module.scss b/src/styles/blog/blog.overview.module.scss deleted file mode 100644 index 7759d56..0000000 --- a/src/styles/blog/blog.overview.module.scss +++ /dev/null @@ -1,87 +0,0 @@ -@use "blog.variable.module.scss" as blog; - -$blog-content-list-item-height: 38vh; -$blog-content-title-font-size: 1.6rem; -$hr-color: #CFCFCF; - -.blog-content-list { - display: flex; - flex-direction: column; - align-items: center; - gap: 5vh; - padding: 10vh 2vw 2vh 2vw; - position: relative; - - .blog-content { - width: 100%; - height: $blog-content-list-item-height; - padding: 4vh 1vw; - - border: 1px solid blog.$border-color; - border-radius: 15px; - box-shadow: 0 0 5px blog.$border-color; - } - - .blog-content-empty { - display: flex; - justify-content: center; - align-items: center; - } - - .blog-content-summary { - display: grid; - grid-template-rows: 1fr 1fr 8vh 1fr 1fr; - justify-items: center; - align-items: center; - gap: 2vh; - - .title { - font-size: $blog-content-title-font-size; - font-family: "华文楷体", serif; - font-weight: bolder; - position: relative; - } - - // 自动展开下划线 - .title:after { - content: ""; - width: 0; - height: 2px; - background: blog.$theme-color; - // 初始位置在底部中间 - position: absolute; - top: 100%; - left: 50%; - // 动画时间 - transition: all .8s; - } - - // 悬浮式展开到100% - .title:hover::after { - width: 100%; - left: 0; - } - - .content { - font-size: blog.$normal-font-size; - line-height: 150%; - } - - hr { - width: 10%; - height: 1px; - background-color: $hr-color; - border: none; - } - - span, p { - font-family: '华为行楷',serif; - } - } -} - -@media screen and (max-width: 768px) { - .blog-content-list { - padding: 10px; - } -} diff --git a/src/styles/blog/blog.sidebar.module.scss b/src/styles/blog/blog.sidebar.module.scss deleted file mode 100644 index 4485803..0000000 --- a/src/styles/blog/blog.sidebar.module.scss +++ /dev/null @@ -1,282 +0,0 @@ -@use "blog.variable.module.scss" as blog; - -$title-font-color: #FFFFFF; -$title-font-size: 1.8rem; -$sub-title-font-size: 1rem; - -.blog-aside { - .top-container { - width: 100%; - display: grid; - grid-template-rows: 2fr 3fr; - - .title-container { - background-color: blog.$theme-color; - border-top-left-radius: 15px; - border-top-right-radius: 15px; - - display: grid; - grid-template-rows: 1fr 1fr; - justify-items: center; - align-items: center; - - padding: 10px; - - .title { - color: $title-font-color; - font-size: $title-font-size; - } - - .sub-title { - font-family: "华文楷体", serif; - font-size: $sub-title-font-size; - text-align: center; - line-height: 150%; - } - } - - .blog-menu-container { - align-self: center; - height: 90%; - display: grid; - grid-template-rows: repeat(4, 1fr); - align-items: center; - - .el-menu-item { - height: 5vh; - gap: 0.5vw; - font-size: blog.$normal-font-size; - - .menu-title { - - } - - .value { - position: absolute; - right: 10%; - line-height: 2vh; - - background-color: blog.$normal-bg-color; - padding: 2px; - border: solid 1px blog.$normal-bg-color; - border-radius: 15px; - - color: white; - font-size: blog.$small-font-size; - font-family: "Times New Roman",serif; - } - } - } - - .el-menu { - border: none; - } - } - - .blog-summary { - // padding: 2vh 1vw; - display: flex; - flex-direction: column; - align-items: center; - gap: 1vh; - - .blog-summary-tab { - margin-top: 1vh; - height: 5vh; - display: flex; - align-items: center; - gap: 1vw; - - span { - font-size: blog.$small-font-size; - } - - span:hover { - color: #FC6423; - cursor: pointer; - } - - .tab-active { - color: #FC6423; - text-decoration: underline; - text-underline-offset: 0.5em; - } - } - - .blog-brief { - height: 100%; - width: 100%; - display: grid; - grid-template-rows: 150px 50px 50px 1fr; - justify-items: center; - align-items: center; - gap: 1vh; - - .avatar { - padding: 1vh 1vw; - display: grid; - grid-template-rows: 100px 1fr; - justify-items: center; - align-items: center; - gap: 1vh; - - img { - width: 100px; - height: 100px; - border-radius: 50%; - transition: all 2.0s; - cursor: pointer; - } - - img:hover { - transform: rotate(360deg); - } - - span { - font-weight: bold; - } - } - - .info { - display: grid; - grid-template-columns: 1fr 1fr; - justify-items: center; - align-items: center; - gap: 1vw; - - .item { - display: grid; - grid-template-rows: 1fr 1fr; - justify-items: center; - align-items: center; - gap: 5px; - cursor: pointer; - - .name { - color: blog.$gray-text-color; - } - - .name:hover { - color: blog.$hover-text-color; - } - } - } - - .social { - width: 100%; - display: grid; - grid-template-columns: 1fr 1fr; - cursor: pointer; - - .item { - width: 100%; - padding: 5px; - display: flex; - justify-content: center; - align-items: center; - gap: 5px; - color: blog.$normal-text-color; - font-size: blog.$small-font-size; - - .name { - font-size: blog.$small-font-size; - } - } - - .item:hover { - background-color: blog.$hover-bg-color; - color: blog.$hover-text-color; - } - } - - .latest-blog { - height: 100%; - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - gap: 1vh; - - .title { - span { - margin-left: 5px; - font-weight: bold; - } - } - - .blog-list { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - gap: 1.5vh; - cursor: pointer; - - span { - text-align: center; - text-decoration: underline; - text-underline-offset: 0.1em; - font-size: blog.$small-font-size; - color: blog.$normal-text-color; - - width: 80%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - span:hover { - color: blog.$hover-text-color; - } - } - } - - .blog-time { - width: 10vw; - height: 100%; - } - - span { - font-size: blog.$normal-font-size; - } - } - - .md-editor-catalog { - height: 100%; - width: 100%; - overflow: auto; - - span { - word-break: break-all; - white-space: normal; - font-size: blog.$smaller-font-size; - color: #666666; - } - } - - .scroll-container { - padding: 2px 0; - width: 100%; - border-color: blog.$border-color; - border-bottom-left-radius: blog.$border-radius; - border-bottom-right-radius: blog.$border-radius; - background-color: #F8F9FA; - - text-align: center; - - .fa-arrow-up { - color: blog.$theme-color; - margin-right: 0.2vw; - } - - span { - font-size: blog.$small-font-size; - color: #A19FA0; - } - } - - .scroll-container:hover { - cursor: pointer; - } - } -} diff --git a/src/styles/blog/blog.variable.module.scss b/src/styles/blog/blog.variable.module.scss deleted file mode 100644 index 383b7f5..0000000 --- a/src/styles/blog/blog.variable.module.scss +++ /dev/null @@ -1,23 +0,0 @@ -$bg-color: #EEEEEE; -$card-bg-color: #FFFFFF; - -// 主题颜色 -$theme-color: #009FA8; -// 正常文本颜色 -$normal-text-color: #555555; -// 悬浮文本颜色 -$hover-text-color: #000000; -// 灰色文本颜色 -$gray-text-color: #9C9C9C; - -$normal-bg-color: #CCCCCC; -$hover-bg-color: #F9F9F9; - -$blog-title-font-size: 1.6rem; -$large-font-size: 1.3rem; -$normal-font-size: 1rem; -$small-font-size: 0.9rem; -$smaller-font-size: 0.8rem; - -$border-color: #EEEEEE; -$border-radius: 15px; diff --git a/src/utils/blog/blogUtil.ts b/src/utils/blog/blogUtil.ts deleted file mode 100644 index 8a1465d..0000000 --- a/src/utils/blog/blogUtil.ts +++ /dev/null @@ -1,194 +0,0 @@ -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 -} diff --git a/src/utils/blog/ribbon.ts b/src/utils/blog/ribbon.ts deleted file mode 100644 index 9a08944..0000000 --- a/src/utils/blog/ribbon.ts +++ /dev/null @@ -1,164 +0,0 @@ -const { random } = Math - -const randomColor = [ - ['#95e1d3', '#eaffd0', '#fce38a', '#f38181'], - ['#6a2c70', '#b83b5e', '#f08a5d', '#f9ed69'], - ['#edb1f1', '#d59bf6', '#9896f1', '#8ef6e4'], - ['#ff9de2', '#8c82fc', '#b693fe', '#7effdb'], - ['#fff5a5', '#ffaa64', '#ff8264', '#ff6464'] -] - -export interface IRibbonOptions { - zIndex: number; - alpha: number; - size: number; -} - -interface IRibbonPoint { - x: number; - y: number; -} - -const getRandomInt = (value: number) => { - return Math.floor(Math.random() * value) -} - -export class Ribbon { - /** - * 画布元素 - */ - canvas: HTMLCanvasElement; - /** - * Canvas中的Context - */ - ctx: CanvasRenderingContext2D; - /** - * 轨迹路径 - */ - path: IRibbonPoint[]; - /** - * 区域宽度 - */ - width: number; - /** - * 区域高度 - */ - height: number; - /** - * 颜色色系 - */ - colorIndex: number; - /** - * 配置项 - */ - options: IRibbonOptions - - constructor(options: IRibbonOptions = { - alpha: 0.6, - size: 300, - zIndex: 0 - }) { - this.options = options - // 生成Canvas元素 并添加到Body中 - this.canvas = document.createElement('canvas') - this.canvas.classList.add('ribbon-canvas') - this.canvas.style.cssText = `position:fixed;top:0;left:0;z-index:${this.options.zIndex}` - document.getElementsByTagName('body')[0].appendChild(this.canvas) - - this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D - this.width = window.innerWidth - this.height = window.innerHeight - - // 返回实际宽高 - const dpr = window.devicePixelRatio || 1 - this.canvas.width = this.width * dpr - this.canvas.height = this.height * dpr - // 水平、竖直方向缩放 - this.ctx.scale(dpr, dpr) - // 图形透明度 - this.ctx.globalAlpha = this.options.alpha - this.path = [] - this.colorIndex = 0 - - this.reDraw() - } - - /** - * 重新绘制 - */ - reDraw() { - // 清除之前绘制的图形 - this.ctx.clearRect(0, 0, this.width, this.height) - // 生成新的点和路径 - const point1: IRibbonPoint = { - x: 0, - y: this.height * 0.7 + this.options.size - } - const point2: IRibbonPoint = { - x: 0, - y: this.height * 0.7 - this.options.size - } - this.path = [point1, point2] - // 随机选择色系 - this.colorIndex = getRandomInt(randomColor.length) - // 路径没有填满屏幕宽度时,绘制路径 - while (this.path[1].x < this.width + this.options.size) { - this.draw(this.path[0], this.path[1]) - } - } - - /** - * 绘制线 - * @param start 起点 - * @param end 终点 - */ - draw(start: IRibbonPoint, end: IRibbonPoint) { - // 绘制当前点 - this.ctx.beginPath() - this.ctx.moveTo(start.x, start.y) - this.ctx.lineTo(end.x, end.y) - const nextX = this.geneX(end.x) - const nextY = this.geneY(end.y) - this.ctx.lineTo(nextX, nextY) - this.ctx.closePath() - - // 随机生成颜色 - const color = randomColor[this.colorIndex] - this.ctx.fillStyle = `${color[getRandomInt(color.length)]}` - // 根据当前样式填充路径 - this.ctx.fill() - - // 继续绘制下一个点 - // 起点更新为当前终点 - this.path[0] = this.path[1] - // 更新终点 - this.path[1] = { x: nextX, y: nextY } - } - - /** - * 生成下一个x坐标 - * @param x 当前x坐标 - */ - geneX(x: number) { - // 当前坐标向上平移 - return x + this.options.size - } - - /** - * 生成下一个y坐标 - * @param y 当前y坐标 - */ - geneY(y: number) { - // 当前坐标向右平移 - const temp = y + (random() * 2 - 1.1) * this.options.size - return (temp > this.height || temp < 0) ? this.height : temp - } - - /** - * 清除画布 - */ - clear() { - this.canvas.remove() - } -} - diff --git a/src/utils/blog/timeCanvas.ts b/src/utils/blog/timeCanvas.ts deleted file mode 100644 index 22c90a4..0000000 --- a/src/utils/blog/timeCanvas.ts +++ /dev/null @@ -1,345 +0,0 @@ -const timeColors = ['#33B5E5', '#0099CC', '#AA66CC', '#9933CC', '#99CC00', '#669900', '#FFBB33', '#FF8800', '#FF4444', '#CC0000'] -const timeDigits = [ - [ - [0, 0, 1, 1, 1, 0, 0], - [0, 1, 1, 0, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 0, 1, 1, 0], - [0, 0, 1, 1, 1, 0, 0] - ], // 0 - [ - [0, 0, 0, 1, 1, 0, 0], - [0, 1, 1, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [1, 1, 1, 1, 1, 1, 1] - ], // 1 - [ - [0, 1, 1, 1, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 1, 1, 0, 0, 0], - [0, 1, 1, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 1, 1, 1, 1, 1] - ], // 2 - [ - [1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 1, 1, 1, 0, 0], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 1, 1, 0] - ], // 3 - [ - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 1, 0], - [0, 0, 1, 1, 1, 1, 0], - [0, 1, 1, 0, 1, 1, 0], - [1, 1, 0, 0, 1, 1, 0], - [1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 1, 1] - ], // 4 - [ - [1, 1, 1, 1, 1, 1, 1], - [1, 1, 0, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 1, 1, 0] - ], // 5 - [ - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 1, 1, 0, 0, 0], - [0, 1, 1, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0, 0], - [1, 1, 0, 1, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 1, 1, 0] - ], // 6 - [ - [1, 1, 1, 1, 1, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 0, 1, 1, 0, 0, 0], - [0, 0, 1, 1, 0, 0, 0], - [0, 0, 1, 1, 0, 0, 0], - [0, 0, 1, 1, 0, 0, 0] - ], // 7 - [ - [0, 1, 1, 1, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 1, 1, 0] - ], // 8 - [ - [0, 1, 1, 1, 1, 1, 0], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [1, 1, 0, 0, 0, 1, 1], - [0, 1, 1, 1, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 0, 1, 1], - [0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 1, 1, 0, 0], - [0, 1, 1, 0, 0, 0, 0] - ], // 9 - [ - [0, 0, 0, 0], - [0, 0, 0, 0], - [0, 1, 1, 0], - [0, 1, 1, 0], - [0, 0, 0, 0], - [0, 0, 0, 0], - [0, 1, 1, 0], - [0, 1, 1, 0], - [0, 0, 0, 0], - [0, 0, 0, 0] - ]// : -] - -interface ITimeCanvasNum { - num: number; - offsetX: number; - offsetY: number; -} - -interface ITimeCanvasBall { - offsetX: number; - offsetY: number; - color: string; - g: number; - vx: number; - vy: number; -} - -export class TimeCanvas { - context: CanvasRenderingContext2D - width: number - height: number - options: { - RADIUS: number; // 球半径 - NUMBER_GAP: number; // 数字之间的间隙 - u: number; // 碰撞能量损耗系数 - timeColor: string; // 时间颜色 - colors: string[]; // 彩色小球的颜色 - digit: any[]; // 数字显示 - offsetX: number; // 初始x偏移 - offsetY: number; // 初始y偏移 - } - currentNums: ITimeCanvasNum[] = [] // 屏幕显示的8个字符 - balls: ITimeCanvasBall[] = [] // 存储彩色的小球 - - constructor(context: CanvasRenderingContext2D, width: number, height: number) { - this.context = context - this.width = width - this.height = height - this.options = { - RADIUS: 1.2, // 球半径 - NUMBER_GAP: 10, // 数字之间的间隙 - u: 0.4, // 碰撞能量损耗系数 - timeColor: '#1677FF', - colors: timeColors, // 彩色小球的颜色 - digit: timeDigits, // 数字显示 - offsetX: 0, - offsetY: 10 - } - } - - /** - * 绘制时间 - */ - drawDatetime(): void { - // 计算需要显示的时间数字信息 - const timeNums = [] as ITimeCanvasNum[] - - const date = new Date() - const hours = date.getHours() - const hour1 = Math.floor(hours / 10) - const hour2 = hours % 10 - timeNums.push({ num: hour1, offsetX: 0, offsetY: 0 }) - timeNums.push({ num: hour2, offsetX: 0, offsetY: 0 }) - // 添加冒号 下同 - timeNums.push({ num: 10, offsetX: 0, offsetY: 0 }) - - const minutes = date.getMinutes() - const minute1 = Math.floor(minutes / 10) - const minute2 = minutes % 10 - timeNums.push({ num: minute1, offsetX: 0, offsetY: 0 }) - timeNums.push({ num: minute2, offsetX: 0, offsetY: 0 }) - timeNums.push({ num: 10, offsetX: 0, offsetY: 0 }) - - const seconds = date.getSeconds() - const second1 = Math.floor(seconds / 10) - const second2 = seconds % 10 - timeNums.push({ num: second1, offsetX: 0, offsetY: 0 }) - timeNums.push({ num: second2, offsetX: 0, offsetY: 0 }) - - // canvas绘制时的初始x偏移 - let { offsetX } = this.options - // canvas绘制时的初始y偏移 - const { offsetY } = this.options - for (let x = 0; x < timeNums.length; x++) { - timeNums[x].offsetX = offsetX - timeNums[x].offsetY = offsetY - offsetX = this.drawSingleNumber(timeNums[x]) - // 两个数字连一块,应该间隔一些距离 - if (x < timeNums.length - 1) { - offsetX += this.options.NUMBER_GAP - } - } - - // 第一次绘制 - if (this.currentNums.length === 0) { - this.currentNums = timeNums - } else { - // 后续绘制时 替换不同的部分 并添加爆炸小球 - for (let index = 0; index < this.currentNums.length; index++) { - if (this.currentNums[index].num !== timeNums[index].num) { - // 不一样时,添加彩色小球 - // this.addBalls(timeNums[index]) - this.currentNums[index].num = timeNums[index].num - } - } - } - - // 渲染小球 - // this.renderBalls() - // 更新小球 - // this.updateBalls() - } - - /** - * 添加小球 - * @param item 小球数字 - */ - addBalls(item: ITimeCanvasNum) { - const { num, offsetX } = item - const numMatrix = this.options.digit[num] - - for (let y = 0; y < numMatrix.length; y++) { - for (let x = 0; x < numMatrix[y].length; x++) { - if (numMatrix[y][x] === 1) { - const ball: ITimeCanvasBall = { - offsetX: offsetX + this.options.RADIUS + this.options.RADIUS * 2 * x, - offsetY: this.options.offsetY + this.options.RADIUS + this.options.RADIUS * 2 * y, - color: this.options.colors[Math.floor(Math.random() * this.options.colors.length)], - g: 1.5 + Math.random(), - vx: Math.pow(-1, Math.ceil(Math.random() * 10)) * 4 + Math.random(), - vy: -5 - } - this.balls.push(ball) - } - } - } - } - - /** - * 渲染小球 - */ - renderBalls() { - for (let index = 0; index < this.balls.length; index++) { - const ball = this.balls[index] - this.context.beginPath() - this.context.fillStyle = this.balls[index].color - this.context.arc(ball.offsetX, ball.offsetY, this.options.RADIUS, 0, 2 * Math.PI) - this.context.fill() - } - } - - /** - * 更新小球 - */ - updateBalls() { - let i = 0 - const radius = this.options.RADIUS - for (let index = 0; index < this.balls.length; index++) { - const ball = this.balls[index] - ball.offsetX += ball.vx - ball.offsetY += ball.vy - ball.vy += ball.g - - if (ball.offsetY > (this.height - radius)) { - ball.offsetY = this.height - radius - ball.vy = -ball.vy * this.options.u - } - - if (ball.offsetX > radius && ball.offsetX < (this.width - radius)) { - this.balls[i] = this.balls[index] - i++ - } - } - - // 去除出边界的球 - for (;i < this.balls.length; i++) { - this.balls.pop() - } - - // 防止切换至其他窗口 仍然在生成粒子 - if (this.balls.length > 100) { - this.balls = [] - } - } - - /** - * 获取单个数字 - * @param timeNums 数字 - */ - drawSingleNumber(timeNums: ITimeCanvasNum): number { - const numMatrix = this.options.digit[timeNums.num] - const radius = this.options.RADIUS - - for (let y = 0; y < numMatrix.length; y++) { - for (let x = 0; x < numMatrix[y].length; x++) { - if (numMatrix[y][x] === 1) { - this.context.beginPath() - const arcX = timeNums.offsetX + radius + radius * 2 * x - const arcY = timeNums.offsetY + radius + radius * 2 * y - this.context.arc(arcX, arcY, radius, 0, 2 * Math.PI) - this.context.fill() - this.context.fillStyle = this.options.timeColor - } - } - } - this.context.beginPath() - timeNums.offsetX += numMatrix[0].length * radius * 2 - - return timeNums.offsetX as number - } -} diff --git a/src/views/blog/archive.vue b/src/views/blog/archive.vue deleted file mode 100644 index f54bafc..0000000 --- a/src/views/blog/archive.vue +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/src/views/blog/category.vue b/src/views/blog/category.vue deleted file mode 100644 index be9831d..0000000 --- a/src/views/blog/category.vue +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/src/views/blog/category[name].vue b/src/views/blog/category[name].vue deleted file mode 100644 index 4836e5c..0000000 --- a/src/views/blog/category[name].vue +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/src/views/blog/detail[id].vue b/src/views/blog/detail[id].vue deleted file mode 100644 index d568da7..0000000 --- a/src/views/blog/detail[id].vue +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/src/views/blog/meta.ts b/src/views/blog/meta.ts deleted file mode 100644 index e24bf11..0000000 --- a/src/views/blog/meta.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { IRouteMetaConfig } from 'vue3-common/types' - -export const blogMeta: IRouteMetaConfig = { - '/blog': { - path: '/blog', - redirect: '/blog/overview' - } -} diff --git a/src/views/blog/overview.vue b/src/views/blog/overview.vue deleted file mode 100644 index d231757..0000000 --- a/src/views/blog/overview.vue +++ /dev/null @@ -1,7 +0,0 @@ - - -