Browse Source

add new

master
panda 8 months ago
parent
commit
0e18f29607
  1. 623
      src/components/blogs/HomePage.vue
  2. 91
      src/components/blogs/footer/FooterContent.vue
  3. 30
      src/components/blogs/header/NavigateMenu.vue
  4. 11
      src/components/blogs/leftsite/CataloGue.vue
  5. 0
      src/components/blogs/leftsite/ComLink.vue
  6. 0
      src/components/blogs/leftsite/LeftSiteInfo.vue
  7. 0
      src/components/blogs/leftsite/QQCode.vue
  8. 0
      src/components/blogs/leftsite/WechatCode.vue
  9. 58
      src/components/blogs/rightsite/AplayerComponent.vue
  10. 134
      src/components/blogs/rightsite/HeatMap.vue
  11. 49
      src/components/blogs/rightsite/RandomArticle.vue
  12. 0
      src/components/blogs/rightsite/StatisticCount.vue
  13. 33
      src/components/blogs/rightsite/TagCloud.vue
  14. 4
      src/router/blog.ts
  15. 15
      src/stores/index.ts
  16. 4
      src/views/admin/ClassticManageView.vue
  17. 4
      src/views/blog/blogcontent/BlogDetailView.vue
  18. 3
      tsconfig.json

623
src/components/blogs/HomePage.vue

@ -17,190 +17,58 @@
</div>
</div>
<!-- 主要内容区域 -->
<!-- 主要内容区域包括左侧栏main部分右侧栏 -->
<div class="mainContainer" :style="mainCss">
<!-- 左侧栏部分 -->
<div class="leftBar">
<LeftSiteInfo />
<ComLink />
<CataloGue/>
</div>
<!-- main部分导航路由跳转部分 -->
<RouterView/>
<!-- 右侧栏部分 -->
<div class="rightBar">
<StatisticCount />
<a-card hoverable>
<template #cover>
<div class="heatmap" style="border-right: rgba(0, 0, 0, 0.5);">
<div ref="heat" style="height: 100%;"></div>
</div>
</template>
</a-card>
<a-card hoverable>
<template #cover>
<div id="aplayer"></div>
</template>
</a-card>
<a-card title="随机文章" :bordered="false" hoverable>
<div v-for="article in homepagelist">
<div v-if="article.blogtitle">
<div class="article-text">
<span>{{ article.blogtitle }}</span>
<span>{{ article.create_at }}</span>
</div>
<a-image :preview="false" :width="300" :src="article.imglink" />
</div>
<div v-if="article.diarytitle">
<div class="article-text">
<span>{{ article.diarytitle }}</span>
<span>{{ article.create_at }}</span>
</div>
<a-image :preview="false" :width="300" :src="article.imglink" />
</div>
</div>
</a-card>
<a-card title="标签云" :bordered="false" hoverable>
<a-tag :color=randomColor() v-for="label in labellist">{{ label.labelname }}</a-tag>
</a-card>
<HeatMap />
<AplayerComponent />
<RandomArticle />
<TagCloud />
</div>
</div>
<!-- 底部部分 -->
<div class="footer">
<div class="miit">
<a-space wrap>
<a-text type="link">
<component :is=iconComponents.BanQuanLined />
<span class="miit-style">Copyright © 2024</span>
</a-text>
<a-button type="link" href="/home">
<component :is=iconComponents.TitleOutLined />
<span>SunFree</span>
</a-button>
<a-button type="link" href="https://beian.miit.gov.cn/" target="_blank">
<component :is=iconComponents.IcpLined />
<span>苏ICP备2024067473号-1</span>
</a-button>
<a-button type="link" href="https://beian.mps.gov.cn/#/query/webSearch" target="_blank">
<component :is=iconComponents.GongAnLined />
<span>苏公网安备32021402003003号</span>
</a-button>
</a-space>
</div>
<div class="badge">
<a-space wrap>
<a-button type="link" class="badge-button" href="https://cn.vuejs.org/" target="_blank">
<span class="tag-nav">front-end</span>
<span class="tag-main-first">Vue.js</span>
</a-button>
<a-button type="link" class="badge-button" href="https://fastapi.tiangolo.com/zh/" target="_blank">
<span class="tag-nav">back-end</span>
<span class="tag-main-second">FastAPI</span>
</a-button>
<a-button type="link" class="badge-button" href="https://www.antdv.com/docs/vue/introduce-cn/"
target="_blank">
<span class="tag-nav">UI</span>
<span class="tag-main-third">Ant Design Vue</span>
</a-button>
<a-button type="link" class="badge-button" href="https://cloud.tencent.com/" target="_blank">
<span class="tag-nav">VPS</span>
<span class="tag-main-fourth">tencent cloud</span>
</a-button>
</a-space>
</div>
<div>
</div>
<div></div>
<FooterContent />
</div>
</Simplebar>
</template>
<script lang="ts" setup>
import { reactive, ref, nextTick, toRefs,h } from 'vue';
import { reactive, ref, nextTick, toRefs,onMounted} from 'vue';
import { DownCircleOutlined } from '@ant-design/icons-vue';
import Typed from 'typed.js';
import { onMounted, watch } from 'vue';
import 'APlayer/dist/APlayer.min.css';
import APlayer from 'APlayer';
import { get } from "@/tools/request"
import { createEcharts } from "@/hooks/intex"
import { useRouter, useRoute } from 'vue-router';
import iconComponents from "@/assets/index";
import type { classticInterface, labelInterface } from '@/api/admin';
import type { homePageInterface } from '@/api';
import ComLink from './ComLink.vue';
import LeftSiteInfo from "./LeftSiteInfo.vue"
import StatisticCount from "./StatisticCount.vue"
import { useRouter } from 'vue-router';
import ComLink from './leftsite/ComLink.vue';
import LeftSiteInfo from "./leftsite/LeftSiteInfo.vue"
import StatisticCount from "./rightsite/StatisticCount.vue"
import NavigateMenu from './header/NavigateMenu.vue';
import { homePageStore } from '@/stores';
import CarouselImg from './header/CarouselImg.vue';
import { HomeOutlined, HighlightOutlined, ProfileOutlined, CameraOutlined, UsergroupDeleteOutlined } from '@ant-design/icons-vue';
import type { MenuProps } from 'ant-design-vue';
import HeatMap from './rightsite/HeatMap.vue';
import RandomArticle from './rightsite/RandomArticle.vue';
import TagCloud from './rightsite/TagCloud.vue';
import AplayerComponent from './rightsite/AplayerComponent.vue';
import FooterContent from './footer/FooterContent.vue';
import CataloGue from './leftsite/CataloGue.vue';
const router = useRouter()
const author = ref("SunFree.")
const { idShow } = homePageStore()
const route=useRoute()
const router=useRouter()
const { show_menu, show_carousel, show_author, show_anchornDown } = toRefs(idShow)
const mainCss = reactive({
marginTop: "0px"
})
// const current = ref<string[]>(['home']);
// const items = ref<MenuProps['items']>([
// {
// key: 'home',
// icon: () => h(HomeOutlined),
// label: '',
// title: '',
// },
// {
// key: 'blog',
// icon: () => h(HighlightOutlined),
// label: '',
// title: '',
// },
// {
// key: 'diary',
// icon: () => h(ProfileOutlined),
// label: '',
// title: '',
// },
// {
// key: 'album',
// icon: () => h(CameraOutlined),
// label: '',
// title: '',
// children: [
// {
// label: '',
// key: 'personself',
// },
// {
// label: '',
// key: 'otherimg',
// },
// ],
// },
// {
// key: 'chart',
// icon: () => h(UsergroupDeleteOutlined),
// label: '',
// title: '',
// },
// {
// key: 'aboutme',
// icon: () => h(UsergroupDeleteOutlined),
// label: 'sunfree',
// title: 'sunfree',
// },
// ]);
// const searchValue = ref("")
// const jumpMenu = ({ key }: { key: string }) => {
// router.push(`/${key}`)
// };
const updateCarouselVisibility = (routeName: any) => {
handleScrollEnabled.value = false;
if (scrollbar.value) {
@ -218,16 +86,13 @@ const updateCarouselVisibility = (routeName: any) => {
mainCss.marginTop = '48px';
}
};
const mainCss = reactive({
marginTop: "0px"
})
router.beforeEach((to, _, next) => {
updateCarouselVisibility(to.name);
next();
});
//
// const articleTitle = ref<string>('');
// const onSearch = (searchValue: string) => {
// console.log('use value', searchValue);
// console.log('or use this.value', articleTitle.value);
// };
/**
* 滚动条操作
@ -255,285 +120,10 @@ const downScroll = () => {
}
};
/**
* 左侧栏
*/
//
// const classticlist = ref<classticInterface[]>([])
// const classticList = async () => {
// try {
// await get("/classtics/list").then(response => {
// if (response) {
// classticlist.value = response.data.data.map((item: any, index: any) => ({
// key: (index + 1).toString(),
// header: item.header,
// text: item.text,
// descr: item.descr
// }));
// } else {
// console.log("the interface request data does not exist!")
// }
// })
// } catch (error) {
// console.error("Failed to fetch data", error);
// }
// }
// const comLinkClick = (url: string) => {
// if (url.startsWith('http://') || url.startsWith('https://')) {
// window.open(url, "_blank"); // 使 window.location.href
// } else {
// router.push(url); // 使 Vue Router push
// }
// }
// const comlinklist = ref<comLinkInterface[]>([])
// const comLinkList = async () => {
// try {
// await get("/comlinks/list").then(response => {
// if (response) {
// comlinklist.value = response.data.data.map((item: any, index: any) => ({
// key: (index + 1).toString(),
// id: item.id,
// linktext: item.linktext,
// linkurl: item.linkurl,
// descr: item.descr
// }));
// } else {
// console.log("the interface request data does not exist!")
// }
// })
// } catch (error) {
// console.error("Failed to fetch data", error);
// }
// }
// const activeKey = ref(['']);
// const customStyle = 'background: #F5F5F5;border-radius: 4px;margin-bottom: 12px;border: 0;overflow: hidden';
// const buttons = ref([
// { icon: iconComponents.CravatarLined, url: 'https://cravatar.cn/' },
// { icon: iconComponents.QQLined, url: '/qqcode' },
// { icon: iconComponents.WechatLined, url: '/wechatcode' },
// { icon: iconComponents.MusicLined, url: 'https://music.163.com/#/playlist?id=160266689' },
// { icon: iconComponents.GitHubLined, url: 'https://gitee.com/c_panda' },
// ]);
// const handleClick = (url: string) => {
// window.open(url)
// }
/**
* 右侧栏
*/
//
// const createData = new Date('2024-07-12');
// const currentDate = ref(new Date());
// const getDateOnly = (date: Date) => {
// return new Date(date.getFullYear(), date.getMonth(), date.getDate());
// };
// //
// const daysDifference = computed(() => {
// const oneDay = 24 * 60 * 60 * 1000; //
// const diffTime = Math.abs(getDateOnly(currentDate.value).getTime() - getDateOnly(createData).getTime());
// return Math.floor(diffTime / oneDay); // 使Math.floor
// });
// const diaryTotal = ref();
// const blogTotal = ref();
// const diaryList = async () => {
// try {
// const response = await get('/diarys/list');
// diaryTotal.value = response.data.data.total; //
// } catch (error) {
// console.error('Failed to fetch data', error);
// }
// };
// const blogList = async () => {
// try {
// const response = await get("/blogs/list");
// if (response) {
// blogTotal.value = response.data.data.total;
// } else {
// console.log("bloglist is not exits")
// }
// } catch (error) {
// console.log("bloglist is error")
// }
// }
// //
// const updateCurrentDate = () => {
// currentDate.value = new Date();
// };
// const statistics = reactive([
// {
// id: "1",
// title: "DAYS",
// counts: daysDifference
// },
// {
// id: "2",
// title: "DIARYS",
// counts: diaryTotal
// },
// {
// id: "3",
// title: "BLOGS",
// counts: blogTotal
// },
// ])
//
const heat = ref(null);
const current = ref<string[]>(['home']);
function generateDates(numDays: number) {
const dates = [];
const currentDate = new Date();
for (let i = 0; i < numDays; i++) {
const date = new Date(currentDate);
date.setDate(currentDate.getDate() - i);
dates.push(
{ date: date.toISOString().split('T')[0], writCount: 0 }
); //
}
return dates;
}
// 60
const data = generateDates(60);
//
const rawData = ref<any[]>([]);
const statisticList = async () => {
await get("/statistics/list").then(response => {
rawData.value = response.data.data
rawData.value.forEach(newDataItem => {
const item = newData.find((d: any) => d.date === newDataItem.date);
if (item) {
item.writCount = newDataItem.writCount;
}
});
let formattedData = newData.map((item: any, index: number) => {
return [index % 15, Math.floor(index / 15), item.writCount]
});
formattedList(formattedData)
createEcharts(heat, heatMapData);
})
}
const formattedList = (val: any) => {
heatMapData.series[0].data = val
}
const newData = <any>[];
for (let i = 0; i < 60; i += 15) {
// 15
const chunk = data.slice(i, i + 15).reverse();
newData.push(...chunk);
}
const heatMapData = reactive(
{
tooltip: {
position: 'top',
formatter: function (params: any) {
const item = newData[params.dataIndex];
if (item.writCount > 0) {
return `${item.date}<br/>COMMENTS: ${item.writCount}`;
} else {
return `${item.date}`;
}
}
},
grid: {
left: '0', //
right: '0', //
top: '0', //
bottom: '0',
},
xAxis: {
type: 'category',
data: Array.from({ length: 15 }, (_, i) => `Col ${i + 1}`),
splitArea: {
show: true
},
show: false
},
yAxis: {
type: 'category',
data: ['Row 1', 'Row 2', 'Row 3', 'Row 4'],
splitArea: {
show: false
},
show: false
},
visualMap: {
min: 0,
max: 20,
calculable: true,
orient: 'horizontal',
left: 'center',
bottom: '15%',
show: false,
inRange: {
color: ['rgba(182,181,178,0.01)', 'rgba(157,156,153,1)'] // 0, 0
}
},
series: [{
type: 'heatmap',
data: [],
itemStyle: {
borderColor: 'rgb(231,229,225,0.5)', //
borderWidth: 0.5, //
},
label: {
show: false,
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)',
borderColor: '#fff', //
borderWidth: 2 //
}
}
}]
}
)
//
const random = ref();
const homepagelist = ref<homePageInterface[]>([])
//
const homePageList = async () => {
await get(
"/statistics/homepage"
).then(response => {
homepagelist.value = response.data.data.sort(() => 0.5 - Math.random()).slice(0, 3);
})
}
const labellist = ref<labelInterface[]>([])
const randomColor = () => {
const labelColor = ref(["processing", "success", "error", "warning", "magenta", "red", "volcano", "orange", "gold", "lime", "green", "cyan", "blue", "geekblue", "purple"])
return labelColor.value[Math.floor(Math.random() * labelColor.value.length)];
}
const labelList = async () => {
try {
await get("/labels/list").then(response => {
if (response) {
labellist.value = response.data.data;
}
})
} catch (error) {
}
}
onMounted(() => {
// classticList()
// blogList()
labelList()
// diaryList()
// comLinkList()
statisticList()
// updateCurrentDate();
homePageList()
// setInterval(updateCurrentDate, 24 * 60 * 60 * 1000);
createEcharts(heat, heatMapData);
nextTick(() => {
const authorElement = document.querySelector('.author');
if (authorElement) {
@ -558,79 +148,12 @@ onMounted(() => {
showCursor: false //
});
};
const aplayerContainer = document.getElementById('aplayer');
if (aplayerContainer) {
new APlayer({
container: aplayerContainer,
mini: false,
autoplay: false,
theme: '#FADFA3',
loop: 'all',
order: 'random',
preload: 'auto',
volume: 0.7,
mutex: true,
listFolded: true,
listMaxHeight: 90,
lrcType: 3,
audio: [
// {
// name: 'name1',
// artist: 'artist1',
// url: 'url1.mp3',
// cover: 'cover1.jpg',
// lrc: 'lrc1.lrc',
// theme: '#ebd0c2'
// },
// {
// name: 'name2',
// artist: 'artist2',
// url: 'url2.mp3',
// cover: 'cover2.jpg',
// lrc: 'lrc2.lrc',
// theme: '#46718b'
// }
]
});
}
});
scrollbar.value = document.querySelector('.simplebar-content-wrapper');
})
// watch(
// () => route.name,
// (newRouteName) => {
// updateCarouselVisibility(newRouteName);
// if (newRouteName) {
// //
// const menuItem = items.value?.find((item: any) => {
// //
// // route.name route.path
// if (newRouteName === 'blogdetail') {
// return item.key === 'blog'; //
// } else if (newRouteName === 'diarydetail') {
// return item.key === 'diary'; //
// } else {
// return item.key === newRouteName; //
// }
// });
// if (menuItem) {
// current.value = [menuItem.key as string];
// } else {
// current.value = ['home']; // 'home'
// }
// } else {
// current.value = ['home']; // route.name 'home'
// }
// },
// { immediate: true }
// );
</script>
<style scoped>
/* 作者名称 */
.author {
position: absolute;
@ -684,101 +207,19 @@ onMounted(() => {
background-color: rgba(5, 5, 5, 0.08);
}
.leftBar {
width: 15%;
}
.leftBar img {
/* 图片自适应容器并保持宽高比例 */
aspect-ratio: 1/1;
}
.leftBar>* {
margin-bottom: 24px;
}
.leftBar h1 {
text-align: center;
font-family: Georgia, 'Times New Roman', Times, serif;
}
.leftBar .cardText {
min-height: 60px;
text-align: center;
}
.leftBar>:first-child .button-group {
display: flex;
margin: 0 12px 24px 12px;
justify-content: space-between;
}
.leftBar,
.rightBar {
width: 15%;
}
.leftBar>*,
.rightBar>* {
margin-bottom: 24px;
}
.rightBar .article-text {
margin: 12px 0;
}
.rightBar .article-text span {
margin-right: 12px;
font-family: "Lato, 'Helvetica Neue', Arial, Helvetica, sans-serif";
}
#aplayer {
margin: 0;
}
.footer {
display: flex;
flex-direction: column;
align-items: center;
}
.footer a {
color: #6e5555;
}
.footer .miit {
margin: 48px 0 0 0;
}
.footer .badge {
margin: 48px 0;
}
.footer .badge .tag-nav {
background-color: #505050;
}
.footer .miit-style {
padding: 0 0 0 6px;
}
.footer .badge span {
padding: 0 6px;
color: white;
}
.footer .badge .tag-main-first {
background-color: rgb(3, 3, 250);
}
.footer .badge .tag-main-second {
background-color: rgb(235, 15, 228);
}
.footer .badge .tag-main-third {
background-color: rgb(10, 207, 39);
}
.footer .badge .tag-main-fourth {
background-color: rgb(229, 131, 12);
}
</style>

91
src/components/blogs/footer/FooterContent.vue

@ -0,0 +1,91 @@
<template>
<div class="miit">
<a-space wrap>
<a-text type="link">
<component :is=iconComponents.BanQuanLined />
<span class="miit-style">Copyright © 2024</span>
</a-text>
<a-button type="link" href="/home">
<component :is=iconComponents.TitleOutLined />
<span>SunFree</span>
</a-button>
<a-button type="link" href="https://beian.miit.gov.cn/" target="_blank">
<component :is=iconComponents.IcpLined />
<span>苏ICP备2024067473号-1</span>
</a-button>
<a-button type="link" href="https://beian.mps.gov.cn/#/query/webSearch" target="_blank">
<component :is=iconComponents.GongAnLined />
<span>苏公网安备32021402003003号</span>
</a-button>
</a-space>
</div>
<div class="badge">
<a-space wrap>
<a-button type="link" class="badge-button" href="https://cn.vuejs.org/" target="_blank">
<span class="tag-nav">front-end</span>
<span class="tag-main-first">Vue.js</span>
</a-button>
<a-button type="link" class="badge-button" href="https://fastapi.tiangolo.com/zh/" target="_blank">
<span class="tag-nav">back-end</span>
<span class="tag-main-second">FastAPI</span>
</a-button>
<a-button type="link" class="badge-button" href="https://www.antdv.com/docs/vue/introduce-cn/"
target="_blank">
<span class="tag-nav">UI</span>
<span class="tag-main-third">Ant Design Vue</span>
</a-button>
<a-button type="link" class="badge-button" href="https://cloud.tencent.com/" target="_blank">
<span class="tag-nav">VPS</span>
<span class="tag-main-fourth">tencent cloud</span>
</a-button>
</a-space>
</div>
</template>
<script setup lang='ts'>
import iconComponents from "@/assets/index";
</script>
<style scoped>
a {
color: #6e5555;
}
.miit {
margin: 48px 0 0 0;
}
.badge {
margin: 48px 0;
}
.badge .tag-nav {
background-color: #505050;
}
.miit-style {
padding: 0 0 0 6px;
}
.badge span {
padding: 0 6px;
color: white;
}
.badge .tag-main-first {
background-color: rgb(3, 3, 250);
}
.badge .tag-main-second {
background-color: rgb(235, 15, 228);
}
.badge .tag-main-third {
background-color: rgb(10, 207, 39);
}
.badge .tag-main-fourth {
background-color: rgb(229, 131, 12);
}
</style>

30
src/components/blogs/header/NavigateMenu.vue

@ -9,7 +9,7 @@
<script setup lang='ts'>
import { ref, toRefs, watch, h } from 'vue';
import router from '@/router';
import { homePageStore } from '@/stores';
import { homePageStore } from '@/stores/index';
import { useRoute } from 'vue-router';
import { HomeOutlined, HighlightOutlined, ProfileOutlined, CameraOutlined, UsergroupDeleteOutlined } from '@ant-design/icons-vue';
import type { MenuProps } from 'ant-design-vue';
@ -81,26 +81,28 @@ watch(
(newRouteName) => {
props.updateCarouselVisibility(newRouteName);
if (newRouteName) {
//
const menuItem = items.value?.find((item: any) => {
//
// route.name route.path
if (newRouteName === 'blogdetail') {
return item.key === 'blog'; //
} else if (newRouteName === 'diarydetail') {
return item.key === 'diary'; //
} else {
return item.key === newRouteName; //
const findMenuItem = (items: any): any => {
if (!items) return null;
for (const item of items) {
if (item.key === newRouteName) {
return item;
}
if (item.children) {
const found = findMenuItem(item.children);
if (found) return found;
}
});
}
return null;
};
const menuItem = findMenuItem(items.value);
if (menuItem) {
current.value = [menuItem.key as string];
} else {
current.value = ['home']; // 'home'
current.value = ['home'];
}
} else {
current.value = ['home']; // route.name 'home'
current.value = ['home'];
}
},
{ immediate: true }

11
src/components/blogs/leftsite/CataloGue.vue

@ -0,0 +1,11 @@
<template>
</template>
<script setup lang='ts'>
</script>
<style>
</style>

0
src/components/blogs/ComLink.vue → src/components/blogs/leftsite/ComLink.vue

0
src/components/blogs/LeftSiteInfo.vue → src/components/blogs/leftsite/LeftSiteInfo.vue

0
src/components/blogs/QQCode.vue → src/components/blogs/leftsite/QQCode.vue

0
src/components/blogs/WechatCode.vue → src/components/blogs/leftsite/WechatCode.vue

58
src/components/blogs/rightsite/AplayerComponent.vue

@ -0,0 +1,58 @@
<template>
<a-card hoverable>
<template #cover>
<div id="aplayer"></div>
</template>
</a-card>
</template>
<script setup lang='ts'>
import 'APlayer/dist/APlayer.min.css';
import APlayer from 'APlayer';
import { nextTick, onMounted } from 'vue';
onMounted(() => {
nextTick(() => {
const aplayerContainer = document.getElementById('aplayer');
if (aplayerContainer) {
new APlayer({
container: aplayerContainer,
mini: false,
autoplay: false,
theme: '#FADFA3',
loop: 'all',
order: 'random',
preload: 'auto',
volume: 0.7,
mutex: true,
listFolded: true,
listMaxHeight: 90,
lrcType: 3,
audio: [
// {
// name: 'name1',
// artist: 'artist1',
// url: 'url1.mp3',
// cover: 'cover1.jpg',
// lrc: 'lrc1.lrc',
// theme: '#ebd0c2'
// },
// {
// name: 'name2',
// artist: 'artist2',
// url: 'url2.mp3',
// cover: 'cover2.jpg',
// lrc: 'lrc2.lrc',
// theme: '#46718b'
// }
]
});
}
})
})
</script>
<style scoped>
#aplayer {
margin: 0;
}
</style>

134
src/components/blogs/rightsite/HeatMap.vue

@ -0,0 +1,134 @@
<template>
<a-card hoverable>
<template #cover>
<div class="heatmap" style="border-right: rgba(0, 0, 0, 0.5);">
<div ref="heat" style="height: 100%;"></div>
</div>
</template>
</a-card>
</template>
<script setup lang='ts'>
import { get } from '@/tools/request';
import { ref, reactive, onMounted } from 'vue';
import { createEcharts } from "@/hooks/intex"
const heat = ref(null);
function generateDates(numDays: number) {
const dates = [];
const currentDate = new Date();
for (let i = 0; i < numDays; i++) {
const date = new Date(currentDate);
date.setDate(currentDate.getDate() - i);
dates.push(
{ date: date.toISOString().split('T')[0], writCount: 0 }
); //
}
return dates;
}
// 60
const data = generateDates(60);
//
const rawData = ref<any[]>([]);
const statisticList = async () => {
await get("/statistics/list").then(response => {
rawData.value = response.data.data
rawData.value.forEach(newDataItem => {
const item = newData.find((d: any) => d.date === newDataItem.date);
if (item) {
item.writCount = newDataItem.writCount;
}
});
let formattedData = newData.map((item: any, index: number) => {
return [index % 15, Math.floor(index / 15), item.writCount]
});
formattedList(formattedData)
createEcharts(heat, heatMapData);
})
}
const formattedList = (val: any) => {
heatMapData.series[0].data = val
}
const newData = <any>[];
for (let i = 0; i < 60; i += 15) {
// 15
const chunk = data.slice(i, i + 15).reverse();
newData.push(...chunk);
}
const heatMapData = reactive(
{
tooltip: {
position: 'top',
formatter: function (params: any) {
const item = newData[params.dataIndex];
if (item.writCount > 0) {
return `${item.date}<br/>COMMENTS: ${item.writCount}`;
} else {
return `${item.date}`;
}
}
},
grid: {
left: '0', //
right: '0', //
top: '0', //
bottom: '0',
},
xAxis: {
type: 'category',
data: Array.from({ length: 15 }, (_, i) => `Col ${i + 1}`),
splitArea: {
show: true
},
show: false
},
yAxis: {
type: 'category',
data: ['Row 1', 'Row 2', 'Row 3', 'Row 4'],
splitArea: {
show: false
},
show: false
},
visualMap: {
min: 0,
max: 20,
calculable: true,
orient: 'horizontal',
left: 'center',
bottom: '15%',
show: false,
inRange: {
color: ['rgba(182,181,178,0.01)', 'rgba(157,156,153,1)'] // 0, 0
}
},
series: [{
type: 'heatmap',
data: [],
itemStyle: {
borderColor: 'rgb(231,229,225,0.5)', //
borderWidth: 0.5, //
},
label: {
show: false,
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)',
borderColor: '#fff', //
borderWidth: 2 //
}
}
}]
}
)
onMounted(() => {
statisticList()
createEcharts(heat, heatMapData);
})
</script>
<style></style>

49
src/components/blogs/rightsite/RandomArticle.vue

@ -0,0 +1,49 @@
<template>
<a-card title="随机文章" :bordered="false" hoverable>
<div v-for="article in homepagelist">
<div v-if="article.blogtitle">
<div class="article-text">
<span>{{ article.blogtitle }}</span>
<span>{{ article.create_at }}</span>
</div>
<a-image :preview="false" :width="300" :src="article.imglink" />
</div>
<div v-if="article.diarytitle">
<div class="article-text">
<span>{{ article.diarytitle }}</span>
<span>{{ article.create_at }}</span>
</div>
<a-image :preview="false" :width="300" :src="article.imglink" />
</div>
</div>
</a-card>
</template>
<script setup lang='ts'>
import { get } from '@/tools/request';
import { onMounted, ref } from 'vue';
import type { homePageInterface } from '@/api';
const homepagelist = ref<homePageInterface[]>([])
//
const homePageList = async () => {
await get(
"/statistics/homepage"
).then(response => {
homepagelist.value = response.data.data.sort(() => 0.5 - Math.random()).slice(0, 3);
})
}
onMounted(()=>{
homePageList()
})
</script>
<style scoped>
.article-text {
margin: 12px 0;
}
.article-text span {
margin-right: 12px;
font-family: "Lato, 'Helvetica Neue', Arial, Helvetica, sans-serif";
}
</style>

0
src/components/blogs/StatisticCount.vue → src/components/blogs/rightsite/StatisticCount.vue

33
src/components/blogs/rightsite/TagCloud.vue

@ -0,0 +1,33 @@
<template>
<a-card title="标签云" :bordered="false" hoverable>
<a-tag :color=randomColor() v-for="label in labellist">{{ label.labelname }}</a-tag>
</a-card>
</template>
<script setup lang='ts'>
import { onMounted, ref } from 'vue';
import type { labelInterface } from '@/api/admin';
import { get } from '@/tools/request';
const randomColor = () => {
const labelColor = ref(["processing", "success", "error", "warning", "magenta", "red", "volcano", "orange", "gold", "lime", "green", "cyan", "blue", "geekblue", "purple"])
return labelColor.value[Math.floor(Math.random() * labelColor.value.length)];
}
const labellist = ref<labelInterface[]>([])
const labelList = async () => {
try {
await get("/labels/list").then(response => {
if (response) {
labellist.value = response.data.data;
}
})
} catch (error) {
}
}
onMounted(()=>{
labelList()
})
</script>
<style></style>

4
src/router/blog.ts

@ -62,12 +62,12 @@ const blogRoute:Array<RouteRecordRaw>=[
{
path:"/qqcode",
name:"qqcode",
component:()=>import("@/components/blogs/QQCode.vue"),
component:()=>import("@/components/blogs/leftsite/QQCode.vue"),
},
{
path:"/wechatcode",
name:"wechatcode",
component:()=>import("@/components/blogs/WechatCode.vue"),
component:()=>import("@/components/blogs/leftsite/WechatCode.vue"),
},
]
export default blogRoute

15
src/stores/index.ts

@ -22,6 +22,21 @@ export const blogStore = defineStore("blog", () => {
return { delControl }
})
export const useBlogStore = defineStore('catalogue', () => {
const blogContent = ref('');
const titles = ref<{ title: string; lineIndex: string; indent: number }[]>([]);
const setBlogContent = (content: string) => {
blogContent.value = content;
};
const setTitles = (newTitles: { title: string; lineIndex: string; indent: number }[]) => {
titles.value = newTitles;
};
return { blogContent, titles, setBlogContent, setTitles };
});
export const diaryStore = defineStore("diary", () => {
const delControl = reactive({
open: false,

4
src/views/admin/ClassticManageView.vue

@ -19,7 +19,7 @@
<a-form-item label="语录内容" name="text" :rules="[{ required: true, message: '请输入语录内容!' }]">
<a-input v-model:value="addList.text" />
</a-form-item>
<a-form-item label="备注" name="descr" :rules="[{ required: true, message: '请输入备注!' }]">
<a-form-item label="备注" name="descr">
<a-input v-model:value="addList.descr" />
</a-form-item>
</a-form>
@ -36,7 +36,7 @@
<a-form-item label="语录内容" name="text" :rules="[{ required: true, message: '请修改语录内容!' }]">
<a-input v-model:value="editList.text" />
</a-form-item>
<a-form-item label="备注" name="descr" :rules="[{ required: true, message: '请修改备注!' }]">
<a-form-item label="备注" name="descr">
<a-input v-model:value="editList.descr" />
</a-form-item>
</a-form>

4
src/views/blog/blogcontent/BlogDetailView.vue

@ -1,9 +1,9 @@
<template>
<div id="blogDetail">
<!-- <div v-for="anchor in titles" :style="{ padding: `4px 0 4px ${anchor.indent * 20}px` }"
<div v-for="anchor in titles" :style="{ padding: `4px 0 4px ${anchor.indent * 20}px` }"
@click="handleAnchorClick(anchor)" :key="anchor.lineIndex">
<a style="cursor: pointer">{{ anchor.title }}</a>
</div> -->
</div>
<a-card>
<v-md-preview v-if="text.blogcontent" :text="text.blogcontent" v-bind="previewProps" />
</a-card>

3
tsconfig.json

@ -7,5 +7,6 @@
{
"path": "./tsconfig.app.json"
}
]
],
}
Loading…
Cancel
Save