feat:删除博客模块
This commit is contained in:
@@ -1,62 +0,0 @@
|
||||
<template>
|
||||
<div class="blog-archive blog-section">
|
||||
<h4 class="module-title">文章归档</h4>
|
||||
|
||||
<el-date-picker
|
||||
v-model="currentYear"
|
||||
type="year" placeholder="请选择年份"
|
||||
style="width: 100px"
|
||||
@change="changeDateEvent"
|
||||
/>
|
||||
|
||||
<span class="total">共计{{ blogStore.blogList.length }}篇文章</span>
|
||||
|
||||
<el-timeline class="time-line">
|
||||
<el-timeline-item v-for="(value, index) in blogStore.blogList" :key="index">
|
||||
<div class="item">
|
||||
<span class="date">{{ value.createTime }}</span>
|
||||
<span class="title" @click="viewBlogClick(index)">{{ value.title }}</span>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useBlogStore } from '@/store/blog.ts'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
const blogStore = useBlogStore()
|
||||
const router = useRouter()
|
||||
const currentYear = ref(new Date())
|
||||
|
||||
onMounted(() => {
|
||||
blogStore.queryBlogByCondition({
|
||||
year: (new Date()).getFullYear()
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 选择年份事件
|
||||
* @param value 年份
|
||||
*/
|
||||
const changeDateEvent = async (value: Date) => {
|
||||
await blogStore.queryBlogByCondition({
|
||||
year: value.getFullYear()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击查看博客按钮
|
||||
* @param index 索引
|
||||
*/
|
||||
const viewBlogClick = async (index: number) => {
|
||||
const blogId = blogStore.blogList[index].id
|
||||
await router.push({ name: 'BlogDetail', params: { id: blogId } })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/blog/blog.archive.module.scss";
|
||||
</style>
|
||||
@@ -1,40 +0,0 @@
|
||||
<template>
|
||||
<div class="blog-category blog-section">
|
||||
<h4 class="module-title">文章分类</h4>
|
||||
|
||||
<span class="total">目前共计{{ blogStore.blogCategoryList.length }}个分类</span>
|
||||
|
||||
<div class="category-list">
|
||||
<div v-for="(item, index) in blogStore.blogCategoryList"
|
||||
:key="index" class="item" @click="viewBlogCategoryClick(item.name)">
|
||||
<span class="label">{{ item.name }}</span>
|
||||
<span class="value">{{ `(${item.count})` }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useBlogStore } from '@/store/blog.ts'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
const blogStore = useBlogStore()
|
||||
const router = useRouter()
|
||||
|
||||
onMounted(() => {
|
||||
blogStore.queryBlogCategory()
|
||||
})
|
||||
|
||||
/**
|
||||
* 点击查看博客分类信息
|
||||
* @param name 类别名称
|
||||
*/
|
||||
const viewBlogCategoryClick = async (name: string) => {
|
||||
await router.push({ name: 'BlogCategoryName', params: { name } })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/blog/blog.category.module.scss";
|
||||
</style>
|
||||
@@ -1,41 +0,0 @@
|
||||
<template>
|
||||
<div class="blog-category-detail blog-section">
|
||||
<span class="name">{{ route.params?.name }}</span>
|
||||
|
||||
<div class="category-detail-list">
|
||||
<div v-for="(item, index) in blogStore.blogList"
|
||||
:key="index" class="item" @click="viewBlogClick(index)">
|
||||
<span class="number">{{ index+1 }}</span>
|
||||
<span class="date">{{ item?.createTime }}</span>
|
||||
<span class="title">{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useBlogStore } from '@/store/blog.ts'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
const blogStore = useBlogStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
onMounted(async () => {
|
||||
const categoryName = route.params.name as string
|
||||
await blogStore.queryBlogByCondition({
|
||||
category: categoryName
|
||||
})
|
||||
})
|
||||
|
||||
const viewBlogClick = async (index: number) => {
|
||||
const blogId = blogStore.blogList[index].id
|
||||
await router.push({ name: 'BlogDetailId', params: { id: blogId } })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/blog/blog.category.detail.module.scss";
|
||||
</style>
|
||||
@@ -1,34 +0,0 @@
|
||||
<template>
|
||||
<div class="blog-comment">
|
||||
<div class="comment-total">
|
||||
<span>共 {{ blogStore.blogNestedCommentList.length }} 条评论</span>
|
||||
</div>
|
||||
|
||||
<div class="comment-list">
|
||||
<blog-comment-item v-for="(item, index) in blogStore.blogNestedCommentList" :key="index"
|
||||
:comment="item.comment" :replyList="item.replies"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import BlogCommentItem from '@/components/Blog/Comment/BlogCommentItem.vue'
|
||||
import { useBlogStore } from '@/store/blog'
|
||||
|
||||
const blogStore = useBlogStore()
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.blog-comment {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #EEEEEE;
|
||||
border-radius: 10px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,125 +0,0 @@
|
||||
<template>
|
||||
<div class="blog-comment-edit">
|
||||
<div class="comment-title">
|
||||
<el-input v-model="blogComment.name"
|
||||
:maxlength="10" show-word-limit
|
||||
placeholder="请输入昵称"/>
|
||||
<el-input v-model="blogComment.website"
|
||||
:maxlength="50" show-word-limit
|
||||
placeholder="请输入个人博客网站"/>
|
||||
</div>
|
||||
|
||||
<el-input v-model="blogComment.content"
|
||||
placeholder="有什么想和我说的呢" :maxlength="500"
|
||||
:rows="7" show-word-limit type="textarea"
|
||||
/>
|
||||
|
||||
<div class="comment-button">
|
||||
<el-popover ref="emojiPopoverRef" placement="top" :width="300" trigger="hover">
|
||||
<template #default>
|
||||
<div class="emoji-panel">
|
||||
<EmojiPicker :native="true" @select="onSelectEmoji" />
|
||||
</div>
|
||||
</template>
|
||||
<template #reference>
|
||||
<el-button circle>
|
||||
<i class="fa-regular fa-face-smile"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
|
||||
<el-button type="primary"
|
||||
:disabled="blogComment.content.length === 0"
|
||||
@click="sendCommentClick"
|
||||
class="send-button">发送</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import EmojiPicker from 'vue3-emoji-picker'
|
||||
import 'vue3-emoji-picker/css'
|
||||
import { defineProps, ref, onMounted } from 'vue'
|
||||
import { useBlogStore } from '@/store/blog.ts'
|
||||
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
|
||||
import { IBlogComment } from '@/types/blog.ts'
|
||||
|
||||
const blogStore = useBlogStore()
|
||||
const emojiPopoverRef = ref()
|
||||
|
||||
const props = defineProps({
|
||||
parentId: {
|
||||
required: true,
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
const blogComment = ref<IBlogComment>({
|
||||
id: 0,
|
||||
blogId: 0,
|
||||
name: '',
|
||||
website: '',
|
||||
content: '',
|
||||
ipAddress: '',
|
||||
userAgent: '',
|
||||
parentId: 0,
|
||||
isApproved: true,
|
||||
createTime: ''
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
blogComment.value = deepCopyObject(blogStore.getEmptyBlogComment())
|
||||
})
|
||||
|
||||
const onSelectEmoji = (emoji) => {
|
||||
if (blogComment.value.content.length <= 498) {
|
||||
blogComment.value.content += emoji.i
|
||||
}
|
||||
|
||||
emojiPopoverRef.value.hide()
|
||||
}
|
||||
|
||||
const sendCommentClick = async () => {
|
||||
blogComment.value.parentId = props.parentId
|
||||
blogComment.value.blogId = blogStore.currentBlogId
|
||||
|
||||
if (!blogComment.value.name) {
|
||||
blogComment.value.name = 'Anonymous'
|
||||
}
|
||||
|
||||
await blogStore.addBlogComment(blogComment.value)
|
||||
blogComment.value = deepCopyObject(blogStore.getEmptyBlogComment())
|
||||
blogStore.isShowBlogCommentEdit = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.blog-comment-edit {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #EEEEEE;
|
||||
border-radius: 10px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
.comment-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.comment-button {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
.send-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,127 +0,0 @@
|
||||
<template>
|
||||
<div class="blog-comment-card">
|
||||
<el-avatar :src="`https://api.dicebear.com/6.x/adventurer/svg?seed=${props.comment.name}`"
|
||||
:size="50" alt="未加载"/>
|
||||
|
||||
<div class="blog-comment-content">
|
||||
<div class="basic-info">
|
||||
<span>{{ props.comment.name }}</span>
|
||||
<span class="content">{{ getBrowser(props.comment.userAgent) }}</span>
|
||||
<span class="content">{{ getOsInfo(props.comment.userAgent) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sub-info">
|
||||
<span class="content">{{ props.comment.createTime }}</span>
|
||||
<el-button v-if="!blogComment.isShowEdit"
|
||||
type="primary" text @click="replyClick">回复</el-button>
|
||||
<el-button v-else type="primary"
|
||||
text @click="cancelClick">取消</el-button>
|
||||
</div>
|
||||
|
||||
<p>{{ props.comment.content }}</p>
|
||||
|
||||
<blog-comment-edit v-if="blogComment.isShowEdit"
|
||||
:parent-id="props.comment.id"/>
|
||||
|
||||
<div class="reply-list">
|
||||
<div class="blog-comment-card" v-for="(item, index) in props.replyList" :key="index">
|
||||
<el-avatar :src="`https://api.dicebear.com/6.x/adventurer/svg?seed=${item.name}`"
|
||||
:size="50" alt="未加载"/>
|
||||
|
||||
<div class="blog-comment-content">
|
||||
<div class="basic-info">
|
||||
<span>{{ item.name }}</span>
|
||||
<span class="content">{{ getBrowser(props.comment.userAgent) }}</span>
|
||||
<span class="content">{{ getOsInfo(props.comment.userAgent) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sub-info">
|
||||
<span class="content">{{ item.createTime }}</span>
|
||||
</div>
|
||||
<p>{{ item.content }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import BlogCommentEdit from '@/components/Blog/Comment/BlogCommentEdit.vue'
|
||||
import 'vue3-emoji-picker/css'
|
||||
import { defineProps, PropType, reactive } from 'vue'
|
||||
import { IBlogComment } from '@/types/blog'
|
||||
import { useBlogStore } from '@/store/blog.ts'
|
||||
import { UAParser } from 'ua-parser-js'
|
||||
|
||||
const blogStore = useBlogStore()
|
||||
|
||||
const props = defineProps({
|
||||
comment: {
|
||||
required: true,
|
||||
type: Object as PropType<IBlogComment>
|
||||
},
|
||||
replyList: {
|
||||
required: true,
|
||||
type: Object as PropType<IBlogComment[]>
|
||||
}
|
||||
})
|
||||
|
||||
const blogComment = reactive({
|
||||
isShowEdit: false
|
||||
})
|
||||
|
||||
const replyClick = () => {
|
||||
blogStore.currentBlogComment = blogStore.getEmptyBlogComment()
|
||||
blogComment.isShowEdit = true
|
||||
}
|
||||
|
||||
const cancelClick = () => {
|
||||
blogComment.isShowEdit = false
|
||||
}
|
||||
|
||||
const getOsInfo = (userAgent: string) => {
|
||||
const uaParse = new UAParser(userAgent)
|
||||
return `${uaParse.getOS().name} ${uaParse.getOS().version}`
|
||||
}
|
||||
|
||||
const getBrowser = (userAgent: string) => {
|
||||
const uaParse = new UAParser(userAgent)
|
||||
return `${uaParse.getBrowser().name} ${uaParse.getBrowser().version}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.blog-comment-card {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 60px 1fr;
|
||||
gap: 5px;
|
||||
|
||||
.blog-comment-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
|
||||
.basic-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.sub-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: small;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,65 +0,0 @@
|
||||
<template>
|
||||
<div class="blog-detail blog-section" id="blog-section">
|
||||
<div class="blog-title">
|
||||
<span class="title">{{ blogStore.currentBlog.title }}</span>
|
||||
<blog-label :basic-blog="getBasicBlog(blogStore.currentBlog)"/>
|
||||
</div>
|
||||
|
||||
<div class="blog-md">
|
||||
<MdPreview editorId="blog-id" v-model="blogStore.currentBlog.content" />
|
||||
</div>
|
||||
|
||||
<div class="blog-copyright">
|
||||
<span>
|
||||
<strong>本文作者: </strong> Cxx
|
||||
</span>
|
||||
<span>
|
||||
<strong>本文链接: </strong>
|
||||
<a href="https://www.microsoft.com/">{{ currentUrl.href }}</a>
|
||||
</span>
|
||||
<span>
|
||||
<strong>版权声明: </strong>
|
||||
本博客所有文章除特别声明外,均采用
|
||||
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">©BY-NC-SA</a>
|
||||
许可协议。转载请注明出处!
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="blog-ending">
|
||||
<span>-------------本文结束♥感谢您的阅读♥给个五星好评吧~~-------------</span>
|
||||
</div>
|
||||
|
||||
<blog-comment-edit :parent-id="0"/>
|
||||
|
||||
<blog-comment />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import BlogLabel from '@/components/Blog/Content/BlogLabel.vue'
|
||||
import BlogCommentEdit from '@/components/Blog/Comment/BlogCommentEdit.vue'
|
||||
import BlogComment from '@/components/Blog/Comment/BlogComment.vue'
|
||||
import { MdPreview } from 'md-editor-v3'
|
||||
import 'md-editor-v3/lib/style.css'
|
||||
import { useBlogStore } from '@/store/blog.ts'
|
||||
import { getBasicBlog } from '@/utils/blog/blogUtil.ts'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { computed, onMounted } from 'vue'
|
||||
|
||||
const blogStore = useBlogStore()
|
||||
const route = useRoute()
|
||||
|
||||
const currentUrl = new URL(window.location.href)
|
||||
|
||||
const blogComment = computed(() => blogStore.getEmptyBlogComment())
|
||||
|
||||
onMounted(async () => {
|
||||
const blogId = route.params.id as number
|
||||
blogStore.currentBlogId = blogId
|
||||
await blogStore.queryBlogById(blogId)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/blog/blog.detail.module.scss";
|
||||
</style>
|
||||
@@ -1,92 +0,0 @@
|
||||
<template>
|
||||
<div class="label-list">
|
||||
<div class="container">
|
||||
<div class="top-value item" v-if="props.basicBlog?.topValue > 1">
|
||||
<i class="fa-solid fa-thumbtack"></i>
|
||||
</div>
|
||||
|
||||
<div v-if="props.basicBlog?.topValue > 1" class="item">|</div>
|
||||
|
||||
<div class="great item" v-if="props.basicBlog?.isGreat">
|
||||
<i class="fa-regular fa-newspaper"></i>
|
||||
</div>
|
||||
|
||||
<div v-if="props.basicBlog?.isGreat" class="item">|</div>
|
||||
|
||||
<div class="item">
|
||||
<i class="fa-regular fa-calendar"></i>
|
||||
<span class="date">{{ formatDate(props.basicBlog.createTime) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="item">|</div>
|
||||
|
||||
<div class="item">
|
||||
<i class="fa-regular fa-calendar-check"></i>
|
||||
<span class="date">{{ formatDate(props.basicBlog.createTime) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="item">|</div>
|
||||
|
||||
<div class="item">
|
||||
<i class="fa-regular fa-folder"></i>
|
||||
<span class="category" @click="viewBlogCategoryClick">
|
||||
{{ props.basicBlog.category }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="item">|</div>
|
||||
|
||||
<div class="item">
|
||||
<i class="fa-solid fa-eye"></i>
|
||||
<span>{{ props.basicBlog.visitCount }} 次</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="item">
|
||||
<i class="fa-regular fa-file-word"></i>
|
||||
<span>{{ props.basicBlog.wordCount }} 字</span>
|
||||
</div>
|
||||
|
||||
<div class="item">|</div>
|
||||
|
||||
<div class="item">
|
||||
<i class="fa-regular fa-clock"></i>
|
||||
<span>{{ props.basicBlog.readDuration.toFixed(0) }} 分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps, PropType } from 'vue'
|
||||
import { IBasicBlog } from '@/types/blog.ts'
|
||||
import { useBlogStore } from '@/store/blog.ts'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { formatDate } from 'vue3-common/utils/dateUtil'
|
||||
|
||||
const blogStore = useBlogStore()
|
||||
const router = useRouter()
|
||||
|
||||
const props = defineProps({
|
||||
basicBlog: {
|
||||
required: true,
|
||||
type: Object as PropType<IBasicBlog>
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 点击查看博客分类按钮
|
||||
*/
|
||||
const viewBlogCategoryClick = async () => {
|
||||
const category = props.basicBlog?.category as string
|
||||
await blogStore.queryBlogByCondition({
|
||||
category
|
||||
})
|
||||
await router.push({ name: 'CategoriesBlog', params: { name: category } })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/blog/blog.label.module.scss";
|
||||
</style>
|
||||
@@ -1,66 +0,0 @@
|
||||
<template>
|
||||
<div class="blog-content-list blog-section">
|
||||
<div v-if="blogStore.blogList.length === 0"
|
||||
class="blog-content blog-content-empty" >
|
||||
</div>
|
||||
|
||||
<div class="blog-content blog-content-summary"
|
||||
v-for="(item, index) in blogStore.blogList" :key="index">
|
||||
<span class="title">{{ item.title }}</span>
|
||||
|
||||
<blog-label :basic-blog="getBasicBlog(item)"/>
|
||||
|
||||
<p class="content">
|
||||
{{ item.summary }}
|
||||
</p>
|
||||
|
||||
<el-button type="primary" @click="readBlogClick(index)">阅读全文 »</el-button>
|
||||
<hr/>
|
||||
</div>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="blogStore.pageInfo.currentPage"
|
||||
:page-size="blogStore.pageInfo.pageSize"
|
||||
:total="blogStore.pageInfo.totalSize"
|
||||
layout="total, prev, pager, next"
|
||||
@current-change="changePageClick"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import BlogLabel from '@/components/Blog/Content/BlogLabel.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { onMounted } from 'vue'
|
||||
import { useBlogStore } from '@/store/blog.ts'
|
||||
import { getBasicBlog } from '@/utils/blog/blogUtil.ts'
|
||||
|
||||
const router = useRouter()
|
||||
const blogStore = useBlogStore()
|
||||
|
||||
onMounted(async () => {
|
||||
await blogStore.queryBlog()
|
||||
})
|
||||
|
||||
/**
|
||||
* 点击阅读博客按钮
|
||||
* @param index 索引
|
||||
*/
|
||||
const readBlogClick = async (index: number) => {
|
||||
const blogId = blogStore.blogList[index].id
|
||||
await router.push({ name: 'BlogDetailId', params: { id: blogId } })
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击切换博客页数按钮
|
||||
* @param page 页码
|
||||
*/
|
||||
const changePageClick = async (page: number) => {
|
||||
blogStore.pageInfo.currentPage = page
|
||||
await blogStore.queryBlog()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use "@/styles/blog/blog.overview.module.scss";
|
||||
</style>
|
||||
Reference in New Issue
Block a user