Compare commits
8 Commits
fb109cd9ba
...
4c0d15b255
Author | SHA1 | Date | |
---|---|---|---|
4c0d15b255 | |||
4def4530a7 | |||
d4b8191eba | |||
011cd6a549 | |||
999ae23e4c | |||
101f2e082b | |||
1946e3a10e | |||
9418fcb155 |
24
app.vue
24
app.vue
@ -1,12 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const loading = ref<HTMLDivElement | null>(null)
|
const loading = ref<HTMLDivElement | null>(null),
|
||||||
const {$mitt} = useNuxtApp()
|
{ $mitt } = useNuxtApp()
|
||||||
|
|
||||||
$mitt.on('startLoading', on => {
|
$mitt.on('startLoading', (on) => {
|
||||||
if (on) {
|
if (on) {
|
||||||
loading.value?.classList.remove('stop')
|
loading.value?.classList.remove('stop')
|
||||||
removeMobileTopColor()
|
removeMobileTopColor()
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
loading.value?.classList.add('stop')
|
loading.value?.classList.add('stop')
|
||||||
setMobileTopColor()
|
setMobileTopColor()
|
||||||
}
|
}
|
||||||
@ -14,11 +15,14 @@ $mitt.on('startLoading', on => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="loading" class="loading"></div>
|
<div
|
||||||
|
ref="loading"
|
||||||
|
class="loading"
|
||||||
|
/>
|
||||||
<div class="relative flex min-h-screen flex-col overflow-hidden bg-blue-50">
|
<div class="relative flex min-h-screen flex-col overflow-hidden bg-blue-50">
|
||||||
<nav-bar/>
|
<nav-bar />
|
||||||
<nuxt-page/>
|
<nuxt-page />
|
||||||
<footer-main/>
|
<footer-main />
|
||||||
<alert-notification/>
|
<alert-notification />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -10,14 +10,21 @@ defineProps<{
|
|||||||
<div class="flex flex-grow flex-col items-center justify-center p-4 text-center min-h-[30rem]">
|
<div class="flex flex-grow flex-col items-center justify-center p-4 text-center min-h-[30rem]">
|
||||||
<div class="w-full max-w-md rounded-xl bg-white p-6 shadow-lg">
|
<div class="w-full max-w-md rounded-xl bg-white p-6 shadow-lg">
|
||||||
<div class="mb-4 text-6xl">
|
<div class="mb-4 text-6xl">
|
||||||
<span v-if="icon" class="block text-blue-500">{{ icon }}</span>
|
<span
|
||||||
|
v-if="icon"
|
||||||
|
class="block text-blue-500"
|
||||||
|
>{{ icon }}</span>
|
||||||
<span v-else>❓</span>
|
<span v-else>❓</span>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="mb-2 text-2xl font-bold">{{ title }}</h1>
|
<h1 class="mb-2 text-2xl font-bold">
|
||||||
<p class="mb-6 text-gray-600">{{ message }}</p>
|
{{ title }}
|
||||||
|
</h1>
|
||||||
|
<p class="mb-6 text-gray-600">
|
||||||
|
{{ message }}
|
||||||
|
</p>
|
||||||
<router-link
|
<router-link
|
||||||
to="/"
|
to="/"
|
||||||
class="inline-block rounded-lg bg-blue-500 px-6 py-2 text-white shadow transition duration-200 hover:bg-blue-600"
|
class="inline-block rounded-lg bg-blue-500 px-6 py-2 text-white shadow transition duration-200 hover:bg-blue-600"
|
||||||
>
|
>
|
||||||
返回首页
|
返回首页
|
||||||
</router-link>
|
</router-link>
|
||||||
@ -27,4 +34,4 @@ defineProps<{
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,42 +1,43 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const {$mitt} = useNuxtApp()
|
const { $mitt } = useNuxtApp(),
|
||||||
const notifications = ref<INotification[]>([]);
|
notifications = ref<INotification[]>([]),
|
||||||
const typeClasses = {
|
typeClasses = {
|
||||||
success: 'bg-green-100 text-green-800 border border-green-400',
|
success: 'bg-green-100 text-green-800 border border-green-400',
|
||||||
error: 'bg-red-100 text-red-800 border border-red-400',
|
error: 'bg-red-100 text-red-800 border border-red-400',
|
||||||
warning: 'bg-yellow-100 text-yellow-800 border border-yellow-400',
|
warning: 'bg-yellow-100 text-yellow-800 border border-yellow-400',
|
||||||
info: 'bg-blue-100 text-blue-800 border border-blue-400',
|
info: 'bg-blue-100 text-blue-800 border border-blue-400',
|
||||||
};
|
},
|
||||||
|
|
||||||
const addNotification = (notification: INotification) => {
|
addNotification = (notification: INotification) => {
|
||||||
notifications.value.push(notification);
|
notifications.value.push(notification)
|
||||||
|
|
||||||
// 自动移除通知
|
// 自动移除通知
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
notifications.value.shift();
|
notifications.value.shift()
|
||||||
}, 5000);
|
}, 5000)
|
||||||
console.log(1)
|
console.log(1)
|
||||||
|
}
|
||||||
};
|
|
||||||
|
|
||||||
$mitt.on('eventBus', addNotification)
|
$mitt.on('eventBus', addNotification)
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
$mitt.off('eventBus', addNotification);
|
$mitt.off('eventBus', addNotification)
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="fixed top-5 right-5 z-50 space-y-4">
|
<div class="fixed top-5 right-5 z-50 space-y-4">
|
||||||
<div
|
<div
|
||||||
v-for="(notification, index) in notifications"
|
v-for="(notification, index) in notifications"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="[
|
:class="[
|
||||||
'p-4 rounded-lg shadow-lg transition-all duration-300',
|
'p-4 rounded-lg shadow-lg transition-all duration-300',
|
||||||
typeClasses[notification.level]
|
typeClasses[notification.level],
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<h3 class="text-lg font-bold">{{ notification.title }}</h3>
|
<h3 class="text-lg font-bold">
|
||||||
|
{{ notification.title }}
|
||||||
|
</h3>
|
||||||
<p>{{ notification.message }}</p>
|
<p>{{ notification.message }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,30 +1,34 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {computed} from "vue";
|
import { computed } from 'vue'
|
||||||
import {pos} from "ipx";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
post: IPost
|
post: IPost
|
||||||
}>()
|
}>(),
|
||||||
|
|
||||||
const pid = useAsyncData('pid', async () => (Math.floor(Math.random() * (7 - 1 + 1)) + 1).toString())
|
pid = useAsyncData('pid', async () => (Math.floor(Math.random() * (7 - 1 + 1)) + 1).toString()),
|
||||||
|
|
||||||
const thumbUrl = computed(() => props.post.fields.thumb.value)
|
thumbUrl = computed(() => props.post.fields.thumb.value),
|
||||||
|
|
||||||
const randomId = computed(() => pid.data.value!)
|
|
||||||
|
|
||||||
|
randomId = computed(() => pid.data.value!)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nuxt-link :title="post.title"
|
<nuxt-link
|
||||||
class="container relative overflow-hidden rounded-2xl bg-white transition-all duration-300 ease-in-out h-[31rem] hover:-translate-y-2 hover:cursor-pointer hover:shadow"
|
:title="post.title"
|
||||||
:to="{name: 'article-cid', params: {cid: post.cid}}">
|
class="container relative overflow-hidden rounded-2xl bg-white transition-all duration-300 ease-in-out h-[31rem] hover:-translate-y-2 hover:cursor-pointer hover:shadow"
|
||||||
|
:to="{ name: 'article-cid', params: { cid: post.cid } }"
|
||||||
|
>
|
||||||
<!-- 图片 -->
|
<!-- 图片 -->
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<!-- 渐变 -->
|
<!-- 渐变 -->
|
||||||
<div class="absolute top-0 right-0 bottom-0 left-0 bg-gradient-to-t from-white"></div>
|
<div class="absolute top-0 right-0 bottom-0 left-0 bg-gradient-to-t from-white" />
|
||||||
<img v-if="thumbUrl" :src="thumbUrl" alt=""
|
<img
|
||||||
class="aspect-video w-full h-auto object-cover object-center">
|
v-if="thumbUrl"
|
||||||
<card-article-default-image :id="randomId"/>
|
:src="thumbUrl"
|
||||||
|
alt=""
|
||||||
|
class="aspect-video w-full h-auto object-cover object-center"
|
||||||
|
>
|
||||||
|
<card-article-default-image :id="randomId" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 文字部分 -->
|
<!-- 文字部分 -->
|
||||||
<div class="px-5">
|
<div class="px-5">
|
||||||
@ -37,4 +41,4 @@ const randomId = computed(() => pid.data.value!)
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -5,17 +5,45 @@ defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<img src="~/assets/images/common/1.jpg" alt="" v-if="id === '1'"/>
|
<img
|
||||||
<img src="~/assets/images/common/2.jpg" alt="" v-else-if="id === '2'"/>
|
v-if="id === '1'"
|
||||||
<img src="~/assets/images/common/3.jpg" alt="" v-else-if="id === '3'"/>
|
src="~/assets/images/common/1.jpg"
|
||||||
<img src="~/assets/images/common/4.jpg" alt="" v-else-if="id === '4'"/>
|
alt=""
|
||||||
<img src="~/assets/images/common/5.jpg" alt="" v-else-if="id === '5'"/>
|
>
|
||||||
<img src="~/assets/images/common/6.jpg" alt="" v-else-if="id === '6'"/>
|
<img
|
||||||
<img src="~/assets/images/common/7.jpg" alt="" v-else-if="id === '7'"/>
|
v-else-if="id === '2'"
|
||||||
|
src="~/assets/images/common/2.jpg"
|
||||||
|
alt=""
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-else-if="id === '3'"
|
||||||
|
src="~/assets/images/common/3.jpg"
|
||||||
|
alt=""
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-else-if="id === '4'"
|
||||||
|
src="~/assets/images/common/4.jpg"
|
||||||
|
alt=""
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-else-if="id === '5'"
|
||||||
|
src="~/assets/images/common/5.jpg"
|
||||||
|
alt=""
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-else-if="id === '6'"
|
||||||
|
src="~/assets/images/common/6.jpg"
|
||||||
|
alt=""
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-else-if="id === '7'"
|
||||||
|
src="~/assets/images/common/7.jpg"
|
||||||
|
alt=""
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
img {
|
img {
|
||||||
@apply aspect-[16/10] w-full h-auto object-cover object-center
|
@apply aspect-[16/10] w-full h-auto object-cover object-center
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import {computed, onMounted, onUnmounted, ref} from "vue";
|
|
||||||
|
|
||||||
const currentPost = ref<IPost | null>(null)
|
|
||||||
|
|
||||||
const onOpenPost = (e: IPost) => {
|
|
||||||
currentPost.value = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
const onClosePost = () => {
|
|
||||||
currentPost.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const thumbUrl = computed(() => {
|
|
||||||
if (currentPost.value && currentPost.value.fields.thumb.value) {
|
|
||||||
return currentPost.value.fields.thumb.value
|
|
||||||
}
|
|
||||||
return getAssetURL(`${Math.floor(Math.random() * (7 - 1 + 1)) + 1}.jpg`)
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
emitter.on('openPost', onOpenPost)
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
emitter.off('openPost', onOpenPost)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="currentPost" class="fixed top-0 right-0 bottom-0 left-0 z-20 bg-black bg-opacity-60">
|
|
||||||
<div class="container mx-auto my-10 max-h-full overflow-hidden overscroll-y-contain rounded-3xl bg-white">
|
|
||||||
<!-- 标题栏 -->
|
|
||||||
<div class="relative w-full border-b-2 border-b-gray-200">
|
|
||||||
<!-- 文章头图 -->
|
|
||||||
<div class="min-h-52">
|
|
||||||
<img :src="thumbUrl" alt="" class="max-h-64 w-full object-cover object-center"/>
|
|
||||||
</div>
|
|
||||||
<!-- 预览标题 -->
|
|
||||||
<div
|
|
||||||
class="absolute top-0 right-0 left-0 h-12 truncate bg-black bg-opacity-60 pr-16 pl-8 text-white backdrop-blur-3xl py-1.5">
|
|
||||||
<span :title="currentPost.title" class="text-3xl font-bold">
|
|
||||||
文章预览 /
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- 关闭按钮 -->
|
|
||||||
<div
|
|
||||||
class="absolute top-0 right-2 flex h-9 w-9 cursor-pointer items-center justify-center rounded-full text-white transition-all my-1.5 hover:bg-gray-200 hover:text-black"
|
|
||||||
@click="onClosePost"
|
|
||||||
>
|
|
||||||
<font-awesome-icon :icon="['fas', 'xmark']" class="p-2"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 正文 -->
|
|
||||||
<div class="container overflow-y-auto p-8 h-[60vh] preview" v-html="currentPost.digest"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.preview {
|
|
||||||
h1 {
|
|
||||||
@apply mb-2 border-l-8 border-l-blue-400 text-3xl font-bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
@apply mb-2 border-l-4 border-l-blue-400 text-2xl font-bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
@apply mb-2 border-l-2 border-l-blue-400 text-xl font-bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
@apply text-lg font-bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
@apply text-sm font-bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
@apply line-clamp-1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -7,14 +7,15 @@ defineProps<{
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="my-16 relative">
|
<div class="my-16 relative">
|
||||||
<h3 class="text-5xl font-bold text-blue-400 relative z-10">{{ title }}</h3>
|
<h3 class="text-5xl font-bold text-blue-400 relative z-10">
|
||||||
<div class="h-12 w-auto text-5xl text-blue-200 absolute left-48 top-0 -translate-y-6">
|
{{ title }}
|
||||||
<font-awesome-icon :icon="icon"/>
|
</h3>
|
||||||
</div>
|
<div class="h-12 w-auto text-5xl text-blue-200 absolute left-48 top-0 -translate-y-6">
|
||||||
|
<font-awesome-icon :icon="icon" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,102 +1,129 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {computed, ref} from "vue";
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
// 使用 ref 跟踪鼠标的 x 和 y 坐标
|
// 使用 ref 跟踪鼠标的 x 和 y 坐标
|
||||||
const mouseX = ref(0)
|
const mouseX = ref(0),
|
||||||
const mouseY = ref(0)
|
mouseY = ref(0),
|
||||||
const characterUrl = ref("")
|
|
||||||
|
|
||||||
const typedString = ['Hello, Welcome to this site!^1000', '欢迎访问本网站!^1000']
|
typedString = ['Hello, Welcome to this site!^1000', '欢迎访问本网站!^1000'],
|
||||||
const littleWidget = [
|
littleWidget = [
|
||||||
{
|
{
|
||||||
icon: ['fab', 'qq'],
|
icon: ['fab', 'qq'],
|
||||||
url: "https://qm.qq.com/q/9fhtO8JJt0",
|
url: 'https://qm.qq.com/q/9fhtO8JJt0',
|
||||||
title: "QQ",
|
title: 'QQ',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ['fab', 'github-alt'],
|
icon: ['fab', 'github-alt'],
|
||||||
url: "https://github.com/Aurora1949",
|
url: 'https://github.com/Aurora1949',
|
||||||
title: "Github",
|
title: 'Github',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ['fab', 'weibo'],
|
icon: ['fab', 'weibo'],
|
||||||
url: "https://weibo.com/u/7923648952",
|
url: 'https://weibo.com/u/7923648952',
|
||||||
title: "微博"
|
title: '微博',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ['fab', 'steam-symbol'],
|
icon: ['fab', 'steam-symbol'],
|
||||||
url: "https://steamcommunity.com/id/cantyonion/",
|
url: 'https://steamcommunity.com/id/cantyonion/',
|
||||||
title: "Steam"
|
title: 'Steam',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
|
|
||||||
// 监听鼠标移动
|
// 监听鼠标移动
|
||||||
const handleMouseMove = (event: MouseEvent) => {
|
handleMouseMove = (event: MouseEvent) => {
|
||||||
// 根据窗口大小计算鼠标相对于中心位置的偏移比例
|
// 根据窗口大小计算鼠标相对于中心位置的偏移比例
|
||||||
mouseX.value = -(event.clientX / window.innerWidth - 0.5) * 100
|
mouseX.value = -(event.clientX / window.innerWidth - 0.5) * 100
|
||||||
mouseY.value = -(event.clientY / window.innerHeight - 0.5) * 100
|
mouseY.value = -(event.clientY / window.innerHeight - 0.5) * 100
|
||||||
}
|
},
|
||||||
|
|
||||||
const debouncedMouseMove = debounce(handleMouseMove, 5)
|
debouncedMouseMove = debounce(handleMouseMove, 5),
|
||||||
|
|
||||||
// 根据鼠标位置动态调整背景图像的位置
|
// 根据鼠标位置动态调整背景图像的位置
|
||||||
const backgroundStyle = computed(() => ({
|
backgroundStyle = computed(() => ({
|
||||||
backgroundPosition: `${mouseX.value}px ${mouseY.value}px`
|
backgroundPosition: `${mouseX.value}px ${mouseY.value}px`,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
characterUrl.value = getAssetURL('common/hoshino.png')
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :style="backgroundStyle"
|
<div
|
||||||
class="relative flex h-screen w-full items-center justify-center bg-blue-400 bg-fixed bg min-h-[40rem]"
|
:style="backgroundStyle"
|
||||||
@mousemove="debouncedMouseMove"
|
class="relative flex h-svh w-full items-center justify-center bg-blue-400 bg-fixed bg min-h-[40rem]"
|
||||||
|
@mousemove="debouncedMouseMove"
|
||||||
>
|
>
|
||||||
<div class="relative mx-4 w-full rounded-xl bg-white p-8 transition-transform duration-300 min-h-96
|
<div
|
||||||
|
class="relative mx-4 w-full rounded-xl bg-white p-8 transition-transform duration-300 min-h-96
|
||||||
md:mx-0 md:max-w-screen-md"
|
md:mx-0 md:max-w-screen-md"
|
||||||
>
|
>
|
||||||
<p class="text-gray-500">你好!欢迎来到</p>
|
<p class="text-gray-500">
|
||||||
|
你好!欢迎来到
|
||||||
|
</p>
|
||||||
<div class="relative mt-4 h-72 text-5xl text-blue-400 transition-all md:text-6xl lg:text-7xl">
|
<div class="relative mt-4 h-72 text-5xl text-blue-400 transition-all md:text-6xl lg:text-7xl">
|
||||||
<h1 style="font-family: BaconyScript,sans-serif">CantyOni_on's</h1>
|
<h1 style="font-family: BaconyScript,sans-serif">
|
||||||
<h1 class="font-bold">超级基地</h1>
|
CantyOni_on's
|
||||||
|
</h1>
|
||||||
|
<h1 class="font-bold">
|
||||||
|
超级基地
|
||||||
|
</h1>
|
||||||
<div class="absolute -z-10 text-blue-100 top-[3px] left-[3px]">
|
<div class="absolute -z-10 text-blue-100 top-[3px] left-[3px]">
|
||||||
<h1 class="" style="font-family: BaconyScript,sans-serif">CantyOni_on's</h1>
|
<h1
|
||||||
<h1 class="font-bold">超级基地</h1>
|
class=""
|
||||||
|
style="font-family: BaconyScript,sans-serif"
|
||||||
|
>
|
||||||
|
CantyOni_on's
|
||||||
|
</h1>
|
||||||
|
<h1 class="font-bold">
|
||||||
|
超级基地
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TypedJS -->
|
<!-- TypedJS -->
|
||||||
<div class="flex flex-row text-gray-500">
|
<div class="flex flex-row text-gray-500">
|
||||||
<vue-typed :auto-insert-css="true" :backSpeed="30"
|
<vue-typed
|
||||||
:loop="true"
|
:auto-insert-css="true"
|
||||||
:showCursor="true"
|
:back-speed="30"
|
||||||
:strings="typedString"
|
:loop="true"
|
||||||
:typeSpeed="50"
|
:show-cursor="true"
|
||||||
|
:strings="typedString"
|
||||||
|
:type-speed="50"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 社交媒体组件 -->
|
<!-- 社交媒体组件 -->
|
||||||
<div class="flex flex-row text-gray-500">
|
<div class="flex flex-row text-gray-500">
|
||||||
<widget-social-media-widget v-for="item in littleWidget"
|
<widget-social-media-widget
|
||||||
:key="item.url"
|
v-for="item in littleWidget"
|
||||||
:icon="item.icon"
|
:key="item.url"
|
||||||
:title="item.title"
|
:icon="item.icon"
|
||||||
:url="item.url"
|
:title="item.title"
|
||||||
class="h-10 w-10"
|
:url="item.url"
|
||||||
|
class="h-10 w-10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 人物背景 -->
|
<!-- 人物背景 -->
|
||||||
<div class="invisible absolute top-0 -right-6 -bottom-6 opacity-0 transition-all
|
<div
|
||||||
|
class="invisible absolute top-0 -right-6 -bottom-6 opacity-0 transition-all
|
||||||
sm:visible sm:opacity-100 lg:-top-28 lg:-right-28 lg:-bottom-10"
|
sm:visible sm:opacity-100 lg:-top-28 lg:-right-28 lg:-bottom-10"
|
||||||
>
|
>
|
||||||
<img :src="characterUrl" alt="" class="float-right h-full" draggable="false"/>
|
<img
|
||||||
|
src="~/assets/images/common/hoshino.png"
|
||||||
|
alt=""
|
||||||
|
class="float-right h-full"
|
||||||
|
draggable="false"
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 下箭头 -->
|
<!-- 下箭头 -->
|
||||||
<font-awesome-icon :icon="['fas', 'chevron-down']" beat class="absolute bottom-12 h-auto w-6 text-white"/>
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'chevron-down']"
|
||||||
|
beat
|
||||||
|
class="absolute bottom-12 h-auto w-6 text-white"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -1,85 +1,114 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {computed} from "vue";
|
import { computed } from 'vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
commit: IActivity<IContent>
|
commit: IStrictActivity
|
||||||
}>()
|
}>(),
|
||||||
|
|
||||||
const icon = computed(() => {
|
commit_ref_name = computed(() => props.commit.ref_name.substring(props.commit.ref_name.lastIndexOf('/') + 1)),
|
||||||
switch (props.commit.op_type) {
|
|
||||||
case "merge_pull_request":
|
|
||||||
return ['fas', 'code-merge']
|
|
||||||
case "create_repo":
|
|
||||||
return ['fab', 'git-alt']
|
|
||||||
case "commit_repo":
|
|
||||||
default:
|
|
||||||
return ['fas', 'code-commit']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const time = computed(() => timeDifference(props.commit.created))
|
icon = computed(() => {
|
||||||
|
switch (props.commit.op_type) {
|
||||||
|
case 'merge_pull_request':
|
||||||
|
return ['fas', 'code-merge']
|
||||||
|
case 'create_repo':
|
||||||
|
return ['fas', 'book-medical']
|
||||||
|
case 'delete_branch':
|
||||||
|
case 'delete_tag':
|
||||||
|
case 'commit_repo':
|
||||||
|
default:
|
||||||
|
return ['fas', 'code-commit']
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
const timeDifference = (dateString: string): string => {
|
time = computed(() => timeDifference(props.commit.created)),
|
||||||
const targetDate = new Date(dateString);
|
|
||||||
const currentDate = new Date();
|
|
||||||
|
|
||||||
const diffInMillis = currentDate.getTime() - targetDate.getTime();
|
timeDifference = (dateString: string): string => {
|
||||||
|
const targetDate = new Date(dateString),
|
||||||
|
currentDate = new Date(),
|
||||||
|
|
||||||
const minutes = Math.floor(diffInMillis / (1000 * 60));
|
diffInMillis = currentDate.getTime() - targetDate.getTime(),
|
||||||
const hours = Math.floor(diffInMillis / (1000 * 60 * 60));
|
|
||||||
const days = Math.floor(diffInMillis / (1000 * 60 * 60 * 24));
|
|
||||||
const months = Math.floor(diffInMillis / (1000 * 60 * 60 * 24 * 30));
|
|
||||||
const years = Math.floor(diffInMillis / (1000 * 60 * 60 * 24 * 365));
|
|
||||||
|
|
||||||
if (minutes < 60) {
|
minutes = Math.floor(diffInMillis / (1000 * 60)),
|
||||||
return `${minutes} 分钟前`;
|
hours = Math.floor(diffInMillis / (1000 * 60 * 60)),
|
||||||
} else if (hours < 24) {
|
days = Math.floor(diffInMillis / (1000 * 60 * 60 * 24)),
|
||||||
return `${hours} 小时前`;
|
months = Math.floor(diffInMillis / (1000 * 60 * 60 * 24 * 30)),
|
||||||
} else if (days < 30) {
|
years = Math.floor(diffInMillis / (1000 * 60 * 60 * 24 * 365))
|
||||||
return `${days} 天前`;
|
|
||||||
} else if (months < 12) {
|
|
||||||
return `${months} 个月前`;
|
|
||||||
} else {
|
|
||||||
return `${years} 年前`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClick = () => {
|
if (minutes < 60) {
|
||||||
window.open(props.commit.repo.html_url, '_self')
|
return `${minutes} 分钟前`
|
||||||
}
|
}
|
||||||
|
else if (hours < 24) {
|
||||||
|
return `${hours} 小时前`
|
||||||
|
}
|
||||||
|
else if (days < 30) {
|
||||||
|
return `${days} 天前`
|
||||||
|
}
|
||||||
|
else if (months < 12) {
|
||||||
|
return `${months} 个月前`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${years} 年前`
|
||||||
|
},
|
||||||
|
calcHeight = (n: number) => `${n * 2.5}rem`
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="group container relative flex h-36 w-full overflow-hidden rounded-2xl transition-all
|
<div class="relative bg-transparent p-4 transition-all duration-500 group lg:hover:scale-105 lg:hover:rounded-xl lg:hover:shadow">
|
||||||
hover:cursor-pointer hover:shadow-md"
|
<!-- 标题部分 -->
|
||||||
@click="handleClick"
|
<div class="relative flex items-center gap-4 text-lg">
|
||||||
>
|
<font-awesome-icon
|
||||||
<!-- 提交图标 -->
|
class="h-8 w-10"
|
||||||
<div class="before:absolute relative before:top-0 before:right-0 before:bottom-0 flex h-36 before:w-2 w-36
|
:icon="icon"
|
||||||
items-center justify-center bg-pink-300 before:bg-pink-300 text-5xl text-white before:opacity-0
|
/>
|
||||||
before:blur before:transition-all group-hover:before:opacity-100"
|
<p v-if="commit.op_type === 'commit_repo' && commit.content">
|
||||||
|
推送到了仓库 {{ commit.repo.owner.login }}/{{ commit.repo.name }} 的 <span>{{ commit_ref_name }}</span> 分支
|
||||||
|
</p>
|
||||||
|
<p v-else-if="commit.op_type === 'commit_repo' && commit.content === null">
|
||||||
|
于 {{ commit.repo.owner.login }}/{{ commit.repo.name }} 创建了分支 {{ commit_ref_name }}
|
||||||
|
</p>
|
||||||
|
<p v-else-if="commit.op_type === 'create_repo'">
|
||||||
|
创建了仓库 {{ commit.repo.owner.login }}/{{ commit.repo.name }}
|
||||||
|
</p>
|
||||||
|
<span class="absolute top-0 right-0 hidden lg:block">{{ time }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- 提交内容 -->
|
||||||
|
<div
|
||||||
|
v-if="commit.content"
|
||||||
|
class="ml-14 overflow-hidden transition-all duration-500 lg:invisible lg:opacity-0
|
||||||
|
lg:h-0 lg:group-hover:h-[999px] lg:group-hover:visible lg:group-hover:opacity-100"
|
||||||
|
:style="{ maxHeight: calcHeight(commit.content.Commits.length) }"
|
||||||
>
|
>
|
||||||
<font-awesome-icon :icon="icon" class="transition-all group-hover:scale-110"/>
|
<div
|
||||||
</div>
|
v-for="content in commit.content.Commits"
|
||||||
<!-- 内容 -->
|
:key="content.Sha1"
|
||||||
<div class="flex-1 overflow-hidden bg-white p-4">
|
class="my-2 flex items-center gap-2"
|
||||||
<div class="mr-12 truncate text-2xl font-bold">
|
>
|
||||||
仓库:{{ commit.repo.name }}
|
<font-awesome-icon
|
||||||
|
class="h-4 w-5"
|
||||||
|
:icon="icon"
|
||||||
|
/>
|
||||||
|
<!-- SHA1 -->
|
||||||
|
<nuxt-link
|
||||||
|
:to="`https://cantyonion.site/git/${commit.repo.owner.login}/${commit.repo.name}/commit/${content.Sha1}`"
|
||||||
|
class="rounded bg-gray-200 px-2 transition-all hover:bg-gray-400"
|
||||||
|
style="font-family: 'LXGW WenKai Mono', monospace"
|
||||||
|
>{{ content.Sha1.substring(0, 10) }}</nuxt-link>
|
||||||
|
<!-- 提交信息 -->
|
||||||
|
<span class="truncate text-nowrap">{{ content.Message }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-xl line-clamp-2">{{ commit.content.HeadCommit.Message }}</span>
|
<!-- 比较链接 -->
|
||||||
<span class="absolute top-0 right-0 m-4 hidden rounded bg-gray-300 px-2 sm:block">
|
<nuxt-link
|
||||||
{{ commit.content.HeadCommit.Sha1.slice(0, 10) }}
|
v-if="commit.content.Commits.length > 1"
|
||||||
</span>
|
:to="`https://cantyonion.site/git/${commit.content.CompareURL}`"
|
||||||
|
class="text-blue-500 hover:text-blue-700"
|
||||||
<span class="absolute right-0 bottom-0 m-4 rounded bg-gray-300 px-2">
|
style="font-family: 'LXGW WenKai Mono', monospace"
|
||||||
<font-awesome-icon :icon="['far', 'clock']" class="mr-1 text-sm"/>
|
>比较 {{ commit.content.Commits.length }} 提交 >></nuxt-link>
|
||||||
{{ time }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="absolute top-0 right-0 bottom-0 left-0 -z-10 transition-all duration-500 lg:group-hover:rounded-xl lg:group-hover:bg-white/10 lg:group-hover:backdrop-blur-2xl" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
type Widget = {
|
interface Widget {
|
||||||
icon: string[]
|
icon: string[]
|
||||||
url: string
|
url: string
|
||||||
title: string
|
title: string
|
||||||
}
|
}
|
||||||
// const welcomeWords = ['你好👋,很高兴认识你!']
|
// Const welcomeWords = ['你好👋,很高兴认识你!']
|
||||||
|
|
||||||
const littleWidget: Widget[] = [
|
const littleWidget: Widget[] = [
|
||||||
{
|
{
|
||||||
icon: ['fab', 'qq'],
|
icon: ['fab', 'qq'],
|
||||||
url: "tencent://message/?uin=1922471905&Site=&Menu=yes",
|
url: 'tencent://message/?uin=1922471905&Site=&Menu=yes',
|
||||||
title: "QQ",
|
title: 'QQ',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ['fab', 'github-alt'],
|
icon: ['fab', 'github-alt'],
|
||||||
url: "https://github.com/Aurora1949",
|
url: 'https://github.com/Aurora1949',
|
||||||
title: "Github",
|
title: 'Github',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ['fab', 'weibo'],
|
icon: ['fab', 'weibo'],
|
||||||
url: "https://weibo.com/u/7923648952",
|
url: 'https://weibo.com/u/7923648952',
|
||||||
title: "微博"
|
title: '微博',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ['fab', 'steam-symbol'],
|
icon: ['fab', 'steam-symbol'],
|
||||||
url: "https://steamcommunity.com/id/cantyonion/",
|
url: 'https://steamcommunity.com/id/cantyonion/',
|
||||||
title: "Steam"
|
title: 'Steam',
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -34,21 +34,32 @@ const littleWidget: Widget[] = [
|
|||||||
<div class="container my-16 flex w-full">
|
<div class="container my-16 flex w-full">
|
||||||
<!-- 头像部分 -->
|
<!-- 头像部分 -->
|
||||||
<div class="hidden h-32 w-32 overflow-hidden rounded-full bg-white hover:animate-pulse sm:block">
|
<div class="hidden h-32 w-32 overflow-hidden rounded-full bg-white hover:animate-pulse sm:block">
|
||||||
<img alt="" src="https://q.qlogo.cn/g?b=qq&nk=1922471905&s=640">
|
<img
|
||||||
|
alt=""
|
||||||
|
src="https://q.qlogo.cn/g?b=qq&nk=1922471905&s=640"
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 文字部分 -->
|
<!-- 文字部分 -->
|
||||||
<div class="container flex flex-1 flex-col justify-between px-4 sm:px-8">
|
<div class="container flex flex-1 flex-col justify-between px-4 sm:px-8">
|
||||||
<div class="text-4xl">Jeffrey Hsu</div>
|
<div class="text-4xl">
|
||||||
<div class="text-2xl text-gray-400">你好👋,很高兴认识你!</div>
|
Jeffrey Hsu
|
||||||
<!-- <vue-typed-js :strings="['First text', 'Second Text']">-->
|
</div>
|
||||||
<!-- <h1 class="typing"></h1>-->
|
<div class="text-2xl text-gray-400">
|
||||||
<!-- </vue-typed-js>-->
|
你好👋,很高兴认识你!
|
||||||
|
</div>
|
||||||
|
<!-- <vue-typed-js :strings="['First text', 'Second Text']"> -->
|
||||||
|
<!-- <h1 class="typing"></h1> -->
|
||||||
|
<!-- </vue-typed-js> -->
|
||||||
<div class="flex gap-4 text-xl">
|
<div class="flex gap-4 text-xl">
|
||||||
<a v-for="item in littleWidget"
|
<a
|
||||||
:key="item.url" :href=item.url :title=item.title
|
v-for="item in littleWidget"
|
||||||
class="flex h-10 w-10 items-center justify-center rounded-full bg-green-400 text-white transition-all hover:scale-125">
|
:key="item.url"
|
||||||
<font-awesome-icon :icon="item.icon"/>
|
:href="item.url"
|
||||||
|
:title="item.title"
|
||||||
|
class="flex h-10 w-10 items-center justify-center rounded-full bg-green-400 text-white transition-all hover:scale-125"
|
||||||
|
>
|
||||||
|
<font-awesome-icon :icon="item.icon" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -57,4 +68,4 @@ const littleWidget: Widget[] = [
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -6,46 +6,89 @@ defineProps<{
|
|||||||
errMsg?: object
|
errMsg?: object
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits(['reload'])
|
const emit = defineEmits(['reload']),
|
||||||
|
|
||||||
const handleReload = () => {
|
handleReload = () => {
|
||||||
emit('reload')
|
emit('reload')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="{'justify-center': (empty || error)}" class="container flex">
|
<div
|
||||||
|
:class="{ 'justify-center': (empty || error) }"
|
||||||
|
class="container flex"
|
||||||
|
>
|
||||||
<!-- 加载骨架 -->
|
<!-- 加载骨架 -->
|
||||||
<div v-if="loading" class="h-56 w-full text-2xl font-bold">
|
<div
|
||||||
|
v-if="loading"
|
||||||
|
class="h-56 w-full text-2xl font-bold"
|
||||||
|
>
|
||||||
<slot name="skeleton">
|
<slot name="skeleton">
|
||||||
<div class="grid w-full grid-cols-1 gap-4 overflow-hidden sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
<div class="grid w-full grid-cols-1 gap-4 overflow-hidden sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
<div v-for="index of 3" :key="index" class="h-56 animate-pulse rounded-2xl bg-gray-200"></div>
|
<div
|
||||||
|
v-for="index of 3"
|
||||||
|
:key="index"
|
||||||
|
class="h-56 animate-pulse rounded-2xl bg-gray-200"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="w-full">
|
<div
|
||||||
|
v-else
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
<!-- 错误处理 -->
|
<!-- 错误处理 -->
|
||||||
<div v-if="error" @click="handleReload">
|
<div
|
||||||
|
v-if="error"
|
||||||
|
@click="handleReload"
|
||||||
|
>
|
||||||
<div class="flex items-center justify-center gap-8">
|
<div class="flex items-center justify-center gap-8">
|
||||||
<p class="text-2xl text-gray-700 border-2 border-black py-2 px-8 rounded-full rounded-br-lg">载入错误</p>
|
<p class="text-2xl text-gray-700 border-2 border-black py-2 px-8 rounded-full rounded-br-lg">
|
||||||
<img class="h-32 w-auto relative" src="~/assets/images/common/plana_e.png" alt="">
|
载入错误
|
||||||
|
</p>
|
||||||
|
<img
|
||||||
|
class="h-32 w-auto relative"
|
||||||
|
src="~/assets/images/common/plana_e.png"
|
||||||
|
alt=""
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="errMsg" class="bg-white mt-4 p-4 border border-dashed rounded-md" style="font-family: 'LXGW WenKai Mono', monospace">
|
<div
|
||||||
|
v-if="errMsg"
|
||||||
|
class="bg-white mt-4 p-4 border border-dashed rounded-md"
|
||||||
|
style="font-family: 'LXGW WenKai Mono', monospace"
|
||||||
|
>
|
||||||
{{ errMsg }}
|
{{ errMsg }}
|
||||||
</div>
|
</div>
|
||||||
<p class="text-right text-xs">(点击重试)</p>
|
<p class="text-right text-xs">
|
||||||
|
(点击重试)
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<!-- 正式内容 -->
|
<!-- 正式内容 -->
|
||||||
<div v-if="empty">
|
<div v-if="empty">
|
||||||
<div v-if="empty" class="flex items-center justify-center gap-8" @click="handleReload">
|
<div
|
||||||
<p class="text-2xl text-gray-700 border-2 border-black py-2 px-8 rounded-full rounded-br-lg">最近没有动态~</p>
|
v-if="empty"
|
||||||
<img class="h-32 w-auto relative" src="~/assets/images/common/plana_e.png" alt="">
|
class="flex items-center justify-center gap-8"
|
||||||
|
@click="handleReload"
|
||||||
|
>
|
||||||
|
<p class="text-2xl text-gray-700 border-2 border-black py-2 px-8 rounded-full rounded-br-lg">
|
||||||
|
最近没有动态~
|
||||||
|
</p>
|
||||||
|
<img
|
||||||
|
class="h-32 w-auto relative"
|
||||||
|
src="~/assets/images/common/plana_e.png"
|
||||||
|
alt=""
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-right text-xs">(点击重试)</p>
|
<p class="text-right text-xs">
|
||||||
|
(点击重试)
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<slot v-else name="default"/>
|
<slot
|
||||||
|
v-else
|
||||||
|
name="default"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -53,4 +96,4 @@ const handleReload = () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -8,22 +8,41 @@
|
|||||||
<div class="grid-cols-4 gird min-h-48">
|
<div class="grid-cols-4 gird min-h-48">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<img alt="" class="h-20 w-20" src="~/assets/images/logo/c.png">
|
<img
|
||||||
<div class="text-4xl leading-7 text-gray-500" style="font-family: BaconyScript, sans-serif">
|
alt=""
|
||||||
|
class="h-20 w-20"
|
||||||
|
src="~/assets/images/logo/c.png"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="text-4xl leading-7 text-gray-500"
|
||||||
|
style="font-family: BaconyScript, sans-serif"
|
||||||
|
>
|
||||||
<p>CantyOni_on's</p>
|
<p>CantyOni_on's</p>
|
||||||
<p>Index</p>
|
<p>Index</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-20 leading-8 text-gray-500">©2024 All rights reserved.</p>
|
<p class="mt-20 leading-8 text-gray-500">
|
||||||
|
©2024 All rights reserved.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col text-gray-500 md:flex-row">
|
<div class="flex flex-col text-gray-500 md:flex-row">
|
||||||
<a class="mr-4 hover:text-black" href="https://beian.miit.gov.cn/"
|
<a
|
||||||
target="_blank">苏ICP备2022016243号-1</a>
|
class="mr-4 hover:text-black"
|
||||||
|
href="https://beian.miit.gov.cn/"
|
||||||
|
target="_blank"
|
||||||
|
>苏ICP备2022016243号-1</a>
|
||||||
<div>
|
<div>
|
||||||
<img alt="" class="inline-block w-4 pb-1" src="~/assets/images/common/beian.png"/>
|
<img
|
||||||
<a class="hover:text-black" href="https://beian.mps.gov.cn/#/query/webSearch?code=32050602011641/"
|
alt=""
|
||||||
target="_blank">苏公网安备32050602011641</a>
|
class="inline-block w-4 pb-1"
|
||||||
|
src="~/assets/images/common/beian.png"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="hover:text-black"
|
||||||
|
href="https://beian.mps.gov.cn/#/query/webSearch?code=32050602011641/"
|
||||||
|
target="_blank"
|
||||||
|
>苏公网安备32050602011641</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -37,4 +56,4 @@
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,45 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="loader">
|
<div class="loader">
|
||||||
<svg
|
<svg
|
||||||
class="icon"
|
class="icon"
|
||||||
height="200"
|
height="200"
|
||||||
p-id="1166"
|
p-id="1166"
|
||||||
t="1676267704832"
|
t="1676267704832"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
viewBox="0 0 1024 1024"
|
viewBox="0 0 1024 1024"
|
||||||
width="200"
|
width="200"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M787.009641 293.979897c21.792821-0.183795 51.922051-0.459487 53.39241-0.722051a12.826256 12.826256 0 0 0 6.642872-3.610256l58.092308-57.737846a12.589949 12.589949 0 0 0 3.662769-6.603488c0.393846-2.376205-0.131282-4.424205-1.496615-5.802666l-67.872821-67.413334c-1.352205-1.378462-3.452718-1.890462-5.77641-1.522871a12.616205 12.616205 0 0 0-6.695385 3.610256l-58.092307 57.737846a12.826256 12.826256 0 0 0-3.675898 6.669128c-0.236308 1.575385 4.030359 35.997538 3.977846 57.803488-54.350769-40.539897-114.346667-68.096-184.595692-78.336V116.36841h25.915077c2.691282 0 10.660103-3.990974 12.524308-5.356307 2.021744-1.378462 8.388923-6.445949 8.388923-8.375795V7.286154c0-1.929846-6.419692-3.807179-8.310154-5.172513-1.929846-1.352205-9.885538-2.113641-12.603077-2.113641H419.052308c-2.743795 0-11.106462 0.774564-13.049436 2.139897-1.890462 1.352205-8.756513 3.21641-8.756513 5.146257v95.310769c0 1.969231 6.892308 7.036718 8.795897 8.402051 1.929846 1.339077 10.266256 5.369436 13.036308 5.369436h24.996103v81.657436C256.761436 227.511795 91.897436 400.108308 91.897436 608.518564 91.897436 837.618872 281.888821 1024 512.380718 1024 742.859487 1024 931.577436 837.632 931.577436 608.518564c-0.026256-125.702564-55.492923-238.316308-144.567795-314.538667z m-67.413333 146.116924L530.825846 627.698872a25.665641 25.665641 0 0 1-35.551179-0.590769 25.337436 25.337436 0 0 1-0.577641-35.341129l188.783589-187.602051a25.521231 25.521231 0 0 1 18.064411-7.443692 25.665641 25.665641 0 0 1 18.051282 7.443692 25.403077 25.403077 0 0 1 0 35.905641z"
|
d="M787.009641 293.979897c21.792821-0.183795 51.922051-0.459487 53.39241-0.722051a12.826256 12.826256 0 0 0 6.642872-3.610256l58.092308-57.737846a12.589949 12.589949 0 0 0 3.662769-6.603488c0.393846-2.376205-0.131282-4.424205-1.496615-5.802666l-67.872821-67.413334c-1.352205-1.378462-3.452718-1.890462-5.77641-1.522871a12.616205 12.616205 0 0 0-6.695385 3.610256l-58.092307 57.737846a12.826256 12.826256 0 0 0-3.675898 6.669128c-0.236308 1.575385 4.030359 35.997538 3.977846 57.803488-54.350769-40.539897-114.346667-68.096-184.595692-78.336V116.36841h25.915077c2.691282 0 10.660103-3.990974 12.524308-5.356307 2.021744-1.378462 8.388923-6.445949 8.388923-8.375795V7.286154c0-1.929846-6.419692-3.807179-8.310154-5.172513-1.929846-1.352205-9.885538-2.113641-12.603077-2.113641H419.052308c-2.743795 0-11.106462 0.774564-13.049436 2.139897-1.890462 1.352205-8.756513 3.21641-8.756513 5.146257v95.310769c0 1.969231 6.892308 7.036718 8.795897 8.402051 1.929846 1.339077 10.266256 5.369436 13.036308 5.369436h24.996103v81.657436C256.761436 227.511795 91.897436 400.108308 91.897436 608.518564 91.897436 837.618872 281.888821 1024 512.380718 1024 742.859487 1024 931.577436 837.632 931.577436 608.518564c-0.026256-125.702564-55.492923-238.316308-144.567795-314.538667z m-67.413333 146.116924L530.825846 627.698872a25.665641 25.665641 0 0 1-35.551179-0.590769 25.337436 25.337436 0 0 1-0.577641-35.341129l188.783589-187.602051a25.521231 25.521231 0 0 1 18.064411-7.443692 25.665641 25.665641 0 0 1 18.051282 7.443692 25.403077 25.403077 0 0 1 0 35.905641z"
|
||||||
fill="#ffffff"
|
fill="#ffffff"
|
||||||
p-id="1167"
|
p-id="1167"
|
||||||
></path>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {watch} from "vue";
|
import { watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
loading: {
|
loading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.loading,
|
() => props.loading,
|
||||||
(v) => {
|
(v) => {
|
||||||
const el: HTMLElement = document.querySelector("body") as HTMLElement;
|
const el: HTMLElement = document.querySelector('body') as HTMLElement
|
||||||
if (v) {
|
if (v) {
|
||||||
el.classList.add("loading");
|
el.classList.add('loading')
|
||||||
} else {
|
|
||||||
el.classList.remove("loading");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
else {
|
||||||
|
el.classList.remove('loading')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -5,10 +5,12 @@ defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="markdown-body" v-html="$mdRender.render(content)"/>
|
<div
|
||||||
|
class="markdown-body"
|
||||||
|
v-html="$mdRender.render(content)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.markdown-body {
|
.markdown-body {
|
||||||
font-family: "LXGW WenKai", sans-serif;
|
font-family: "LXGW WenKai", sans-serif;
|
||||||
@ -34,4 +36,4 @@ defineProps<{
|
|||||||
scrollbar-width: none; /* Firefox */
|
scrollbar-width: none; /* Firefox */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,106 +1,106 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {computed, onMounted, onUnmounted, ref} from "vue";
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
type Widget = {
|
interface Widget {
|
||||||
icon: string[]
|
icon: string[]
|
||||||
url: string
|
url: string
|
||||||
title: string
|
title: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
navColor: {
|
navColor: {
|
||||||
default: false,
|
default: false,
|
||||||
type: Boolean
|
type: Boolean,
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
// 使用 ref 跟踪是否滚动
|
// 使用 ref 跟踪是否滚动
|
||||||
const scrolled = ref(false)
|
scrolled = ref(false),
|
||||||
const littleWidget: Widget[] = [
|
littleWidget: Widget[] = [
|
||||||
{
|
{
|
||||||
icon: ['fas', 'gauge'],
|
icon: ['fas', 'gauge'],
|
||||||
url: "/netdata/",
|
url: '/netdata/',
|
||||||
title: "Pi Dashboard",
|
title: 'Pi Dashboard',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: ['fas', 'cloud'],
|
||||||
|
url: '/nas/',
|
||||||
|
title: 'Nextcloud',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: ['fas', 'code-branch'],
|
||||||
|
url: '/git/',
|
||||||
|
title: 'Git Repository',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// Const logoUrl = getAssetURL("w.png")
|
||||||
|
logoType = 'logo',
|
||||||
|
showLogo = computed(() => logoType === 'text'),
|
||||||
|
isExpended = ref<boolean>(false),
|
||||||
|
route = useRoute(),
|
||||||
|
|
||||||
|
entry = [
|
||||||
|
{
|
||||||
|
title: '主页',
|
||||||
|
icon: ['fas', 'home'],
|
||||||
|
entry: [],
|
||||||
|
to: 'index',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '文章',
|
||||||
|
icon: ['fas', 'pen'],
|
||||||
|
entry: [
|
||||||
|
{
|
||||||
|
title: '博客',
|
||||||
|
url: false,
|
||||||
|
to: 'article',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '日记',
|
||||||
|
url: false,
|
||||||
|
to: 'diary',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '作品',
|
||||||
|
icon: ['fas', 'brush'],
|
||||||
|
entry: [
|
||||||
|
{
|
||||||
|
title: '软件',
|
||||||
|
url: false,
|
||||||
|
to: 'soft',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '仓库',
|
||||||
|
url: true,
|
||||||
|
to: 'https://cantyonion.site/git/',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '杂谈',
|
||||||
|
icon: ['fas', 'chess-rook'],
|
||||||
|
entry: [],
|
||||||
|
to: 'talk',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
menuExpanded = () => {
|
||||||
|
isExpended.value = !isExpended.value
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: ['fas', 'cloud'],
|
menuClose = () => {
|
||||||
url: "/nas/",
|
isExpended.value = false
|
||||||
title: "Nextcloud",
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: ['fas', 'code-branch'],
|
|
||||||
url: "/git/",
|
|
||||||
title: "Git Repository"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
//const logoUrl = getAssetURL("w.png")
|
|
||||||
const logoType: string = "logo";
|
|
||||||
const showLogo = computed(() => logoType === 'text')
|
|
||||||
const isExpended = ref<boolean>(false)
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const entry = [
|
// 监听 scroll 事件
|
||||||
{
|
handleScroll = () => {
|
||||||
title: '主页',
|
scrolled.value = window.scrollY > 0
|
||||||
icon: ['fas', 'home'],
|
|
||||||
entry: [],
|
|
||||||
to: 'index'
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '文章',
|
|
||||||
icon: ['fas', 'pen'],
|
|
||||||
entry: [
|
|
||||||
{
|
|
||||||
title: '博客',
|
|
||||||
url: false,
|
|
||||||
to: 'article'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '日记',
|
|
||||||
url: false,
|
|
||||||
to: 'diary'
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '作品',
|
|
||||||
icon: ['fas', 'brush'],
|
|
||||||
entry: [
|
|
||||||
{
|
|
||||||
title: '软件',
|
|
||||||
url: false,
|
|
||||||
to: 'soft'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '仓库',
|
|
||||||
url: true,
|
|
||||||
to: 'https://cantyonion.site/git/'
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '杂谈',
|
|
||||||
icon: ['fas', 'chess-rook'],
|
|
||||||
entry: [],
|
|
||||||
to: 'talk'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const menuExpanded = () => {
|
debouncedScroll = debounce(handleScroll, 10),
|
||||||
isExpended.value = !isExpended.value
|
|
||||||
}
|
|
||||||
|
|
||||||
const menuClose = () => {
|
alwaysBlueBackground = computed(() => route.name !== 'index' || props.navColor)
|
||||||
isExpended.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听 scroll 事件
|
|
||||||
const handleScroll = () => {
|
|
||||||
scrolled.value = window.scrollY > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const debouncedScroll = debounce(handleScroll, 10)
|
|
||||||
|
|
||||||
const alwaysBlueBackground = computed(() => route.name !== 'index' || props.navColor)
|
|
||||||
|
|
||||||
// 在组件挂载和卸载时添加和移除事件监听
|
// 在组件挂载和卸载时添加和移除事件监听
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -113,59 +113,86 @@ onUnmounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav :class="{'bg-transparent': !scrolled && !alwaysBlueBackground,
|
<nav
|
||||||
'bg-blue-400': scrolled || alwaysBlueBackground,
|
:class="{ 'bg-transparent': !scrolled && !alwaysBlueBackground,
|
||||||
'shadow': scrolled}"
|
'bg-blue-400': scrolled || alwaysBlueBackground,
|
||||||
class="fixed top-0 z-20 h-16 w-full text-white transition-all duration-300"
|
'shadow': scrolled }"
|
||||||
|
class="fixed top-0 z-20 h-16 w-full text-white transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div class="container mx-auto flex h-16 w-full items-center justify-between px-4 md:px-0 xl:max-w-screen-xl">
|
<div class="container mx-auto flex h-16 w-full items-center justify-between px-4 md:px-0 xl:max-w-screen-xl">
|
||||||
<!-- 下拉菜单按钮 -->
|
<!-- 下拉菜单按钮 -->
|
||||||
<div class="flex h-8 w-10 items-center justify-center rounded-md border-gray-600 md:hidden"
|
<div
|
||||||
@click="menuExpanded"
|
class="flex h-8 w-10 items-center justify-center rounded-md border-gray-600 md:hidden"
|
||||||
|
@click="menuExpanded"
|
||||||
>
|
>
|
||||||
<font-awesome-icon :icon="['fas', 'bars']" class="h-6"/>
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'bars']"
|
||||||
|
class="h-6"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
<div class="absolute left-1/2 -translate-x-1/2 md:relative md:left-0 md:translate-x-0">
|
<div class="absolute left-1/2 -translate-x-1/2 md:relative md:left-0 md:translate-x-0">
|
||||||
<!-- 文字模式 -->
|
<!-- 文字模式 -->
|
||||||
<div v-if="showLogo" class="h-full text-4xl min-w-16 logo py-3.5">
|
<div
|
||||||
|
v-if="showLogo"
|
||||||
|
class="h-full text-4xl min-w-16 logo py-3.5"
|
||||||
|
>
|
||||||
<span>CantyOni_on's Index</span>
|
<span>CantyOni_on's Index</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 图片模式 -->
|
<!-- 图片模式 -->
|
||||||
<div v-else class="py-2">
|
<div
|
||||||
<img src="~/assets/images/logo/w.png" alt="" class="h-12 w-12"/>
|
v-else
|
||||||
|
class="py-2"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="~/assets/images/logo/w.png"
|
||||||
|
alt=""
|
||||||
|
class="h-12 w-12"
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Entry -->
|
<!-- Entry -->
|
||||||
<div :class="{'h-screen opacity-100 visible': isExpended, 'h-0 opacity-0 invisible': !isExpended}"
|
<div
|
||||||
class="absolute top-full right-0 left-0 bg-white transition-all md:visible md:relative md:top-0 md:flex
|
:class="{ 'h-screen opacity-100 visible': isExpended, 'h-0 opacity-0 invisible': !isExpended }"
|
||||||
md:h-auto md:bg-transparent md:opacity-100"
|
class="absolute top-full right-0 left-0 bg-white transition-all md:visible md:relative md:top-0 md:flex
|
||||||
|
md:h-auto md:bg-transparent md:opacity-100"
|
||||||
>
|
>
|
||||||
<nav-bar-entry-item v-for="item in entry" :key="item.title"
|
<nav-bar-entry-item
|
||||||
:entry="item.entry" :icon="item.icon"
|
v-for="item in entry"
|
||||||
:title="item.title" :to="item.to"
|
:key="item.title"
|
||||||
@on-go="menuClose"
|
:entry="item.entry"
|
||||||
|
:icon="item.icon"
|
||||||
|
:title="item.title"
|
||||||
|
:to="item.to"
|
||||||
|
@on-go="menuClose"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 快速跳转小组件 -->
|
<!-- 快速跳转小组件 -->
|
||||||
<div class="flex items-center justify-between gap-4 text-xl">
|
<div class="flex items-center justify-between gap-4 text-xl">
|
||||||
<a v-for="item in littleWidget"
|
<a
|
||||||
:key="item.url" :href=item.url :title=item.title
|
v-for="item in littleWidget"
|
||||||
class="transition-all hover:scale-125">
|
:key="item.url"
|
||||||
<font-awesome-icon :icon="item.icon"/>
|
:href="item.url"
|
||||||
|
:title="item.title"
|
||||||
|
class="transition-all hover:scale-125"
|
||||||
|
>
|
||||||
|
<font-awesome-icon :icon="item.icon" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- 用于隔开元素 -->
|
<!-- 用于隔开元素 -->
|
||||||
<div class="h-16 w-full" v-if="alwaysBlueBackground"></div>
|
<div
|
||||||
|
v-if="alwaysBlueBackground"
|
||||||
|
class="h-16 w-full"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.logo {
|
.logo {
|
||||||
font-family: BaconyScript, serif;
|
font-family: BaconyScript, serif;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
type Entry = {
|
interface Entry {
|
||||||
title: string,
|
title: string
|
||||||
url: boolean,
|
url: boolean
|
||||||
to: string,
|
to: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<(e: 'onGo') => void>()
|
||||||
(e: 'onGo'): void
|
|
||||||
}>();
|
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
title: string
|
title: string
|
||||||
@ -23,39 +21,48 @@ const onClick = (to: string | undefined, url: boolean) => {
|
|||||||
|
|
||||||
if (url)
|
if (url)
|
||||||
window.open(to)
|
window.open(to)
|
||||||
|
|
||||||
emit('onGo')
|
emit('onGo')
|
||||||
router.push({name: to})
|
router.push({ name: to })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container relative w-full max-w-max flex-auto shrink-0 cursor-pointer group md:w-16 md:hover:w-full"
|
<div
|
||||||
@click="onClick(to, false)"
|
class="container relative w-full max-w-max flex-auto shrink-0 cursor-pointer group md:w-16 md:hover:w-full"
|
||||||
|
@click="onClick(to, false)"
|
||||||
>
|
>
|
||||||
<div class="my-1 flex h-14 w-screen items-center rounded-md pr-4 pl-2 text-gray-500
|
<div
|
||||||
md:w-auto md:text-white md:group-hover:bg-gray-100 md:group-hover:bg-opacity-30"
|
class="my-1 flex h-14 w-screen items-center rounded-md pr-4 pl-2 text-gray-500
|
||||||
|
md:w-auto md:text-white md:group-hover:bg-gray-100 md:group-hover:bg-opacity-30"
|
||||||
>
|
>
|
||||||
<font-awesome-icon :icon="icon" class="mx-2 h-5 w-5 md:h-8 md:w-8"/>
|
<font-awesome-icon
|
||||||
<span class="w-full max-w-max overflow-hidden text-lg transition-all duration-300 text-nowrap font-bold
|
:icon="icon"
|
||||||
md:font-normal md:w-0 md:text-xl md:group-hover:w-full"
|
class="mx-2 h-5 w-5 md:h-8 md:w-8"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="w-full max-w-max overflow-hidden text-lg transition-all duration-300 text-nowrap font-bold
|
||||||
|
md:font-normal md:w-0 md:text-xl md:group-hover:w-full"
|
||||||
>
|
>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 下拉菜单 -->
|
<!-- 下拉菜单 -->
|
||||||
<div v-if="entry"
|
<div
|
||||||
class="grid grid-cols-2 overflow-hidden transition-all duration-300 min-w-32 item md:invisible md:absolute
|
v-if="entry"
|
||||||
md:top-full md:left-1/2 md:block md:rounded-xl md:bg-white md:opacity-0 md:shadow-md
|
class="grid grid-cols-2 overflow-hidden transition-all duration-300 min-w-32 item md:invisible md:absolute
|
||||||
md:group-hover:visible md:group-hover:-translate-x-1/2 md:group-hover:opacity-100"
|
md:top-full md:left-1/2 md:block md:rounded-xl md:bg-white md:opacity-0 md:shadow-md
|
||||||
|
md:group-hover:visible md:group-hover:-translate-x-1/2 md:group-hover:opacity-100"
|
||||||
>
|
>
|
||||||
<div v-for="item in entry" :key="item.title"
|
<div
|
||||||
class="px-11 py-2 text-gray-500 text-md md:px-0 md:text-center md:text-lg md:hover:bg-gray-100"
|
v-for="item in entry"
|
||||||
@click="onClick(item.to, item.url)"
|
:key="item.title"
|
||||||
|
class="px-11 py-2 text-gray-500 text-md md:px-0 md:text-center md:text-lg md:hover:bg-gray-100"
|
||||||
|
@click="onClick(item.to, item.url)"
|
||||||
>
|
>
|
||||||
{{ item.title }}
|
{{ item.title }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -66,4 +73,4 @@ const onClick = (to: string | undefined, url: boolean) => {
|
|||||||
transform: perspective(500px) rotateX(-45deg) translateX(-50%);
|
transform: perspective(500px) rotateX(-45deg) translateX(-50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -3,11 +3,18 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a class="flex items-center justify-center" href="https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral">
|
<a
|
||||||
本网站由<img :src="getAssetURL('upyun.png')" alt="又拍云" class="h-8 w-auto">提供CDN加速/云储存服务
|
class="flex items-center justify-center"
|
||||||
|
href="https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral"
|
||||||
|
>
|
||||||
|
本网站由<img
|
||||||
|
:src="getAssetURL('upyun.png')"
|
||||||
|
alt="又拍云"
|
||||||
|
class="h-8 w-auto"
|
||||||
|
>提供CDN加速/云储存服务
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -8,13 +8,17 @@ defineProps<{
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex gap-4 text-xl">
|
<div class="flex gap-4 text-xl">
|
||||||
<a :key="url" :href=url :title=title
|
<a
|
||||||
class="flex items-center justify-center transition-all hover:scale-125">
|
:key="url"
|
||||||
<font-awesome-icon :icon="icon"/>
|
:href="url"
|
||||||
|
:title="title"
|
||||||
|
class="flex items-center justify-center transition-all hover:scale-125"
|
||||||
|
>
|
||||||
|
<font-awesome-icon :icon="icon" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
export const getAssetURL = (image: string) => {
|
export const getAssetURL = (image: string) => {
|
||||||
// 参数一: 相对路径
|
// 参数一: 相对路径
|
||||||
// 参数二: 当前路径的URL
|
// 参数二: 当前路径的URL
|
||||||
if (import.meta.client)
|
if (import.meta.client) {
|
||||||
return new URL(`../assets/images/${image}`, import.meta.url).href
|
return new URL(`../assets/images/${image}`, import.meta.url).href
|
||||||
|
}
|
||||||
return `/_nuxt/assets/images/${image}`
|
return `/_nuxt/assets/images/${image}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
export const debounce = (fn: Function, delay: number) => {
|
export const debounce = (fn: Function, delay: number) => {
|
||||||
let timeoutId: NodeJS.Timeout
|
let timeoutId: NodeJS.Timeout
|
||||||
return (...args: any[]) => {
|
return (...args: unknown[]) => {
|
||||||
clearTimeout(timeoutId)
|
clearTimeout(timeoutId)
|
||||||
timeoutId = setTimeout(() => {
|
timeoutId = setTimeout(() => {
|
||||||
fn(...args)
|
fn(...args)
|
||||||
@ -17,20 +19,20 @@ export const debounce = (fn: Function, delay: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const setMobileTopColor = () => {
|
export const setMobileTopColor = () => {
|
||||||
const lightMeta = document.createElement('meta');
|
const lightMeta = document.createElement('meta')
|
||||||
lightMeta.setAttribute('name', 'theme-color');
|
lightMeta.setAttribute('name', 'theme-color')
|
||||||
lightMeta.setAttribute('media', '(prefers-color-scheme: light)');
|
lightMeta.setAttribute('media', '(prefers-color-scheme: light)')
|
||||||
lightMeta.setAttribute('content', '#60a5fa');
|
lightMeta.setAttribute('content', '#60a5fa')
|
||||||
|
|
||||||
const darkMeta = document.createElement('meta');
|
const darkMeta = document.createElement('meta')
|
||||||
darkMeta.setAttribute('name', 'theme-color');
|
darkMeta.setAttribute('name', 'theme-color')
|
||||||
darkMeta.setAttribute('media', '(prefers-color-scheme: dark)');
|
darkMeta.setAttribute('media', '(prefers-color-scheme: dark)')
|
||||||
darkMeta.setAttribute('content', '#60a5fa');
|
darkMeta.setAttribute('content', '#60a5fa')
|
||||||
|
|
||||||
document.head.appendChild(lightMeta);
|
document.head.appendChild(lightMeta)
|
||||||
document.head.appendChild(darkMeta);
|
document.head.appendChild(darkMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const removeMobileTopColor = () => {
|
export const removeMobileTopColor = () => {
|
||||||
document.querySelectorAll('meta[name="theme-color"]').forEach(meta => meta.remove());
|
document.querySelectorAll('meta[name="theme-color"]').forEach(meta => meta.remove())
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
import type { AsyncData, UseFetchOptions } from '#app'
|
import type { AsyncData, UseFetchOptions } from '#app'
|
||||||
|
|
||||||
export function requestCore<DataT>(url: string, options: UseFetchOptions<DataT>) {
|
export function requestCore<DataT>(url: string, options: UseFetchOptions<DataT>) {
|
||||||
// const config = useRuntimeConfig()
|
// Const config = useRuntimeConfig()
|
||||||
const {$mitt} = useNuxtApp()
|
const { $mitt } = useNuxtApp()
|
||||||
return useFetch(url, {
|
return useFetch(url, {
|
||||||
baseURL: "https://cantyonion.site",
|
baseURL: 'https://cantyonion.site',
|
||||||
retry: false,
|
retry: false,
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
onRequest({ options }) {
|
onRequest({ options }) {
|
||||||
let token: string = ""
|
let token = ''
|
||||||
if (import.meta.client) {
|
if (import.meta.client) {
|
||||||
token = localStorage.getItem("token") || ""
|
token = localStorage.getItem('token') || ''
|
||||||
}
|
}
|
||||||
if (!options.headers.get('Authorization'))
|
if (!options.headers.get('Authorization')) {
|
||||||
options.headers.set('Authorization', `Bearer ${token}`)
|
options.headers.set('Authorization', `Bearer ${token}`)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onResponse({ response }) {
|
onResponse({ response }) {
|
||||||
// HTTP状态码2XX/3XX执行,否则不执行
|
// HTTP状态码2XX/3XX执行,否则不执行
|
||||||
if (response.status < 200 || response.status >= 400) {
|
if (response.status < 200 || response.status >= 400) {
|
||||||
console.error(`HTTP 错误: ${response.status}`)
|
console.error(`HTTP 错误: ${response.status}`)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
// 业务code状态码判断
|
// 业务code状态码判断
|
||||||
// if (response._data.code !== 200) {
|
// If (response._data.code !== 200) {
|
||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
},
|
},
|
||||||
@ -30,10 +30,10 @@ export function requestCore<DataT>(url: string, options: UseFetchOptions<DataT>)
|
|||||||
$mitt.emit('eventBus', {
|
$mitt.emit('eventBus', {
|
||||||
level: 'error',
|
level: 'error',
|
||||||
title: `HTTP错误:${response.status}`,
|
title: `HTTP错误:${response.status}`,
|
||||||
message: response.statusText
|
message: response.statusText,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
...options
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,8 +43,9 @@ export function get<DataT, ErrorT>(url: string, options?: UseFetchOptions<DataT>
|
|||||||
return async (): Promise<DataT> => {
|
return async (): Promise<DataT> => {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
await result.refresh()
|
await result.refresh()
|
||||||
if (result.status.value === 'success')
|
if (result.status.value === 'success') {
|
||||||
return result.data.value
|
return result.data.value
|
||||||
|
}
|
||||||
throw result.error
|
throw result.error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,4 +55,4 @@ export function get<DataT, ErrorT>(url: string, options?: UseFetchOptions<DataT>
|
|||||||
}) as AsyncData<DataT, ErrorT>
|
}) as AsyncData<DataT, ErrorT>
|
||||||
return result.data.value
|
return result.data.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
86
error.vue
86
error.vue
@ -3,49 +3,49 @@ import type { NuxtError } from '#app'
|
|||||||
import { setMobileTopColor } from '~/composables/function'
|
import { setMobileTopColor } from '~/composables/function'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
error: Object as () => NuxtError
|
error: Object as () => NuxtError,
|
||||||
})
|
}),
|
||||||
|
|
||||||
const {$mitt} = useNuxtApp()
|
{ $mitt } = useNuxtApp(),
|
||||||
|
|
||||||
const errorMessages: Record<string, { icon: string; title: string; message: string }> = {
|
errorMessages: Record<string, { icon: string, title: string, message: string }> = {
|
||||||
'400': {
|
400: {
|
||||||
icon: '❓',
|
icon: '❓',
|
||||||
title: '哦豁!请求有点问题',
|
title: '哦豁!请求有点问题',
|
||||||
message: '请求似乎有点小问题,服务器君有点摸不着头脑呢!',
|
message: '请求似乎有点小问题,服务器君有点摸不着头脑呢!',
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
icon: '🔒',
|
||||||
|
title: '哎呀!大门被锁住了',
|
||||||
|
message: '你没有权限访问这个角落,试试返回首页吧!',
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
icon: '🌌',
|
||||||
|
title: '哎呀!你发现了超级基地的秘密角落',
|
||||||
|
message: '看起来你找的页面藏到了未知的时空层里!',
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
icon: '🚀',
|
||||||
|
title: '糟糕!超级基地出了一点故障',
|
||||||
|
message: '服务器君正在努力解决问题,请稍等片刻!',
|
||||||
|
},
|
||||||
|
502: {
|
||||||
|
icon: '👽',
|
||||||
|
title: '糟糕!基地信号被外星人拦截了',
|
||||||
|
message: '网络通道似乎遇到了问题,请稍后再试!',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
icon: '⚠️',
|
||||||
|
title: '哦豁!此时遇到了点小麻烦',
|
||||||
|
message: '请求在穿越洋葱星球时迷路了,请返回首页重新探索!',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'403': {
|
|
||||||
icon: '🔒',
|
|
||||||
title: '哎呀!大门被锁住了',
|
|
||||||
message: '你没有权限访问这个角落,试试返回首页吧!',
|
|
||||||
},
|
|
||||||
'404': {
|
|
||||||
icon: '🌌',
|
|
||||||
title: '哎呀!你发现了超级基地的秘密角落',
|
|
||||||
message: '看起来你找的页面藏到了未知的时空层里!',
|
|
||||||
},
|
|
||||||
'500': {
|
|
||||||
icon: '🚀',
|
|
||||||
title: '糟糕!超级基地出了一点故障',
|
|
||||||
message: '服务器君正在努力解决问题,请稍等片刻!',
|
|
||||||
},
|
|
||||||
'502': {
|
|
||||||
icon: '👽',
|
|
||||||
title: '糟糕!基地信号被外星人拦截了',
|
|
||||||
message: '网络通道似乎遇到了问题,请稍后再试!',
|
|
||||||
},
|
|
||||||
default: {
|
|
||||||
icon: '⚠️',
|
|
||||||
title: '哦豁!此时遇到了点小麻烦',
|
|
||||||
message: '请求在穿越洋葱星球时迷路了,请返回首页重新探索!',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const errorContent = computed(() => errorMessages[props.error!.statusCode] || errorMessages['default']);
|
errorContent = computed(() => errorMessages[props.error!.statusCode] || errorMessages.default)
|
||||||
console.error(props.error)
|
console.error(props.error)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
$mitt.emit("startLoading", false)
|
$mitt.emit('startLoading', false)
|
||||||
setMobileTopColor()
|
setMobileTopColor()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -56,13 +56,17 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative flex min-h-screen flex-col overflow-hidden bg-blue-50">
|
<div class="relative flex min-h-screen flex-col overflow-hidden bg-blue-50">
|
||||||
<nav-bar :nav-color="true"/>
|
<nav-bar :nav-color="true" />
|
||||||
<alert :title="errorContent.title" :message="errorContent.message" :icon="errorContent.icon"/>
|
<alert
|
||||||
<footer-main/>
|
:title="errorContent.title"
|
||||||
<alert-notification/>
|
:message="errorContent.message"
|
||||||
|
:icon="errorContent.icon"
|
||||||
|
/>
|
||||||
|
<footer-main />
|
||||||
|
<alert-notification />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
3
eslint.config.mts
Normal file
3
eslint.config.mts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import withNuxt from './.nuxt/eslint.config.mjs'
|
||||||
|
|
||||||
|
export default withNuxt()
|
@ -1,4 +1,4 @@
|
|||||||
export default defineNuxtRouteMiddleware(() => {
|
export default defineNuxtRouteMiddleware(() => {
|
||||||
const {$mitt} = useNuxtApp()
|
const { $mitt } = useNuxtApp()
|
||||||
$mitt.emit('startLoading', true)
|
$mitt.emit('startLoading', true)
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export default defineNuxtRouteMiddleware((to, _) => {
|
export default defineNuxtRouteMiddleware((to) => {
|
||||||
if (to.meta.maintenance === true && import.meta.env.DEV)
|
if (to.meta.maintenance === true && import.meta.env.DEV) {
|
||||||
return navigateTo('/error/maintenance')
|
return navigateTo('/error/maintenance')
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
|
|
||||||
|
// See https://blog.csdn.net/m0_53022813/article/details/137379480
|
||||||
|
import eslintPlugin from 'vite-plugin-eslint'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
compatibilityDate: '2024-04-03',
|
modules: ['@pinia/nuxt', '@pinia-plugin-persistedstate/nuxt', '@nuxt/image', '@nuxt/eslint'],
|
||||||
|
components: true,
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: true },
|
||||||
modules: ['@pinia/nuxt', '@pinia-plugin-persistedstate/nuxt', '@nuxt/image'],
|
|
||||||
app: {
|
app: {
|
||||||
head: {
|
head: {
|
||||||
title: 'CANTYONION.SITE',
|
title: 'CANTYONION.SITE',
|
||||||
@ -15,40 +18,50 @@ export default defineNuxtConfig({
|
|||||||
],
|
],
|
||||||
link: [
|
link: [
|
||||||
{ rel: 'icon', href: '/favicon.png' },
|
{ rel: 'icon', href: '/favicon.png' },
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
components: true,
|
|
||||||
css: [
|
css: [
|
||||||
'@fortawesome/fontawesome-svg-core/styles.css',
|
'@fortawesome/fontawesome-svg-core/styles.css',
|
||||||
'github-markdown-css/github-markdown-light.css',
|
'github-markdown-css/github-markdown-light.css',
|
||||||
'~/assets/css/main.scss'
|
'~/assets/css/main.scss',
|
||||||
],
|
],
|
||||||
|
runtimeConfig: {
|
||||||
|
public: {
|
||||||
|
gitApiKey: 'fb8aec429ea7d0a36f7238dbffda9d2d66c7b045',
|
||||||
|
baseURL: 'https://cantyonion.site',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
transpile: ['@fortawesome/vue-fontawesome', 'vue3-typed-js', 'pinia-plugin-persistedstate'],
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
},
|
||||||
|
compatibilityDate: '2024-04-03',
|
||||||
|
vite: {
|
||||||
|
plugins: [
|
||||||
|
eslintPlugin({
|
||||||
|
include: ['**/*.ts', '**/*.vue'],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
css: {
|
||||||
|
preprocessorOptions: {
|
||||||
|
scss: {
|
||||||
|
api: 'modern-compiler', // Or "modern"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
postcss: {
|
postcss: {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
eslint: {
|
||||||
transpile: ['@fortawesome/vue-fontawesome', 'vue3-typed-js', 'pinia-plugin-persistedstate']
|
config: {
|
||||||
},
|
stylistic: true,
|
||||||
vite: {
|
|
||||||
css: {
|
|
||||||
preprocessorOptions: {
|
|
||||||
scss: {
|
|
||||||
api: 'modern-compiler' // or "modern"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
runtimeConfig: {
|
})
|
||||||
public: {
|
|
||||||
gitApiKey: 'fb8aec429ea7d0a36f7238dbffda9d2d66c7b045',
|
|
||||||
baseURL: 'https://cantyonion.site'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
devServer: {
|
|
||||||
host: '0.0.0.0'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
15
package.json
15
package.json
@ -7,7 +7,9 @@
|
|||||||
"dev": "nuxt dev",
|
"dev": "nuxt dev",
|
||||||
"generate": "nuxt generate",
|
"generate": "nuxt generate",
|
||||||
"preview": "nuxt preview",
|
"preview": "nuxt preview",
|
||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"stylefix": "eslint . --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@callmebill/lxgw-wenkai-web": "^1.501.0",
|
"@callmebill/lxgw-wenkai-web": "^1.501.0",
|
||||||
@ -30,11 +32,20 @@
|
|||||||
"vue3-typed-js": "^0.0.1"
|
"vue3-typed-js": "^0.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.18.0",
|
||||||
|
"@nuxt/eslint": "^0.7.5",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
|
"eslint": "^9.18.0",
|
||||||
|
"eslint-plugin-vue": "^9.32.0",
|
||||||
|
"globals": "^15.14.0",
|
||||||
|
"jiti": "^2.4.2",
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.4.49",
|
||||||
"sass-embedded": "^1.81.0",
|
"sass-embedded": "^1.81.0",
|
||||||
"tailwindcss": "^3.4.15"
|
"tailwindcss": "^3.4.5",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"typescript-eslint": "^8.19.1",
|
||||||
|
"vite-plugin-eslint": "^1.8.1"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
navigateTo("/error/404")
|
navigateTo('/error/404')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const {$mitt} = useNuxtApp()
|
const { $mitt } = useNuxtApp()
|
||||||
|
|
||||||
onMounted( () => {
|
onMounted(() => {
|
||||||
$mitt.emit('startLoading', false)
|
$mitt.emit('startLoading', false)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nuxt-page/>
|
<nuxt-page />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const route = useRoute()
|
const route = useRoute(),
|
||||||
const cid = route.params.cid as string
|
cid = route.params.cid as string,
|
||||||
const content = ref<IBlogResponse<IPostFull>>()
|
content = ref<IBlogResponse<IPostFull>>(),
|
||||||
|
|
||||||
const getPost = get<IBlogResponse<IPostFull>, any>('/blog/index.php/api/post', {
|
getPost = get<IBlogResponse<IPostFull>, unknown>('/blog/index.php/api/post', {
|
||||||
params: {
|
params: {
|
||||||
cid: cid,
|
cid,
|
||||||
md: false
|
md: false,
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
|
|
||||||
const title = computed(() => content.value?.data.title || 'Can not see me... can not see me...')
|
title = computed(() => content.value?.data.title || 'Can not see me... can not see me...'),
|
||||||
const text = computed(() => content.value?.data.text || '## Ops!')
|
text = computed(() => content.value?.data.text || '## Ops!'),
|
||||||
const date = computed(() => new Date((content.value?.data.date.timeStamp || 0) * 1000)
|
date = computed(() => new Date((content.value?.data.date.timeStamp || 0) * 1000)
|
||||||
.toISOString()
|
.toISOString()
|
||||||
.split('T')[0]
|
.split('T')[0],
|
||||||
)
|
),
|
||||||
const category = computed(() => content.value?.data.category || 'default')
|
category = computed(() => content.value?.data.category || 'default'),
|
||||||
const url = computed(() => content.value?.data.url || '/error/404')
|
url = computed(() => content.value?.data.url || '/error/404')
|
||||||
|
|
||||||
content.value = await getPost()
|
content.value = await getPost()
|
||||||
</script>
|
</script>
|
||||||
@ -25,7 +25,9 @@ content.value = await getPost()
|
|||||||
<template>
|
<template>
|
||||||
<section class="container m-4 mx-auto rounded-xl bg-white p-8 shadow-md lg:max-w-screen-lg">
|
<section class="container m-4 mx-auto rounded-xl bg-white p-8 shadow-md lg:max-w-screen-lg">
|
||||||
<div class="mb-4 border-b-2 border-dashed border-gray-200 pb-4">
|
<div class="mb-4 border-b-2 border-dashed border-gray-200 pb-4">
|
||||||
<h1 class="text-center text-3xl font-bold">{{ title }}</h1>
|
<h1 class="text-center text-3xl font-bold">
|
||||||
|
{{ title }}
|
||||||
|
</h1>
|
||||||
<div class="flex w-full items-center justify-center gap-4 text-gray-600">
|
<div class="flex w-full items-center justify-center gap-4 text-gray-600">
|
||||||
<!-- 日期 -->
|
<!-- 日期 -->
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@ -43,10 +45,10 @@ content.value = await getPost()
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<markdown-section :content="text"/>
|
<markdown-section :content="text" />
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
maintenance: true
|
maintenance: true,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NuxtWelcome/>
|
<NuxtWelcome />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<h1>auth view</h1>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
maintenance: true
|
maintenance: true,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NuxtWelcome/>
|
<NuxtWelcome />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,20 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const route = useRoute()
|
const route = useRoute(),
|
||||||
const errorCode = parseInt(route.params.code as string, 10)
|
errorCode = parseInt(route.params.code as string, 10)
|
||||||
|
|
||||||
if (import.meta.server)
|
if (import.meta.server) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: errorCode,
|
statusCode: errorCode,
|
||||||
})
|
})
|
||||||
else
|
}
|
||||||
showError({statusCode: errorCode})
|
else { showError({ statusCode: errorCode }) }
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const {$mitt} = useNuxtApp()
|
const { $mitt } = useNuxtApp()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
$mitt.emit("startLoading", false)
|
$mitt.emit('startLoading', false)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<alert icon="🚧" title="建设中" message="此页面正在建设中,请稍后再来查看更新。"/>
|
<alert
|
||||||
|
icon="🚧"
|
||||||
|
title="建设中"
|
||||||
|
message="此页面正在建设中,请稍后再来查看更新。"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
245
pages/index.vue
245
pages/index.vue
@ -1,96 +1,95 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {computed, onMounted, ref} from "vue";
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
|
||||||
type errObj = {
|
interface errObj {
|
||||||
blog?: object,
|
blog?: object
|
||||||
git?: object
|
git?: object
|
||||||
}
|
}
|
||||||
|
|
||||||
const {$mitt} = useNuxtApp()
|
const { $mitt } = useNuxtApp(),
|
||||||
const recentPosts = ref<IPost[] | null>(null);
|
recentPosts = ref<IPost[] | null>(null),
|
||||||
const recentActivities = ref<IActivity[] | null>(null);
|
recentActivities = ref<IActivity[] | null>(null),
|
||||||
const isLoading = ref({
|
isLoading = ref({
|
||||||
blog: true,
|
blog: true,
|
||||||
git: true
|
git: true,
|
||||||
});
|
}),
|
||||||
const isError = ref({
|
isError = ref({
|
||||||
blog: false,
|
blog: false,
|
||||||
git: false
|
git: false,
|
||||||
});
|
}),
|
||||||
const errMsg = ref<errObj>({
|
errMsg = ref<errObj>({
|
||||||
blog: undefined,
|
blog: undefined,
|
||||||
git: undefined
|
git: undefined,
|
||||||
})
|
}),
|
||||||
|
|
||||||
const config = useRuntimeConfig()
|
getBlogRecentPost = get<IBlogResponse<IPostsData>, unknown>('/blog/index.php/api/posts', {
|
||||||
const getBlogRecentPost = get<IBlogResponse<IPostsData>, any>('/blog/index.php/api/posts', {
|
params: {
|
||||||
params: {
|
showDigest: 'excerpt',
|
||||||
showDigest: "excerpt",
|
limit: 100,
|
||||||
limit: 100
|
},
|
||||||
}
|
}),
|
||||||
})
|
getActivity = get<IActivity[], unknown>('/git/api/v1/users/cantyonion/activities/feeds', {
|
||||||
const getActivity = get<IActivity[], any>('/git/api/v1/users/cantyonion/activities/feeds', {
|
params: {
|
||||||
headers: {
|
'limit': 10,
|
||||||
Authorization: ` ${config.public.gitApiKey}`
|
'only-performed-by': true,
|
||||||
},
|
},
|
||||||
params: {
|
}),
|
||||||
limit: 10,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const hasPosts = computed(() => (recentPosts.value ?? []).length > 0);
|
hasPosts = computed(() => (recentPosts.value ?? []).length > 0),
|
||||||
const hasActivities = computed(() => (recentActivities.value ?? []).length > 0);
|
hasActivities = computed(() => (recentActivities.value ?? []).length > 0),
|
||||||
const postsData = computed(() => {
|
postsData = computed(() => {
|
||||||
if (!recentPosts.value) return [];
|
if (!recentPosts.value) {
|
||||||
if (recentPosts.value.length > 4)
|
return []
|
||||||
return recentPosts.value.slice(0, 4)
|
|
||||||
return recentPosts.value
|
|
||||||
})
|
|
||||||
const activitiesData = computed((): IActivity<IContent>[] => {
|
|
||||||
if (!Array.isArray(recentActivities.value)) return [];
|
|
||||||
return recentActivities.value.map(item => {
|
|
||||||
try {
|
|
||||||
if (item.op_type === "commit_repo")
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
content: JSON.parse(item.content)
|
|
||||||
}
|
|
||||||
else return null
|
|
||||||
} catch (e) {
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}).filter(item => item !== null)
|
if (recentPosts.value.length > 4) {
|
||||||
})
|
return recentPosts.value.slice(0, 4)
|
||||||
|
}
|
||||||
|
return recentPosts.value
|
||||||
|
}),
|
||||||
|
activitiesData = computed((): IStrictActivity[] => {
|
||||||
|
if (!Array.isArray(recentActivities.value)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return recentActivities.value.map((item) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
content: typeof item.content === 'string' && item.content ? JSON.parse(item.content) : null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
|
||||||
const reloadPosts = async () => {
|
reloadPosts = async () => {
|
||||||
try {
|
try {
|
||||||
isLoading.value.blog = true
|
isLoading.value.blog = true
|
||||||
isError.value.blog = false
|
isError.value.blog = false
|
||||||
const postData = await getBlogRecentPost()
|
const postData = await getBlogRecentPost()
|
||||||
if (postData !== null)
|
if (postData !== null) {
|
||||||
recentPosts.value = postData.data.dataSet
|
recentPosts.value = postData.data.dataSet
|
||||||
} catch (e) {
|
}
|
||||||
isError.value.blog = true
|
}
|
||||||
errMsg.value.blog = e as object
|
catch (e) {
|
||||||
}
|
isError.value.blog = true
|
||||||
finally {
|
errMsg.value.blog = e as object
|
||||||
isLoading.value.blog = false
|
}
|
||||||
}
|
finally {
|
||||||
}
|
isLoading.value.blog = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
const reloadActivities = async () => {
|
reloadActivities = async () => {
|
||||||
try {
|
try {
|
||||||
isLoading.value.git = true
|
isLoading.value.git = true
|
||||||
isError.value.git = false
|
isError.value.git = false
|
||||||
recentActivities.value = await getActivity()
|
recentActivities.value = await getActivity()
|
||||||
} catch (e) {
|
}
|
||||||
isError.value.git = true
|
catch (e) {
|
||||||
errMsg.value.git = e as object
|
isError.value.git = true
|
||||||
|
errMsg.value.git = e as object
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
isLoading.value.git = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
isLoading.value.git = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.allSettled([reloadPosts(), reloadActivities()])
|
await Promise.allSettled([reloadPosts(), reloadActivities()])
|
||||||
|
|
||||||
@ -100,39 +99,67 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- 个人介绍 -->
|
<div class="main">
|
||||||
<card-full-screen-intro-card/>
|
<!-- 个人介绍 -->
|
||||||
|
<card-full-screen-intro-card />
|
||||||
|
|
||||||
<div class="container mx-auto xl:max-w-screen-xl">
|
<div class="container mx-auto xl:max-w-screen-xl">
|
||||||
<!-- 博客部分 -->
|
<!-- 博客部分 -->
|
||||||
<section class="w-full px-4 sm:px-0">
|
<section class="w-full px-4 sm:px-0">
|
||||||
<!-- 标题部分 -->
|
<!-- 标题部分 -->
|
||||||
<card-title :icon="['fas', 'blog']" title="最近文章"/>
|
<card-title
|
||||||
|
:icon="['fas', 'blog']"
|
||||||
|
title="最近文章"
|
||||||
|
/>
|
||||||
|
|
||||||
<card-section-card :empty="!hasPosts" :error="isError.blog" :loading="isLoading.blog" @reload="reloadPosts" :err-msg="errMsg.blog">
|
<card-section-card
|
||||||
<template v-slot:default>
|
:empty="!hasPosts"
|
||||||
<div class="grid w-full grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
:error="isError.blog"
|
||||||
<card-article-card v-for="item in postsData" :key="item.cid" :post="item"/>
|
:loading="isLoading.blog"
|
||||||
</div>
|
:err-msg="errMsg.blog"
|
||||||
</template>
|
@reload="reloadPosts"
|
||||||
</card-section-card>
|
>
|
||||||
</section>
|
<template #default>
|
||||||
|
<div class="grid w-full grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
<card-article-card
|
||||||
|
v-for="item in postsData"
|
||||||
|
:key="item.cid"
|
||||||
|
:post="item"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</card-section-card>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Git部分 -->
|
<!-- Git部分 -->
|
||||||
<section class="my-4 w-full px-4 sm:px-0">
|
<section class="my-4 w-full px-4 sm:px-0">
|
||||||
<card-title :icon="['fas', 'code']" title="最近活动"/>
|
<card-title
|
||||||
|
:icon="['fas', 'code']"
|
||||||
|
title="最近活动"
|
||||||
|
/>
|
||||||
|
|
||||||
<card-section-card :empty="!hasActivities" :error="isError.git" :loading="isLoading.git" @reload="reloadActivities" :err-msg="errMsg.git">
|
<card-section-card
|
||||||
<template v-slot:default>
|
:empty="!hasActivities"
|
||||||
<div class="grid w-full grid-cols-1 gap-4 lg:grid-cols-2">
|
:error="isError.git"
|
||||||
<card-git-card v-for="item in activitiesData" :key="item.id" :commit="item"/>
|
:loading="isLoading.git"
|
||||||
</div>
|
:err-msg="errMsg.git"
|
||||||
</template>
|
@reload="reloadActivities"
|
||||||
</card-section-card>
|
>
|
||||||
</section>
|
<template #default>
|
||||||
|
<div class="w-full rounded-xl bg-white divide-y divide-dashed">
|
||||||
|
<card-git-card
|
||||||
|
v-for="item in activitiesData"
|
||||||
|
:key="item.id"
|
||||||
|
:commit="item"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</card-section-card>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
maintenance: true
|
maintenance: true,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NuxtWelcome/>
|
<NuxtWelcome />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
maintenance: true
|
maintenance: true,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NuxtWelcome/>
|
<NuxtWelcome />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
import {library, config} from '@fortawesome/fontawesome-svg-core'
|
import { config, library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
|
|
||||||
import {faClock, faFolder} from '@fortawesome/free-regular-svg-icons'
|
|
||||||
import {
|
import {
|
||||||
faXmark,
|
faBars,
|
||||||
faBlog,
|
faBlog,
|
||||||
faGauge,
|
|
||||||
faCodeBranch,
|
|
||||||
faCloud,
|
|
||||||
faChevronRight,
|
|
||||||
faCodeCommit,
|
|
||||||
faCode,
|
|
||||||
faHouse,
|
|
||||||
faPen,
|
|
||||||
faBrush,
|
faBrush,
|
||||||
faChessRook,
|
faChessRook,
|
||||||
faBars,
|
|
||||||
faChevronDown,
|
faChevronDown,
|
||||||
faLink
|
faChevronRight,
|
||||||
|
faCloud,
|
||||||
|
faCode,
|
||||||
|
faCodeBranch,
|
||||||
|
faCodeCommit,
|
||||||
|
faGauge,
|
||||||
|
faHouse,
|
||||||
|
faLink,
|
||||||
|
faPen,
|
||||||
|
faXmark,
|
||||||
|
faBookMedical,
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
import {faWeibo, faQq, faGithubAlt, faSteamSymbol} from '@fortawesome/free-brands-svg-icons'
|
import { faClock, faFolder } from '@fortawesome/free-regular-svg-icons'
|
||||||
|
import { faGithubAlt, faQq, faSteamSymbol, faWeibo } from '@fortawesome/free-brands-svg-icons'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||||
|
|
||||||
// 因为默认添加了 nuxt会造成一些错误,所以不自动添加样式
|
// 因为默认添加了 nuxt会造成一些错误,所以不自动添加样式
|
||||||
config.autoAddCss = false
|
config.autoAddCss = false
|
||||||
@ -26,8 +27,8 @@ config.autoAddCss = false
|
|||||||
export default defineNuxtPlugin((nuxtApp) => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
library.add(
|
library.add(
|
||||||
faClock, faXmark, faBlog, faGauge, faCodeBranch, faCloud, faWeibo, faQq, faGithubAlt, faSteamSymbol, faChevronRight,
|
faClock, faXmark, faBlog, faGauge, faCodeBranch, faCloud, faWeibo, faQq, faGithubAlt, faSteamSymbol, faChevronRight,
|
||||||
faCodeCommit, faCode, faHouse, faPen, faBrush, faChessRook, faBars, faChevronDown, faFolder, faLink
|
faCodeCommit, faCode, faHouse, faPen, faBrush, faChessRook, faBars, faChevronDown, faFolder, faLink, faBookMedical,
|
||||||
)
|
)
|
||||||
|
|
||||||
nuxtApp.vueApp.component('font-awesome-icon', FontAwesomeIcon)
|
nuxtApp.vueApp.component('font-awesome-icon', FontAwesomeIcon)
|
||||||
})
|
})
|
||||||
|
@ -1,48 +1,23 @@
|
|||||||
import md, { type Options } from 'markdown-it'
|
import md, { type Options } from 'markdown-it'
|
||||||
// @ts-ignore
|
|
||||||
import mm from 'markdown-it-mathjax3'
|
import mm from 'markdown-it-mathjax3'
|
||||||
|
|
||||||
const options: Options = {
|
const options: Options = {
|
||||||
// Enable HTML tags in source
|
breaks: true,
|
||||||
html: true,
|
html: true,
|
||||||
|
langPrefix: 'language-',
|
||||||
// Use '/' to close single tags (<br />).
|
linkify: true,
|
||||||
// This is only for full CommonMark compatibility.
|
quotes: '“”‘’',
|
||||||
xhtmlOut: true,
|
typographer: true,
|
||||||
|
xhtmlOut: true,
|
||||||
// Convert '\n' in paragraphs into <br>
|
|
||||||
breaks: true,
|
|
||||||
|
|
||||||
// CSS language prefix for fenced blocks. Can be
|
|
||||||
// useful for external highlighters.
|
|
||||||
langPrefix: 'language-',
|
|
||||||
|
|
||||||
// Autoconvert URL-like text to links
|
|
||||||
linkify: true,
|
|
||||||
|
|
||||||
// Enable some language-neutral replacement + quotes beautification
|
|
||||||
// For the full list of replacements, see https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.mjs
|
|
||||||
typographer: true,
|
|
||||||
|
|
||||||
// Double + single quotes replacement pairs, when typographer enabled,
|
|
||||||
// and smartquotes on. Could be either a String or an Array.
|
|
||||||
//
|
|
||||||
// For example, you can use '«»„“' for Russian, '„“‚‘' for German,
|
|
||||||
// and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
|
|
||||||
quotes: '“”‘’',
|
|
||||||
|
|
||||||
// Highlighter function. Should return escaped HTML,
|
|
||||||
// or '' if the source string is not changed and should be escaped externally.
|
|
||||||
// If result starts with <pre... internal wrapper is skipped.
|
|
||||||
highlight: function (/*str, lang*/) { return ''; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin(() => {
|
||||||
const render = md(options)
|
const render = md(options)
|
||||||
.use(mm, {
|
.use(mm, {
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
provide: {
|
provide: {
|
||||||
mdRender: render
|
mdRender: render,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
import mitt from 'mitt'
|
import mitt from 'mitt'
|
||||||
|
|
||||||
type Events = {
|
interface Events {
|
||||||
openPost: IPost
|
openPost: IPost
|
||||||
startLoading: boolean
|
startLoading: boolean
|
||||||
eventBus: INotification
|
eventBus: INotification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin(() => {
|
||||||
const emitter = mitt<Events>()
|
const emitter = mitt<Events>()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
provide: {
|
provide: {
|
||||||
mitt: emitter
|
mitt: emitter,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// @ts-expect-error
|
// @ts-expect-error: 'vue3-typed-js' has no types
|
||||||
import VueTyped from 'vue3-typed-js'
|
import VueTyped from 'vue3-typed-js'
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
nuxtApp.vueApp.use(VueTyped)
|
nuxtApp.vueApp.use(VueTyped)
|
||||||
})
|
})
|
||||||
|
@ -2,16 +2,15 @@ import type { Config } from 'tailwindcss'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: [
|
||||||
"./components/**/*.{js,vue,ts}",
|
'./components/**/*.{js,vue,ts}',
|
||||||
"./layouts/**/*.vue",
|
'./layouts/**/*.vue',
|
||||||
"./pages/**/*.vue",
|
'./pages/**/*.vue',
|
||||||
"./plugins/**/*.{js,ts}",
|
'./plugins/**/*.{js,ts}',
|
||||||
"./app.vue",
|
'./app.vue',
|
||||||
"./error.vue",
|
'./error.vue',
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
} satisfies Config
|
} satisfies Config
|
||||||
|
|
||||||
|
69
types/blog.d.ts
vendored
69
types/blog.d.ts
vendored
@ -1,69 +0,0 @@
|
|||||||
declare interface IBlogResponse<T> {
|
|
||||||
status: string
|
|
||||||
message: string
|
|
||||||
data: T
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IPostsData {
|
|
||||||
page: number
|
|
||||||
pageSize: number
|
|
||||||
pages: number
|
|
||||||
count: number
|
|
||||||
dataSet: []
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IPost {
|
|
||||||
cid: string
|
|
||||||
title: string
|
|
||||||
created: string
|
|
||||||
modified: string
|
|
||||||
slug: string
|
|
||||||
commentsNum: string
|
|
||||||
type: string
|
|
||||||
digest: string
|
|
||||||
password: string
|
|
||||||
categories: ICategory[]
|
|
||||||
category: string
|
|
||||||
directory: string[]
|
|
||||||
date: IDate
|
|
||||||
year: string
|
|
||||||
month: string
|
|
||||||
day: string
|
|
||||||
hidden: boolean
|
|
||||||
pathinfo: string
|
|
||||||
permalink: string
|
|
||||||
url: string
|
|
||||||
isMarkdown: boolean
|
|
||||||
feedUrl: string
|
|
||||||
feedRssUrl: string
|
|
||||||
feedAtomUrl: string
|
|
||||||
fields: any
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface ICategory {
|
|
||||||
mid: string
|
|
||||||
name: string
|
|
||||||
slug: string
|
|
||||||
type: string
|
|
||||||
description: string
|
|
||||||
count: string
|
|
||||||
order: string
|
|
||||||
parent: string
|
|
||||||
cid: string
|
|
||||||
directory: string[]
|
|
||||||
url: string
|
|
||||||
feedUrl: string
|
|
||||||
feedRssUrl: string
|
|
||||||
feedAtomUrl: string
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IDate {
|
|
||||||
timeStamp: number
|
|
||||||
year: string
|
|
||||||
month: string
|
|
||||||
day: string
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IPostFull extends IPost {
|
|
||||||
text: string
|
|
||||||
}
|
|
72
types/blog.ts
Normal file
72
types/blog.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
declare global {
|
||||||
|
interface IBlogResponse<T> {
|
||||||
|
status: string
|
||||||
|
message: string
|
||||||
|
data: T
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPostsData {
|
||||||
|
page: number
|
||||||
|
pageSize: number
|
||||||
|
pages: number
|
||||||
|
count: number
|
||||||
|
dataSet: []
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPost {
|
||||||
|
cid: string
|
||||||
|
title: string
|
||||||
|
created: string
|
||||||
|
modified: string
|
||||||
|
slug: string
|
||||||
|
commentsNum: string
|
||||||
|
type: string
|
||||||
|
digest: string
|
||||||
|
password: string
|
||||||
|
categories: ICategory[]
|
||||||
|
category: string
|
||||||
|
directory: string[]
|
||||||
|
date: IDate
|
||||||
|
year: string
|
||||||
|
month: string
|
||||||
|
day: string
|
||||||
|
hidden: boolean
|
||||||
|
pathinfo: string
|
||||||
|
permalink: string
|
||||||
|
url: string
|
||||||
|
isMarkdown: boolean
|
||||||
|
feedUrl: string
|
||||||
|
feedRssUrl: string
|
||||||
|
feedAtomUrl: string
|
||||||
|
fields: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICategory {
|
||||||
|
mid: string
|
||||||
|
name: string
|
||||||
|
slug: string
|
||||||
|
type: string
|
||||||
|
description: string
|
||||||
|
count: string
|
||||||
|
order: string
|
||||||
|
parent: string
|
||||||
|
cid: string
|
||||||
|
directory: string[]
|
||||||
|
url: string
|
||||||
|
feedUrl: string
|
||||||
|
feedRssUrl: string
|
||||||
|
feedAtomUrl: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IDate {
|
||||||
|
timeStamp: number
|
||||||
|
year: string
|
||||||
|
month: string
|
||||||
|
day: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPostFull extends IPost {
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
27
types/git.d.ts
vendored
27
types/git.d.ts
vendored
@ -1,27 +0,0 @@
|
|||||||
declare interface IActivity<T = string> {
|
|
||||||
id: string;
|
|
||||||
content: T
|
|
||||||
op_type: string
|
|
||||||
repo: {
|
|
||||||
name: string
|
|
||||||
html_url: string
|
|
||||||
}
|
|
||||||
created: string
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface IContent {
|
|
||||||
Commits: ICommit[]
|
|
||||||
HeadCommit: ICommit
|
|
||||||
CompareURL: string
|
|
||||||
Len: number
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface ICommit {
|
|
||||||
Sha1: string,
|
|
||||||
Message: string,
|
|
||||||
AuthorEmail: string,
|
|
||||||
AuthorName: string,
|
|
||||||
CommitterEmail: string,
|
|
||||||
CommitterName: string,
|
|
||||||
Timestamp: string
|
|
||||||
}
|
|
37
types/git.ts
Normal file
37
types/git.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
declare global {
|
||||||
|
interface IActivity {
|
||||||
|
id: string
|
||||||
|
content: null | string | IContent
|
||||||
|
op_type: 'commit_repo' | 'create_repo' | 'delete_branch' | 'delete_tag' | 'merge_pull_request'
|
||||||
|
repo: {
|
||||||
|
name: string
|
||||||
|
html_url: string
|
||||||
|
owner: {
|
||||||
|
login: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
created: string
|
||||||
|
ref_name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IStrictActivity extends IActivity {
|
||||||
|
content: IContent | null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IContent {
|
||||||
|
Commits: ICommit[]
|
||||||
|
HeadCommit: ICommit
|
||||||
|
CompareURL: string
|
||||||
|
Len: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICommit {
|
||||||
|
Sha1: string
|
||||||
|
Message: string
|
||||||
|
AuthorEmail: string
|
||||||
|
AuthorName: string
|
||||||
|
CommitterEmail: string
|
||||||
|
CommitterName: string
|
||||||
|
Timestamp: string
|
||||||
|
}
|
||||||
|
}
|
5
types/index.d.ts
vendored
5
types/index.d.ts
vendored
@ -1,5 +0,0 @@
|
|||||||
declare interface IBlogResponse<T = null> {
|
|
||||||
status: string
|
|
||||||
message: string
|
|
||||||
data: T
|
|
||||||
}
|
|
7
types/index.ts
Normal file
7
types/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
declare global {
|
||||||
|
interface IBlogResponse<T = null> {
|
||||||
|
status: string
|
||||||
|
message: string
|
||||||
|
data: T
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
declare interface INotification {
|
declare global {
|
||||||
|
interface INotification {
|
||||||
level: 'warning' | 'error' | 'info' | 'success'
|
level: 'warning' | 'error' | 'info' | 'success'
|
||||||
title: string
|
title: string
|
||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user