添加返回顶部组件,优化博客详情页布局和样式,更新Markdown编辑器配置和依赖声明
This commit is contained in:
3
components.d.ts
vendored
3
components.d.ts
vendored
@ -9,6 +9,7 @@ export {}
|
|||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
Aplayer: typeof import('./src/components/aplayer.vue')['default']
|
Aplayer: typeof import('./src/components/aplayer.vue')['default']
|
||||||
|
BackTop: typeof import('./src/components/backTop.vue')['default']
|
||||||
ContextMenu: typeof import('./src/components/contextMenu.vue')['default']
|
ContextMenu: typeof import('./src/components/contextMenu.vue')['default']
|
||||||
Gallery: typeof import('./src/components/Gallery.vue')['default']
|
Gallery: typeof import('./src/components/Gallery.vue')['default']
|
||||||
HomeSide: typeof import('./src/components/homeSide.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']
|
Mask: typeof import('./src/components/mask.vue')['default']
|
||||||
MenuH: typeof import('./src/components/menuH.vue')['default']
|
MenuH: typeof import('./src/components/menuH.vue')['default']
|
||||||
NAvatar: typeof import('naive-ui')['NAvatar']
|
NAvatar: typeof import('naive-ui')['NAvatar']
|
||||||
|
NBackTop: typeof import('naive-ui')['NBackTop']
|
||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
NCard: typeof import('naive-ui')['NCard']
|
NCard: typeof import('naive-ui')['NCard']
|
||||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
@ -37,6 +39,7 @@ declare module 'vue' {
|
|||||||
NModal: typeof import('naive-ui')['NModal']
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
NModalProvider: typeof import('naive-ui')['NModalProvider']
|
NModalProvider: typeof import('naive-ui')['NModalProvider']
|
||||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||||
|
NScrollBar: typeof import('naive-ui')['NScrollBar']
|
||||||
NSelect: typeof import('naive-ui')['NSelect']
|
NSelect: typeof import('naive-ui')['NSelect']
|
||||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||||
NTabs: typeof import('naive-ui')['NTabs']
|
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 "vue3-masonry-plus";
|
||||||
declare module "vite";
|
declare module "vite";
|
||||||
declare module "vue-devui/tag";
|
declare module "vue-devui/tag";
|
||||||
declare module "@markdown-next/vue";
|
declare module "es-toolkit";
|
||||||
declare module "@kangc/v-md-editor/lib/preview";
|
|
||||||
declare module "@kangc/v-md-editor/lib/theme/github.js";
|
|
||||||
@ -1,2 +1,3 @@
|
|||||||
@import "./base.less";
|
@import "./base.less";
|
||||||
@import "qweather-icons/font/qweather-icons.css";
|
@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>
|
<template>
|
||||||
<div class="article-page w-full">
|
<div class="flex justify-between">
|
||||||
<div class="title">{{ blogData.title }}</div>
|
<div class="left w-[15%] shadow">
|
||||||
<div class="tags">
|
<div class="fixed top-20 left-8">
|
||||||
<div v-for="item in blogData.tags" :key="item.tid" class="tag">{{ item.name }}</div>
|
<div class="text-center text-3xl text-bold mb-2">目录</div>
|
||||||
|
<MdCatalog :editorId="blogData.aid" :scrollElement="scrollElement" />
|
||||||
</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>
|
</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>
|
||||||
|
<back-top />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import backTop from '@/components/backTop.vue';
|
||||||
|
import { formatTime } from '@/util';
|
||||||
import type { ExposeParam } from 'md-editor-v3';
|
import type { ExposeParam } from 'md-editor-v3';
|
||||||
import { MdCatalog, MdPreview } from 'md-editor-v3';
|
import { MdCatalog, MdPreview } from 'md-editor-v3';
|
||||||
import 'md-editor-v3/lib/preview.css';
|
|
||||||
|
|
||||||
const editorRef = ref<ExposeParam>();
|
const editorRef = ref<ExposeParam>();
|
||||||
const id = 'preview-only';
|
const scrollElement: any = document.querySelector('.n-scrollbar .n-scrollbar-container');
|
||||||
const scrollElement = document.documentElement;
|
|
||||||
|
|
||||||
|
const tags = ref<any[]>([])
|
||||||
const route: any = useRoute()
|
const route: any = useRoute()
|
||||||
definePage({
|
definePage({
|
||||||
name: 'blog/:bid',
|
name: 'blog/:bid',
|
||||||
@ -28,16 +47,22 @@ definePage({
|
|||||||
title: '文章详情',
|
title: '文章详情',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const markdown = ref('# Hello World\n\nThis is **markdown**!');
|
const markdown = ref('');
|
||||||
const blogData = ref<any>({})
|
const blogData = ref<any>({
|
||||||
|
aid: 'preview-only',
|
||||||
|
updated_at: Date.now()
|
||||||
|
})
|
||||||
async function getBlogDetail() {
|
async function getBlogDetail() {
|
||||||
const bid: any = route.params?.bid || 0
|
const bid: any = route.params?.bid || 0
|
||||||
// console.log('>>> --> getBlogDetail --> bid:', route, bid)
|
// console.log('>>> --> getBlogDetail --> bid:', route, bid)
|
||||||
const res = await $http.blog.getBlogDetail(bid)
|
const res = await $http.blog.getBlogDetail(bid)
|
||||||
console.log('>>> --> getBlogDetail --> res:', res.data)
|
console.log('>>> --> getBlogDetail --> res:', res.data)
|
||||||
blogData.value = 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)
|
editorRef.value?.toggleCatalog(true)
|
||||||
|
markdown.value = res.data.cont
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -46,6 +71,22 @@ onMounted(() => {
|
|||||||
|
|
||||||
</script>
|
</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>
|
</style>
|
||||||
Reference in New Issue
Block a user