18 changed files with 451 additions and 616 deletions
-
627src/components/blogs/HomePage.vue
-
91src/components/blogs/footer/FooterContent.vue
-
34src/components/blogs/header/NavigateMenu.vue
-
11src/components/blogs/leftsite/CataloGue.vue
-
0src/components/blogs/leftsite/ComLink.vue
-
0src/components/blogs/leftsite/LeftSiteInfo.vue
-
0src/components/blogs/leftsite/QQCode.vue
-
0src/components/blogs/leftsite/WechatCode.vue
-
58src/components/blogs/rightsite/AplayerComponent.vue
-
134src/components/blogs/rightsite/HeatMap.vue
-
49src/components/blogs/rightsite/RandomArticle.vue
-
0src/components/blogs/rightsite/StatisticCount.vue
-
33src/components/blogs/rightsite/TagCloud.vue
-
4src/router/blog.ts
-
15src/stores/index.ts
-
4src/views/admin/ClassticManageView.vue
-
4src/views/blog/blogcontent/BlogDetailView.vue
-
3tsconfig.json
@ -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> |
@ -0,0 +1,11 @@ |
|||||
|
<template> |
||||
|
|
||||
|
</template> |
||||
|
|
||||
|
<script setup lang='ts'> |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
|
||||
|
</style> |
@ -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> |
@ -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> |
@ -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,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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue