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