添加返回顶部组件,优化博客详情页布局和样式,更新Markdown编辑器配置和依赖声明
This commit is contained in:
3
components.d.ts
vendored
3
components.d.ts
vendored
@ -9,6 +9,7 @@ export {}
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Aplayer: typeof import('./src/components/aplayer.vue')['default']
|
||||
BackTop: typeof import('./src/components/backTop.vue')['default']
|
||||
ContextMenu: typeof import('./src/components/contextMenu.vue')['default']
|
||||
Gallery: typeof import('./src/components/Gallery.vue')['default']
|
||||
HomeSide: typeof import('./src/components/homeSide.vue')['default']
|
||||
@ -16,6 +17,7 @@ declare module 'vue' {
|
||||
Mask: typeof import('./src/components/mask.vue')['default']
|
||||
MenuH: typeof import('./src/components/menuH.vue')['default']
|
||||
NAvatar: typeof import('naive-ui')['NAvatar']
|
||||
NBackTop: typeof import('naive-ui')['NBackTop']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NCard: typeof import('naive-ui')['NCard']
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
@ -37,6 +39,7 @@ declare module 'vue' {
|
||||
NModal: typeof import('naive-ui')['NModal']
|
||||
NModalProvider: typeof import('naive-ui')['NModalProvider']
|
||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
NScrollBar: typeof import('naive-ui')['NScrollBar']
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
|
||||
4
extra.d.ts
vendored
4
extra.d.ts
vendored
@ -3,6 +3,4 @@ declare module "vue3-video-play";
|
||||
declare module "vue3-masonry-plus";
|
||||
declare module "vite";
|
||||
declare module "vue-devui/tag";
|
||||
declare module "@markdown-next/vue";
|
||||
declare module "@kangc/v-md-editor/lib/preview";
|
||||
declare module "@kangc/v-md-editor/lib/theme/github.js";
|
||||
declare module "es-toolkit";
|
||||
@ -1,2 +1,3 @@
|
||||
@import "./base.less";
|
||||
@import "qweather-icons/font/qweather-icons.css";
|
||||
@import 'md-editor-v3/lib/preview.css';
|
||||
60
src/components/backTop.vue
Normal file
60
src/components/backTop.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div id="goTop">
|
||||
<div class="icons" v-show="visiable" @click="handleScrollTop">
|
||||
<n-icon>
|
||||
<icon-backtop width="60" height="60" />
|
||||
</n-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { throttle } from 'es-toolkit/function';
|
||||
const scrollTop: any = ref(null)
|
||||
const visiable = ref(false)
|
||||
const scrollElement: any = document.querySelector('.n-scrollbar .n-scrollbar-container')
|
||||
const handleScroll = throttle(() => {
|
||||
scrollTop.value = scrollElement.scrollTop
|
||||
visiable.value = scrollTop.value > 200;
|
||||
},1000)
|
||||
function handleScrollTop() {
|
||||
let timer: any = null;
|
||||
cancelAnimationFrame(timer);
|
||||
timer = requestAnimationFrame(function fn() {
|
||||
if (scrollTop.value > 0) {
|
||||
scrollTop.value -= 50;
|
||||
scrollElement.scrollTop = document.documentElement.scrollTop = scrollTop.value;
|
||||
timer = requestAnimationFrame(fn);
|
||||
} else {
|
||||
cancelAnimationFrame(timer);
|
||||
visiable.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
scrollElement?.addEventListener('scroll', handleScroll);
|
||||
})
|
||||
onBeforeMount(() => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.icons {
|
||||
position: fixed;
|
||||
right: 60px;
|
||||
bottom: 40px;
|
||||
border-radius: 50%;
|
||||
z-index: 9999999;
|
||||
}
|
||||
|
||||
:deep(.n-icon svg) {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
</style>
|
||||
1
src/icon/backtop.svg
Normal file
1
src/icon/backtop.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767098232410" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11412" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M552.96 875.229867c0 37.034667-40.96 94.685867-40.96 94.685866s-40.96-61.44-40.96-94.685866a41.915733 41.915733 0 1 1 81.92-18.3808 43.349333 43.349333 0 0 1 0 18.3808m14.609067-441.326934a82.3808 82.3808 0 1 0-116.258134-5.256533l0.1024 0.1024a82.5344 82.5344 0 0 0 116.155734 5.12M799.914667 743.406933v15.36l-177.493334-35.9424c-11.434667 38.3488-57.326933 67.822933-111.018666 67.822934s-99.6352-29.474133-111.121067-69.051734L224.085333 756.053333v-15.36c0-87.04 24.2688-149.589333 79.172267-202.0864C284.125867 290.56 395.144533 133.12 504.951467 69.188267l6.3488-3.7376 6.3488 3.7376C627.5584 133.12 737.1776 290.56 719.325867 538.7776c56.32 52.394667 80.4864 115.080533 80.5888 204.629333" p-id="11413" fill="#ec66ab"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
src/icon/tag.svg
Normal file
1
src/icon/tag.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767094212200" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5845" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M788.48 629.418667c0-17.066667-5.802667-31.744-17.749333-44.714667L421.205333 235.861333c-12.288-12.288-29.354667-22.869333-49.834667-31.744-21.162667-8.874667-39.936-12.970667-57.002667-12.970667L110.933333 191.146667c-17.066667 0-31.744 6.485333-44.032 18.773333-12.288 12.288-18.773333 26.965333-18.773333 44.032l0 202.752c0 17.066667 4.096 36.522667 12.970667 57.002667 8.874667 21.162667 18.773333 37.546667 31.744 49.834667l349.525333 349.525333c12.288 12.288 26.965333 17.749333 44.032 17.749333 17.066667 0 31.744-5.802667 44.714667-17.749333l239.616-239.616C781.994667 661.162667 788.48 646.485333 788.48 629.418667L788.48 629.418667 788.48 629.418667zM248.490667 392.192c-12.288 12.288-26.965333 18.090667-44.032 18.090667-17.066667 0-31.744-5.802667-44.032-18.090667-12.288-12.288-18.090667-26.965333-18.090667-44.032 0-17.066667 5.802667-31.744 18.090667-44.032 12.288-12.288 26.965333-18.090667 44.032-18.090667 17.066667 0 31.744 5.802667 44.032 18.090667C260.778667 316.416 266.24 331.093333 266.24 348.16 267.264 365.568 260.778667 379.904 248.490667 392.192L248.490667 392.192 248.490667 392.192zM958.122667 584.362667 608.597333 235.861333c-12.288-12.288-29.354667-22.869333-49.834667-31.744C537.6 195.242667 518.826667 191.146667 501.76 191.146667l-109.909333 0c17.066667 0 36.522667 4.096 57.002667 12.970667 21.162667 8.874667 37.546667 18.773333 49.834667 31.744l349.525333 348.501333c12.288 12.970667 18.090667 27.648 18.090667 44.714667s-5.802667 31.744-18.090667 44.032l-229.034667 229.717333c9.898667 9.898667 18.773333 17.066667 25.941333 21.845333 7.168 4.778667 17.066667 6.485333 28.672 6.485333 17.066667 0 31.744-5.802667 44.714667-18.090667l239.616-240.298667c12.288-12.288 17.749333-26.965333 17.749333-44.032S970.069333 597.674667 958.122667 584.362667L958.122667 584.362667 958.122667 584.362667zM958.122667 584.362667" fill="#ec66ab" p-id="5846"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@ -1,25 +1,44 @@
|
||||
<template>
|
||||
<div class="article-page w-full">
|
||||
<div class="title">{{ blogData.title }}</div>
|
||||
<div class="tags">
|
||||
<div v-for="item in blogData.tags" :key="item.tid" class="tag">{{ item.name }}</div>
|
||||
<div class="flex justify-between">
|
||||
<div class="left w-[15%] shadow">
|
||||
<div class="fixed top-20 left-8">
|
||||
<div class="text-center text-3xl text-bold mb-2">目录</div>
|
||||
<MdCatalog :editorId="blogData.aid" :scrollElement="scrollElement" />
|
||||
</div>
|
||||
</div>
|
||||
<div ref="" class="w-[85%]">
|
||||
<div class="w-full px-1/20 rounded-md">
|
||||
<div class="text-center text-xl text-bold text-black` mt-10">{{ blogData.title }}</div>
|
||||
<div class="flex flex-wrap gap-4 justify-center mt-4">
|
||||
<div v-for="item in tags" :key="item.tid" class="flex items-center gap-1 text-primary cursor-pointer">
|
||||
<n-icon><icon-tag /></n-icon>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 justify-end mb-2">
|
||||
<em class="text-primary">
|
||||
{{ blogData.nickname }}</em>
|
||||
<em class="time">{{ formatTime(blogData.updated_at, "YYYY年MM月DD日hh时") }}</em>
|
||||
</div>
|
||||
<MdPreview ref="mdp" class="md relative !bg-[#FDFBFB]" :editorId="blogData.aid" previewTheme="github"
|
||||
:modelValue="markdown" codeTheme="kimbie" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="auther">{{ blogData.nickname }}</div>
|
||||
<MdPreview class="!w-[80%] ml-1/10" :editorId="id" previewTheme="github" :modelValue="markdown" />
|
||||
<MdCatalog class="absolute top-4 right-4" :editorId="id" :scrollElement="scrollElement" />
|
||||
<div class="time">{{ blogData.updated_at }}</div>
|
||||
</div>
|
||||
<back-top />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import backTop from '@/components/backTop.vue';
|
||||
import { formatTime } from '@/util';
|
||||
import type { ExposeParam } from 'md-editor-v3';
|
||||
import { MdCatalog, MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/preview.css';
|
||||
|
||||
|
||||
const editorRef = ref<ExposeParam>();
|
||||
const id = 'preview-only';
|
||||
const scrollElement = document.documentElement;
|
||||
const scrollElement: any = document.querySelector('.n-scrollbar .n-scrollbar-container');
|
||||
|
||||
const tags = ref<any[]>([])
|
||||
const route: any = useRoute()
|
||||
definePage({
|
||||
name: 'blog/:bid',
|
||||
@ -28,16 +47,22 @@ definePage({
|
||||
title: '文章详情',
|
||||
}
|
||||
})
|
||||
const markdown = ref('# Hello World\n\nThis is **markdown**!');
|
||||
const blogData = ref<any>({})
|
||||
const markdown = ref('');
|
||||
const blogData = ref<any>({
|
||||
aid: 'preview-only',
|
||||
updated_at: Date.now()
|
||||
})
|
||||
async function getBlogDetail() {
|
||||
const bid: any = route.params?.bid || 0
|
||||
// console.log('>>> --> getBlogDetail --> bid:', route, bid)
|
||||
const res = await $http.blog.getBlogDetail(bid)
|
||||
console.log('>>> --> getBlogDetail --> res:', res.data)
|
||||
blogData.value = res.data
|
||||
markdown.value = res.data.cont
|
||||
let ts = res.data.tags
|
||||
ts = ts.replaceAll(',', ',')
|
||||
tags.value = ts.split(',')
|
||||
editorRef.value?.toggleCatalog(true)
|
||||
markdown.value = res.data.cont
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@ -46,6 +71,22 @@ onMounted(() => {
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="less">
|
||||
/* 文章页样式 */
|
||||
:deep(.md img) {
|
||||
width: auto !important;
|
||||
height: 200px !important;
|
||||
}
|
||||
|
||||
:deep(.md-editor-catalog-active>span) {
|
||||
color: @primary;
|
||||
}
|
||||
|
||||
:deep(.md-editor-catalog-indicator) {
|
||||
background-color: @primary;
|
||||
}
|
||||
|
||||
:deep(.md-editor-catalog-link span):hover {
|
||||
color: @primary;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user