修正了一堆东西
This commit is contained in:
parent
376b5964a1
commit
6e3f55480a
@ -1,8 +0,0 @@
|
||||
import {request} from "~/composables/network";
|
||||
|
||||
export async function getBlogRecentPost(): Promise<IBlogResponse<IPostsData>> {
|
||||
return request({
|
||||
url: "/blog/index.php/api/posts",
|
||||
method: "get"
|
||||
})
|
||||
}
|
16
api/git.ts
16
api/git.ts
@ -1,16 +0,0 @@
|
||||
import {request} from "~/composables/network";
|
||||
|
||||
const TOKEN: string = "fb8aec429ea7d0a36f7238dbffda9d2d66c7b045"
|
||||
|
||||
export async function getActivity(): Promise<IActivity[]> {
|
||||
return request({
|
||||
url: '/git/api/v1/users/cantyonion/activities/feeds',
|
||||
method: 'get',
|
||||
headers: {
|
||||
Authorization: ` ${TOKEN}`,
|
||||
},
|
||||
params: {
|
||||
limit: 10
|
||||
}
|
||||
})
|
||||
}
|
4
app.vue
4
app.vue
@ -16,8 +16,8 @@ emitter.on('startLoading', on => {
|
||||
<div ref="loading" class="loading"></div>
|
||||
<div class="relative flex min-h-screen flex-col overflow-hidden bg-blue-50">
|
||||
<nav-bar/>
|
||||
<NuxtPage/>
|
||||
<nuxt-page/>
|
||||
<footer-main/>
|
||||
<alert-notification/>
|
||||
</div>
|
||||
|
||||
</template>
|
47
components/alert/Notification.vue
Normal file
47
components/alert/Notification.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<script lang="ts" setup>
|
||||
const notifications = ref<INotification[]>([]);
|
||||
|
||||
const typeClasses = {
|
||||
success: 'bg-green-100 text-green-800 border border-green-400',
|
||||
error: 'bg-red-100 text-red-800 border border-red-400',
|
||||
warning: 'bg-yellow-100 text-yellow-800 border border-yellow-400',
|
||||
info: 'bg-blue-100 text-blue-800 border border-blue-400',
|
||||
};
|
||||
|
||||
const addNotification = (notification: INotification) => {
|
||||
notifications.value.push(notification);
|
||||
|
||||
// 自动移除通知
|
||||
setTimeout(() => {
|
||||
notifications.value.shift();
|
||||
}, 5000);
|
||||
console.log(1)
|
||||
|
||||
};
|
||||
|
||||
emitter.on('eventBus', addNotification)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
emitter.off('eventBus', addNotification);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed top-5 right-5 space-y-4 z-50">
|
||||
<div
|
||||
v-for="(notification, index) in notifications"
|
||||
:key="index"
|
||||
:class="[
|
||||
'p-4 rounded-lg shadow-lg transition-all duration-300',
|
||||
typeClasses[notification.level]
|
||||
]"
|
||||
>
|
||||
<h3 class="font-bold text-lg">{{ notification.title }}</h3>
|
||||
<p>{{ notification.message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -30,7 +30,7 @@ const handleReload = () => {
|
||||
</div>
|
||||
<div v-else>
|
||||
<!-- 正式内容 -->
|
||||
<span v-if="empty" class="text-2xl font-bold">最近没有动态</span>
|
||||
<span v-if="empty" class="text-center text-2xl font-bold">最近没有动态</span>
|
||||
<slot v-else name="default"/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,6 +7,12 @@ type Widget = {
|
||||
title: string
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
navColor: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
}
|
||||
})
|
||||
// 使用 ref 跟踪是否滚动
|
||||
const scrolled = ref(false)
|
||||
const littleWidget: Widget[] = [
|
||||
@ -94,7 +100,7 @@ const handleScroll = () => {
|
||||
|
||||
const debouncedScroll = debounce(handleScroll, 10)
|
||||
|
||||
const alwaysBlueBackground = computed(() => route.name !== 'index')
|
||||
const alwaysBlueBackground = computed(() => route.name !== 'index' || props.navColor)
|
||||
|
||||
// 在组件挂载和卸载时添加和移除事件监听
|
||||
onMounted(() => {
|
||||
|
@ -3,6 +3,7 @@ import mitt from 'mitt'
|
||||
type Events = {
|
||||
openPost: IPost
|
||||
startLoading: boolean
|
||||
eventBus: INotification
|
||||
}
|
||||
|
||||
const emitter = mitt<Events>()
|
||||
|
@ -1,7 +1,9 @@
|
||||
export const getAssetURL = (image: string) => {
|
||||
// 参数一: 相对路径
|
||||
// 参数二: 当前路径的URL
|
||||
return new URL(`../assets/images/${image}`, import.meta.url).href
|
||||
if (import.meta.client)
|
||||
return new URL(`../assets/images/${image}`, import.meta.url).href
|
||||
return `/_nuxt/assets/images/${image}`
|
||||
}
|
||||
|
||||
export const debounce = (fn: Function, delay: number) => {
|
||||
|
@ -1,24 +1,53 @@
|
||||
import axios from 'axios'
|
||||
import type {AxiosRequestConfig, AxiosError} from 'axios'
|
||||
import type { AsyncData, UseFetchOptions } from '#app'
|
||||
|
||||
export async function request<T = unknown>(config: AxiosRequestConfig<any>): Promise<T> {
|
||||
const instance = axios.create({
|
||||
baseURL: import.meta.env.VITE_BASE_URL,
|
||||
timeout: 5000,
|
||||
headers: {},
|
||||
export function requestCore<DataT>(url: string, options: UseFetchOptions<DataT>) {
|
||||
// const config = useRuntimeConfig()
|
||||
return useFetch(url, {
|
||||
baseURL: "https://cantyonion.site",
|
||||
retry: false,
|
||||
onRequest({ options }) {
|
||||
let token: string = ""
|
||||
if (import.meta.client) {
|
||||
token = localStorage.getItem("token") || ""
|
||||
}
|
||||
if (!options.headers.get('Authorization'))
|
||||
options.headers.set('Authorization', `Bearer ${token}`)
|
||||
},
|
||||
onResponse({ response }) {
|
||||
// HTTP状态码2XX/3XX执行,否则不执行
|
||||
if (response.status < 200 || response.status >= 400) {
|
||||
console.error(`HTTP 错误: ${response.status}`)
|
||||
return
|
||||
}
|
||||
// 业务code状态码判断
|
||||
// if (response._data.code !== 200) {
|
||||
//
|
||||
// }
|
||||
},
|
||||
onResponseError({ response }) {
|
||||
emitter.emit('eventBus', {
|
||||
level: 'error',
|
||||
title: `HTTP错误:${response.status}`,
|
||||
message: response.statusText
|
||||
})
|
||||
},
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
instance.interceptors.request.use(config => {
|
||||
return config
|
||||
}, error => error)
|
||||
export function get<DataT, ErrorT>(url: string, options?: UseFetchOptions<DataT>): () => Promise<DataT> {
|
||||
let result: AsyncData<DataT, ErrorT> | null = null
|
||||
|
||||
// bug fixed on csdn https://blog.csdn.net/qq_45325810/article/details/120704910
|
||||
instance.interceptors.response.use(resource => {
|
||||
if (resource.status === 200) return resource
|
||||
return Promise.reject(new Error(resource.data))
|
||||
}, (error: AxiosError) => {
|
||||
return Promise.reject(error.response ? error.response.data : error.code)
|
||||
})
|
||||
return async (): Promise<DataT> => {
|
||||
if (result != null) {
|
||||
await result.refresh()
|
||||
return result.data.value
|
||||
}
|
||||
|
||||
return instance.request<T>(config).then(res => res.data)
|
||||
result = await requestCore(url, {
|
||||
method: 'GET',
|
||||
...options,
|
||||
}) as AsyncData<DataT, ErrorT>
|
||||
return result.data.value
|
||||
}
|
||||
}
|
60
error.vue
Normal file
60
error.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<script setup lang="ts">
|
||||
import type { NuxtError } from '#app'
|
||||
|
||||
const props = defineProps({
|
||||
error: Object as () => NuxtError
|
||||
})
|
||||
|
||||
const errorMessages: Record<string, { icon: string; title: string; message: string }> = {
|
||||
'400': {
|
||||
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']);
|
||||
console.error(props.error)
|
||||
|
||||
onMounted(() => {
|
||||
emitter.emit("startLoading", false)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative flex min-h-screen flex-col overflow-hidden bg-blue-50">
|
||||
<nav-bar :nav-color="true"/>
|
||||
<alert :title="errorContent.title" :message="errorContent.message" :icon="errorContent.icon"/>
|
||||
<footer-main/>
|
||||
<alert-notification/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -11,13 +11,13 @@ export default defineNuxtConfig({
|
||||
head: {
|
||||
title: 'CANTYONION.SITE',
|
||||
meta: [
|
||||
{name: 'viewport', content: 'width=device-width, initial-scale=1'},
|
||||
{name: 'charset', content: 'utf-8'},
|
||||
{name: 'keywords', content: 'cantyonion, onion, 洋葱, 博客, 学习, 主页, index'},
|
||||
{name: 'description', content: 'cantyonion的超级基地,进来看看吧!'},
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||
{ name: 'charset', content: 'utf-8' },
|
||||
{ name: 'keywords', content: 'cantyonion, onion, 洋葱, 博客, 学习, 主页, index' },
|
||||
{ name: 'description', content: 'cantyonion的超级基地,进来看看吧!' },
|
||||
],
|
||||
link: [
|
||||
{rel: 'icon', href: '/favicon.png'},
|
||||
{ rel: 'icon', href: '/favicon.png' },
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -43,14 +43,11 @@ export default defineNuxtConfig({
|
||||
}
|
||||
}
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://cantyonion.site',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, '')
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
gitApiKey: 'fb8aec429ea7d0a36f7238dbffda9d2d66c7b045',
|
||||
baseURL: 'https://cantyonion.site'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -19,6 +19,7 @@
|
||||
"@pinia-plugin-persistedstate/nuxt": "^1.2.1",
|
||||
"@pinia/nuxt": "^0.7.0",
|
||||
"axios": "^1.7.7",
|
||||
"mathjax": "^3.2.2",
|
||||
"mitt": "^3.0.1",
|
||||
"nuxt": "^3.14.159",
|
||||
"vue": "latest",
|
||||
|
11
pages/auth.vue
Normal file
11
pages/auth.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
14
pages/blog/[cid].vue
Normal file
14
pages/blog/[cid].vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const cid = route.params.cid as string
|
||||
onMounted( () => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,48 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const errorCode = route.params.code as string
|
||||
const errorMessages: Record<string, { icon: string; title: string; message: string }> = {
|
||||
'400': {
|
||||
icon: '❓',
|
||||
title: '哦豁!请求有点问题',
|
||||
message: '请求似乎有点小问题,服务器君有点摸不着头脑呢!',
|
||||
},
|
||||
'403': {
|
||||
icon: '🔒',
|
||||
title: '哎呀!大门被锁住了',
|
||||
message: '你没有权限访问这个角落,试试返回首页吧!',
|
||||
},
|
||||
'404': {
|
||||
icon: '🌌',
|
||||
title: '哎呀!你发现了超级基地的秘密角落',
|
||||
message: '看起来你找的页面藏到了未知的时空层里!',
|
||||
},
|
||||
'500': {
|
||||
icon: '🚀',
|
||||
title: '糟糕!超级基地出了一点故障',
|
||||
message: '服务器君正在努力解决问题,请稍等片刻!',
|
||||
},
|
||||
'502': {
|
||||
icon: '👽',
|
||||
title: '糟糕!基地信号被外星人拦截了',
|
||||
message: '网络通道似乎遇到了问题,请稍后再试!',
|
||||
},
|
||||
default: {
|
||||
icon: '⚠️',
|
||||
title: '哦豁!此时遇到了点小麻烦',
|
||||
message: '请求在穿越洋葱星球时迷路了,请返回首页重新探索!',
|
||||
},
|
||||
};
|
||||
const errorCode = parseInt(route.params.code as string, 10)
|
||||
|
||||
const errorContent = computed(() => errorMessages[errorCode] || errorMessages['default']);
|
||||
if (import.meta.server)
|
||||
throw createError({
|
||||
statusCode: errorCode,
|
||||
})
|
||||
else
|
||||
showError({statusCode: errorCode})
|
||||
|
||||
onMounted(() => {
|
||||
emitter.emit("startLoading", false)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<alert :title="errorContent.title" :message="errorContent.message" :icon="errorContent.icon"/>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
@ -1,7 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {getBlogRecentPost} from "~/api/blog";
|
||||
import {getActivity} from "~/api/git"
|
||||
|
||||
const recentPosts = ref<IPost[] | null>(null);
|
||||
const recentActivities = ref<IActivity[] | null>(null);
|
||||
@ -14,6 +12,17 @@ const isError = ref({
|
||||
git: false
|
||||
});
|
||||
|
||||
const config = useRuntimeConfig()
|
||||
const getBlogRecentPost = get<IBlogResponse<IPostsData>, any>('/blog/index.php/api/posts')
|
||||
const getActivity = get<IActivity[], any>('/git/api/v1/users/cantyonion/activities/feeds', {
|
||||
headers: {
|
||||
Authorization: ` ${config.public.gitApiKey}`
|
||||
},
|
||||
params: {
|
||||
limit: 10,
|
||||
}
|
||||
})
|
||||
|
||||
const hasPosts = computed(() => (recentPosts.value ?? []).length > 0);
|
||||
const hasActivities = computed(() => (recentActivities.value ?? []).length > 0);
|
||||
const postsData = computed(() => {
|
||||
@ -41,30 +50,33 @@ const activitiesData = computed((): IActivity<IContent>[] => {
|
||||
const reloadPosts = async () => {
|
||||
try {
|
||||
isLoading.value.blog = true
|
||||
const postData = await getBlogRecentPost();
|
||||
recentPosts.value = postData.data.dataSet
|
||||
isLoading.value.blog = false
|
||||
const postData = await getBlogRecentPost()
|
||||
recentPosts.value = postData!.data.dataSet
|
||||
} catch (e) {
|
||||
isLoading.value.blog = false
|
||||
isError.value.blog = true
|
||||
}
|
||||
finally {
|
||||
isLoading.value.blog = false
|
||||
}
|
||||
}
|
||||
|
||||
const reloadActivities = async () => {
|
||||
try {
|
||||
isLoading.value.git = true
|
||||
recentActivities.value = await getActivity()
|
||||
isLoading.value.git = false
|
||||
} catch (e) {
|
||||
isLoading.value.git = false
|
||||
isError.value.git = true
|
||||
console.log(e)
|
||||
}
|
||||
finally {
|
||||
isLoading.value.git = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await reloadPosts()
|
||||
await reloadActivities()
|
||||
await reloadPosts()
|
||||
await reloadActivities()
|
||||
|
||||
onMounted(() => {
|
||||
emitter.emit('startLoading', false)
|
||||
})
|
||||
</script>
|
||||
|
5
types/notification.ts
Normal file
5
types/notification.ts
Normal file
@ -0,0 +1,5 @@
|
||||
declare interface INotification {
|
||||
level: 'warning' | 'error' | 'info' | 'success'
|
||||
title: string
|
||||
message: string
|
||||
}
|
@ -3319,6 +3319,11 @@ make-dir@^3.1.0:
|
||||
dependencies:
|
||||
semver "^6.0.0"
|
||||
|
||||
mathjax@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.npmmirror.com/mathjax/-/mathjax-3.2.2.tgz#c754d7b46a679d7f3fa03543d6b8bf124ddf9f6b"
|
||||
integrity sha512-Bt+SSVU8eBG27zChVewOicYs7Xsdt40qm4+UpHyX7k0/O9NliPc+x77k1/FEsPsjKPZGJvtRZM1vO+geW0OhGw==
|
||||
|
||||
mdn-data@2.0.28:
|
||||
version "2.0.28"
|
||||
resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba"
|
||||
|
Loading…
x
Reference in New Issue
Block a user