feat: 添加NProgress加载进度条和优化UI样式
添加NProgress依赖并集成到路由中,实现页面切换时的加载进度条 优化友链页面、博客分类和文章列表的UI样式 调整登录按钮和图片懒加载的显示效果 新增标签随机颜色功能,提升视觉体验
This commit is contained in:
1
components.d.ts
vendored
1
components.d.ts
vendored
@ -33,6 +33,7 @@ declare module 'vue' {
|
|||||||
NLayoutFooter: typeof import('naive-ui')['NLayoutFooter']
|
NLayoutFooter: typeof import('naive-ui')['NLayoutFooter']
|
||||||
NLayoutHeader: typeof import('naive-ui')['NLayoutHeader']
|
NLayoutHeader: typeof import('naive-ui')['NLayoutHeader']
|
||||||
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
||||||
|
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
||||||
NMenu: typeof import('naive-ui')['NMenu']
|
NMenu: typeof import('naive-ui')['NMenu']
|
||||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
NModal: typeof import('naive-ui')['NModal']
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
|
|||||||
1
extra.d.ts
vendored
1
extra.d.ts
vendored
@ -4,3 +4,4 @@ declare module "vue3-masonry-plus";
|
|||||||
declare module "vite";
|
declare module "vite";
|
||||||
declare module "vue-devui/tag";
|
declare module "vue-devui/tag";
|
||||||
declare module "es-toolkit";
|
declare module "es-toolkit";
|
||||||
|
declare module "nprogress";
|
||||||
@ -40,6 +40,7 @@
|
|||||||
"@vue/tsconfig": "^0.7.0",
|
"@vue/tsconfig": "^0.7.0",
|
||||||
"naive-ui": "^2.43.2",
|
"naive-ui": "^2.43.2",
|
||||||
"npm-run-all2": "^8.0.4",
|
"npm-run-all2": "^8.0.4",
|
||||||
|
"nprogress": "^0.2.0",
|
||||||
"typescript": "~5.8.0",
|
"typescript": "~5.8.0",
|
||||||
"vfonts": "^0.0.3",
|
"vfonts": "^0.0.3",
|
||||||
"vite": "^7.0.6",
|
"vite": "^7.0.6",
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-config-provider :theme-overrides="themeOverrides">
|
<n-config-provider :theme-overrides="themeOverrides">
|
||||||
|
<n-loading-bar-provider>
|
||||||
<n-message-provider>
|
<n-message-provider>
|
||||||
<n-dialog-provider>
|
<n-dialog-provider>
|
||||||
<n-modal-provider>
|
<n-modal-provider>
|
||||||
@ -9,19 +10,23 @@
|
|||||||
</n-modal-provider>
|
</n-modal-provider>
|
||||||
</n-dialog-provider>
|
</n-dialog-provider>
|
||||||
</n-message-provider>
|
</n-message-provider>
|
||||||
</n-config-provider>
|
</n-loading-bar-provider>
|
||||||
|
</n-config-provider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import themeOverrides from '@/util/theme.ts';
|
import themeOverrides from '@/util/theme.ts';
|
||||||
|
|
||||||
import layIndex from './Index.vue';
|
import layIndex from './Index.vue';
|
||||||
// 空白项目入口
|
// 空白项目入口
|
||||||
const logStatus = $store.log.useLogStore()
|
const logStatus = $store.log.useLogStore()
|
||||||
|
|
||||||
// 全局禁用右键菜单
|
// 全局禁用右键菜单
|
||||||
document.oncontextmenu = function () {
|
document.oncontextmenu = function () {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
||||||
if ($cookies.get('userinfo')) logStatus.setIsLogin(true)
|
if ($cookies.get('userinfo')) logStatus.setIsLogin(true)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -10,3 +10,13 @@
|
|||||||
.n-scrollbar-rail__scrollbar {
|
.n-scrollbar-rail__scrollbar {
|
||||||
background-color: #f6cbe770 !important;
|
background-color: #f6cbe770 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 添加渐变背景色 */
|
||||||
|
#nprogress .bar {
|
||||||
|
background: linear-gradient(to right, #ec66ab, #f78c6c, #7ed6df);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加平滑过渡效果 */
|
||||||
|
#nprogress .bar {
|
||||||
|
transition: width 0.2s ease-in-out;
|
||||||
|
}
|
||||||
@ -1,3 +1,5 @@
|
|||||||
@import "./base.less";
|
|
||||||
@import "qweather-icons/font/qweather-icons.css";
|
@import "qweather-icons/font/qweather-icons.css";
|
||||||
@import 'md-editor-v3/lib/preview.css';
|
@import 'md-editor-v3/lib/preview.css';
|
||||||
|
@import "nprogress/nprogress.css";
|
||||||
|
@import "./base.less";
|
||||||
@ -31,8 +31,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
<div v-else class="flex items-center" @click="toLogin">
|
<div v-else class="flex items-center" @click="toLogin">
|
||||||
<n-avatar round class="cursor-pointer"></n-avatar>
|
<n-button size="small" type="primary" @click="toLogin">登录</n-button>
|
||||||
<div class="cursor-pointer ml-2 text-gray text-sm">登录</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 登录弹窗 -->
|
<!-- 登录弹窗 -->
|
||||||
@ -51,8 +50,10 @@
|
|||||||
<div class="cursor-pointer ml-2 text-gray text-sm">{{ userinfo.nickname }}</div>
|
<div class="cursor-pointer ml-2 text-gray text-sm">{{ userinfo.nickname }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="flex items-center">
|
<div v-else class="flex items-center">
|
||||||
<n-avatar round class="cursor-pointer" @click="toLogin"></n-avatar>
|
<!-- <n-avatar round class="cursor-pointer" @click="toLogin"></n-avatar>
|
||||||
<div class="cursor-pointer ml-2 text-gray text-sm" @click="toLogin">登录</div>
|
<div class="cursor-pointer ml-2 text-gray text-sm" @click="toLogin">登录</div> -->
|
||||||
|
<n-button type="primary" size="small" @click="toLogin">登录</n-button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1 +1,4 @@
|
|||||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767254162122" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5014" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M661.648 118H249.104c-39.088 0-70.304 31.752-70.304 70.304v647.392c0 39.096 31.752 70.304 70.304 70.304h525.8c38.552 0 70.296-31.752 70.296-70.304V301.56L661.648 118z m12.664 74.704l96.176 96.184H684.584c-5.408 0-10.272-4.864-10.272-10.272V192.704z m126.928 642.992a26.24 26.24 0 0 1-26.344 26.352H249.104a26.24 26.24 0 0 1-26.344-26.352V188.304a26.24 26.24 0 0 1 26.344-26.344v-0.464h381.328v117.192c0 29.824 24.416 54.16 54.16 54.16h116.656v502.848z" p-id="5015"></path><path d="M348.68 386.072h188.968a21.84 21.84 0 0 0 21.944-21.936 21.84 21.84 0 0 0-21.944-21.944H348.68a21.832 21.832 0 0 0-21.944 21.944 21.84 21.84 0 0 0 21.944 21.936zM674.848 644.8H348.68c-12.208 0-21.944 9.744-21.944 21.944s9.736 21.944 21.944 21.944h326.168c12.208 0 21.944-9.744 21.944-21.944S687.064 644.8 674.848 644.8zM348.68 495.856a21.824 21.824 0 0 0-21.944 21.944 21.832 21.832 0 0 0 21.944 21.936h326.168a21.832 21.832 0 0 0 21.944-21.936 21.832 21.832 0 0 0-21.944-21.944H348.68z" p-id="5016"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6">
|
||||||
|
<path fill-rule="evenodd" d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625ZM7.5 15a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 7.5 15Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H8.25Z" clip-rule="evenodd" />
|
||||||
|
<path d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z" />
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 652 B |
@ -2,3 +2,4 @@
|
|||||||
<path fill-rule="evenodd" d="M9.75 6.75h-3a3 3 0 0 0-3 3v7.5a3 3 0 0 0 3 3h7.5a3 3 0 0 0 3-3v-7.5a3 3 0 0 0-3-3h-3V1.5a.75.75 0 0 0-1.5 0v5.25Zm0 0h1.5v5.69l1.72-1.72a.75.75 0 1 1 1.06 1.06l-3 3a.75.75 0 0 1-1.06 0l-3-3a.75.75 0 1 1 1.06-1.06l1.72 1.72V6.75Z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M9.75 6.75h-3a3 3 0 0 0-3 3v7.5a3 3 0 0 0 3 3h7.5a3 3 0 0 0 3-3v-7.5a3 3 0 0 0-3-3h-3V1.5a.75.75 0 0 0-1.5 0v5.25Zm0 0h1.5v5.69l1.72-1.72a.75.75 0 1 1 1.06 1.06l-3 3a.75.75 0 0 1-1.06 0l-3-3a.75.75 0 1 1 1.06-1.06l1.72 1.72V6.75Z" clip-rule="evenodd" />
|
||||||
<path d="M7.151 21.75a2.999 2.999 0 0 0 2.599 1.5h7.5a3 3 0 0 0 3-3v-7.5c0-1.11-.603-2.08-1.5-2.599v7.099a4.5 4.5 0 0 1-4.5 4.5H7.151Z" />
|
<path d="M7.151 21.75a2.999 2.999 0 0 0 2.599 1.5h7.5a3 3 0 0 0 3-3v-7.5c0-1.11-.603-2.08-1.5-2.599v7.099a4.5 4.5 0 0 1-4.5 4.5H7.151Z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 529 B After Width: | Height: | Size: 530 B |
@ -1 +1,3 @@
|
|||||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767231816303" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12438" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M788.48 629.418667c0-17.066667-5.802667-31.744-17.749333-44.714667L421.205333 235.861333c-12.288-12.288-29.354667-22.869333-49.834667-31.744-21.162667-8.874667-39.936-12.970667-57.002667-12.970667L110.933333 191.146667c-17.066667 0-31.744 6.485333-44.032 18.773333-12.288 12.288-18.773333 26.965333-18.773333 44.032l0 202.752c0 17.066667 4.096 36.522667 12.970667 57.002667 8.874667 21.162667 18.773333 37.546667 31.744 49.834667l349.525333 349.525333c12.288 12.288 26.965333 17.749333 44.032 17.749333 17.066667 0 31.744-5.802667 44.714667-17.749333l239.616-239.616C781.994667 661.162667 788.48 646.485333 788.48 629.418667L788.48 629.418667 788.48 629.418667zM248.490667 392.192c-12.288 12.288-26.965333 18.090667-44.032 18.090667-17.066667 0-31.744-5.802667-44.032-18.090667-12.288-12.288-18.090667-26.965333-18.090667-44.032 0-17.066667 5.802667-31.744 18.090667-44.032 12.288-12.288 26.965333-18.090667 44.032-18.090667 17.066667 0 31.744 5.802667 44.032 18.090667C260.778667 316.416 266.24 331.093333 266.24 348.16 267.264 365.568 260.778667 379.904 248.490667 392.192L248.490667 392.192 248.490667 392.192zM958.122667 584.362667 608.597333 235.861333c-12.288-12.288-29.354667-22.869333-49.834667-31.744C537.6 195.242667 518.826667 191.146667 501.76 191.146667l-109.909333 0c17.066667 0 36.522667 4.096 57.002667 12.970667 21.162667 8.874667 37.546667 18.773333 49.834667 31.744l349.525333 348.501333c12.288 12.970667 18.090667 27.648 18.090667 44.714667s-5.802667 31.744-18.090667 44.032l-229.034667 229.717333c9.898667 9.898667 18.773333 17.066667 25.941333 21.845333 7.168 4.778667 17.066667 6.485333 28.672 6.485333 17.066667 0 31.744-5.802667 44.714667-18.090667l239.616-240.298667c12.288-12.288 17.749333-26.965333 17.749333-44.032S970.069333 597.674667 958.122667 584.362667L958.122667 584.362667 958.122667 584.362667zM958.122667 584.362667" fill="#a7415d" p-id="12439"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6">
|
||||||
|
<path fill-rule="evenodd" d="M5.25 2.25a3 3 0 0 0-3 3v4.318a3 3 0 0 0 .879 2.121l9.58 9.581c.92.92 2.39 1.186 3.548.428a18.849 18.849 0 0 0 5.441-5.44c.758-1.16.492-2.629-.428-3.548l-9.58-9.581a3 3 0 0 0-2.122-.879H5.25ZM6.375 7.5a1.125 1.125 0 1 0 0-2.25 1.125 1.125 0 0 0 0 2.25Z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 411 B |
@ -2,7 +2,7 @@
|
|||||||
<div class="image-container flex w-full">
|
<div class="image-container flex w-full">
|
||||||
<n-image ref="lazyRef" class="lazy__img" :src="url" @load="handleLoad" @error="handleError">
|
<n-image ref="lazyRef" class="lazy__img" :src="url" @load="handleLoad" @error="handleError">
|
||||||
<template #placeholder>
|
<template #placeholder>
|
||||||
<img :src="loading" alt="loading" />
|
<icon-loading class="zhuan text-primary w-12 h-12" />
|
||||||
</template>
|
</template>
|
||||||
<template #error>
|
<template #error>
|
||||||
<img :src="errorImg" alt="error" />
|
<img :src="errorImg" alt="error" />
|
||||||
@ -74,4 +74,21 @@ onMounted(() => {
|
|||||||
padding: 1em;
|
padding: 1em;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.zhuan {
|
||||||
|
animation: spin 1.5s linear infinite;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
13
src/main.ts
13
src/main.ts
@ -3,20 +3,19 @@ import { useConfig } from "@/config";
|
|||||||
import icon from "@/icon/index.ts";
|
import icon from "@/icon/index.ts";
|
||||||
import { createPinia } from "pinia";
|
import { createPinia } from "pinia";
|
||||||
import "virtual:uno.css";
|
import "virtual:uno.css";
|
||||||
import "vue-devui/tag/style.css";
|
import 'vue-devui/tag/style.css';
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
// 自定义主题配置 - 设置主色和二级色
|
// 自定义主题配置 - 设置主色和二级色\
|
||||||
import backTop from "@/components/backTop.vue";
|
import backTop from '@/components/backTop.vue';
|
||||||
import { formatTime } from "@/util";
|
import 'md-editor-v3/lib/style.css';
|
||||||
import "md-editor-v3/lib/style.css";
|
|
||||||
import "vfonts/FiraCode.css";
|
import "vfonts/FiraCode.css";
|
||||||
import Tag from "vue-devui/tag";
|
import Tag from 'vue-devui/tag';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
app.use(Tag);
|
app.use(Tag)
|
||||||
app.use(createPinia());
|
app.use(createPinia());
|
||||||
app.use(router);
|
app.use(router);
|
||||||
// app.use(VMdPreview)
|
// app.use(VMdPreview)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
import NProgress from 'nprogress';
|
||||||
import { createRouter, createWebHistory } from "vue-router";
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
import { routes } from 'vue-router/auto-routes';
|
import { routes } from "vue-router/auto-routes";
|
||||||
|
|
||||||
routes.unshift({
|
routes.unshift({
|
||||||
path: "/",
|
path: "/",
|
||||||
redirect: "/home",
|
redirect: "/home",
|
||||||
@ -8,7 +8,17 @@ routes.unshift({
|
|||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes:routes as any
|
routes: routes as any,
|
||||||
|
});
|
||||||
|
// 前置守卫
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
NProgress.start()
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 后置守卫
|
||||||
|
router.afterEach(() => {
|
||||||
|
NProgress.done()
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@ -25,8 +25,8 @@
|
|||||||
<div
|
<div
|
||||||
class="absolute rounded-md flex px-2 z-10 truncate bottom-0 w-full bg-[#00000070] text-white justify-between items-center">
|
class="absolute rounded-md flex px-2 z-10 truncate bottom-0 w-full bg-[#00000070] text-white justify-between items-center">
|
||||||
<div>由 <span class="text-[#f1d9db] font-600">{{ item.nickname }}</span> 分享</div>
|
<div>由 <span class="text-[#f1d9db] font-600">{{ item.nickname }}</span> 分享</div>
|
||||||
<div class="text-sm cursor-pointer text-primary" @click="downloadFile(item.filepath)">
|
<div class="text-sm cursor-pointer text-[#f6cbe7] flex items-center" @click="downloadFile(item.filepath)">
|
||||||
<icon-download class="w-4 h-4" />
|
<icon-download class="w-5 h-5" />
|
||||||
下载
|
下载
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<n-card class="card w-100 shadow">
|
<n-card class="card w-100 shadow">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex items-center gap-4 text-primary text-xl font-bold">
|
<div class="flex items-center gap-4 text-primary text-xl font-bold">
|
||||||
<icon-loading class="zhuan text-primary w-6 h-6" />
|
<icon-loading class="zhuan text-primary w-6 h-6" />
|
||||||
申请友链
|
申请友链
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -54,8 +54,8 @@
|
|||||||
站点介绍
|
站点介绍
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<n-input type="textarea" :autosize="{ minRows: 2, maxRows: 3 }" maxlength="50" v-model:value="plinkData.desc"
|
<n-input type="textarea" :autosize="{ minRows: 2, maxRows: 3 }" maxlength="50"
|
||||||
placeholder="请用一句话简短的描述你的站点" />
|
v-model:value="plinkData.desc" placeholder="请用一句话简短的描述你的站点" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item path="tagname">
|
<n-form-item path="tagname">
|
||||||
<template #label>
|
<template #label>
|
||||||
@ -64,8 +64,8 @@
|
|||||||
标签
|
标签
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<n-input type="textarea" :autosize="{ minRows: 2, maxRows: 3 }" maxlength="50" v-model:value="plinkData.tagname"
|
<n-input type="textarea" :autosize="{ minRows: 2, maxRows: 3 }" maxlength="50"
|
||||||
placeholder="输入你的标签,用逗号隔开,注意每个标签最多3个字符,且只显示前3个标签哦~" />
|
v-model:value="plinkData.tagname" placeholder="输入你的标签,用逗号隔开,注意每个标签最多3个字符,且只显示前3个标签哦~" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item>
|
<n-form-item>
|
||||||
<n-button class="w-full" type="primary" @click="submitPut">申请友链</n-button>
|
<n-button class="w-full" type="primary" @click="submitPut">申请友链</n-button>
|
||||||
@ -83,13 +83,16 @@
|
|||||||
<div class="ava">
|
<div class="ava">
|
||||||
<n-avatar round size="large" :src="p.avater"></n-avatar>
|
<n-avatar round size="large" :src="p.avater"></n-avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-primary">
|
<em class="bg-[#fbf2e3] px-4 rounded-full text-sm text-primary mb-4">{{ p.name }}</em>
|
||||||
|
<div class="font-bold text-xl text-gray-700 mb-1">
|
||||||
{{ p.title }}
|
{{ p.title }}
|
||||||
</div>
|
</div>
|
||||||
<em class="text-gray-400 text-sm">{{ p.name }}</em>
|
|
||||||
<div class="flex gap-1 truncate">
|
<div class="flex gap-1 truncate mb-1 text-sm">
|
||||||
<div v-for="i in getTags(p.tagname)" :key="i.tid" class="flex items-center gap-1 text-deepp text-sm">
|
<div v-for="i in getTags(p.tagname)" :key="i.tid"
|
||||||
<icon-tag2 class="w-3 h-3 !text-deepp" />{{ i }}
|
:style="{ backgroundColor: getDictValue(tagColorList, 'tag', i, 'color') }"
|
||||||
|
class="flex items-center gap-1 cursor-pointer text-sm text-white rounded-full px-3">
|
||||||
|
<icon-tag2 class="w-3 h-3 " />{{ i }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="truncate w-full text-gray-400 text-sm text-center">
|
<div class="truncate w-full text-gray-400 text-sm text-center">
|
||||||
@ -108,7 +111,7 @@ import siteIcon from '@/icon/plink/site.svg';
|
|||||||
import tagIcon from '@/icon/plink/tag.svg';
|
import tagIcon from '@/icon/plink/tag.svg';
|
||||||
import urlIcon from '@/icon/plink/uri.svg';
|
import urlIcon from '@/icon/plink/uri.svg';
|
||||||
import userIcon from '@/icon/plink/user.svg';
|
import userIcon from '@/icon/plink/user.svg';
|
||||||
|
import { getDictValue } from '@/util';
|
||||||
|
|
||||||
definePage({
|
definePage({
|
||||||
name: 'plink',
|
name: 'plink',
|
||||||
@ -137,18 +140,32 @@ const rules: any = {
|
|||||||
}
|
}
|
||||||
const pList = ref<any>([])
|
const pList = ref<any>([])
|
||||||
const formRef = ref<any>(null)
|
const formRef = ref<any>(null)
|
||||||
|
const tagColorList: any = ref([])
|
||||||
async function getPlinkList() {
|
async function getPlinkList() {
|
||||||
const res = await $http.plink.getPlinkList()
|
const res = await $http.plink.getPlinkList()
|
||||||
|
|
||||||
|
const list: Array<string> = []
|
||||||
|
// 对res.data.tags进行去重
|
||||||
|
res.data.forEach((i: any) => {
|
||||||
|
const t = getTags(i.tagname)
|
||||||
|
t.forEach((ii: string) => {
|
||||||
|
if (!list.includes(ii)) list.push(ii)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
tagColorList.value = list.map((i: string) => {
|
||||||
|
return {
|
||||||
|
tag: i,
|
||||||
|
color: getRandomFromPalette()
|
||||||
|
}
|
||||||
|
})
|
||||||
pList.value = res.data
|
pList.value = res.data
|
||||||
// for (let i = 0; i < 20; i++) {
|
|
||||||
// pList.value.push(res.data[0])
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
function getTags(str: any) {
|
function getTags(str: any) {
|
||||||
str = str.replaceAll(',', ',')
|
str = str.replaceAll(',', ',')
|
||||||
let tags = str.split(',').slice(0, 3)
|
let tags = str.split(',').slice(0, 3)
|
||||||
tags.forEach((tag: any, idx: number) => {
|
tags.forEach((tag: any, idx: number) => {
|
||||||
tags[idx] = tag.trim().slice(0, 3)
|
tags[idx] = tag.trim().slice(0, 4)
|
||||||
});
|
});
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
@ -178,7 +195,23 @@ function submitPut() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成一个随机的鲜艳颜色,和白色能对比
|
||||||
|
|
||||||
|
function getRandomFromPalette(): string {
|
||||||
|
const hue = Math.floor(Math.random() * 360);
|
||||||
|
return hslToHex(hue, 80, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hslToHex(h: number, s: number, l: number): any {
|
||||||
|
l /= 100;
|
||||||
|
const a = s * Math.min(l, 1 - l) / 100;
|
||||||
|
const f = (n: number) => {
|
||||||
|
const k = (n + h / 30) % 12;
|
||||||
|
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
||||||
|
return Math.round(255 * color).toString(16).padStart(2, '0');
|
||||||
|
}
|
||||||
|
return `#${f(0)}${f(8)}${f(4)}`;
|
||||||
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
boxStyle.value = {
|
boxStyle.value = {
|
||||||
height: window.innerHeight - nav.navH - 1 + 'px',
|
height: window.innerHeight - nav.navH - 1 + 'px',
|
||||||
@ -197,11 +230,8 @@ onMounted(() => {
|
|||||||
100% {
|
100% {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.zhuan {
|
.zhuan {
|
||||||
animation: spin 1.5s linear infinite;
|
animation: spin 1.5s linear infinite;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,13 @@
|
|||||||
<div class="card w-2/3 absolute top-16 left-1/6">
|
<div class="card w-2/3 absolute top-16 left-1/6">
|
||||||
<div @click="clickCate(i, idx)" v-for="(i, idx) in cateList" :key="i.cid"
|
<div @click="clickCate(i, idx)" v-for="(i, idx) in cateList" :key="i.cid"
|
||||||
:class="{ '!bg-primary text-white': idx == currentCateIdx }"
|
:class="{ '!bg-primary text-white': idx == currentCateIdx }"
|
||||||
class="relative cate my-4 py-8 px-16 text-center text-xl font-bold rounded text-gray-600 bg-gray-200 cursor-pointer">
|
class="relative cate flex my-6 py-6 px-8 justify-between text-xl font-bold bg-white shadow rounded cursor-pointer">
|
||||||
{{ i.name }}
|
<div class="flex items-center">
|
||||||
<div class="absolute bottom-1 right-4 ext-sm text-deepp flex items-center gap-2">
|
<icon-arti class="w-5 h-5 text-primary mr-3" :class="{ 'text-white': idx == currentCateIdx }"></icon-arti>
|
||||||
<icon-arti class="w-4 h-4 !fill-deepp"></icon-arti>
|
<span>{{ i.name }}</span>
|
||||||
{{ i.total || 0 }}篇文章
|
</div>
|
||||||
|
<div class="bg-primary px-4 rounded-full text-white ext-sm flex items-center gap-2">
|
||||||
|
{{ i.total || 0 }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -36,12 +38,14 @@
|
|||||||
<div class="text-xl font-bold text-primary mb-2">{{ item.title }}</div>
|
<div class="text-xl font-bold text-primary mb-2">{{ item.title }}</div>
|
||||||
<div class="text-sm text-gray-500 mb-4 line-clamp-3">{{ item.pro }}</div>
|
<div class="text-sm text-gray-500 mb-4 line-clamp-3">{{ item.pro }}</div>
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<div v-for="i in getTags(item.tags)" :key="i.tid" class="flex items-center gap-1 text-deepp cursor-pointer">
|
<div v-for="i in getTags(item.tags)" :key="i.tid"
|
||||||
<icon-tag2 class="w-4 h-4 !text-deepp" />{{ i }}
|
:style="{ backgroundColor: getDictValue(tagColorList, 'tag', i, 'color') }"
|
||||||
|
class="flex items-center gap-1 cursor-pointer text-white rounded-full px-3">
|
||||||
|
<icon-tag2 class="w-4 h-4" />{{ i }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="blogList.length != 0" class="mt-20 w-[80%] ml-1/10 cursor-pointer flex justify-center">
|
<div v-show="blogList.length > page_size" class="mt-20 w-[80%] ml-1/10 cursor-pointer flex justify-center">
|
||||||
<n-pagination v-model:page="page_num" :page-count="total" :page-slot="5" @update:page="getBlogList" />
|
<n-pagination v-model:page="page_num" :page-count="total" :page-slot="5" @update:page="getBlogList" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -49,13 +53,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { formatTime } from '@/util'
|
import { formatTime, getDictValue } from '@/util'
|
||||||
definePage({
|
definePage({
|
||||||
name: 'blog',
|
name: 'blog',
|
||||||
meta: {
|
meta: {
|
||||||
title: '文章',
|
title: '文章',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const blogList = ref<any[]>([])
|
const blogList = ref<any[]>([])
|
||||||
const boxStyle = ref({})
|
const boxStyle = ref({})
|
||||||
const nav: any = $store.nav.useNavStore()
|
const nav: any = $store.nav.useNavStore()
|
||||||
@ -65,6 +70,9 @@ const page_size = ref(5)
|
|||||||
const page_num = ref(1)
|
const page_num = ref(1)
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const category = ref('')
|
const category = ref('')
|
||||||
|
|
||||||
|
const tagColorList: any = ref([])
|
||||||
|
|
||||||
function clickCate(item: any, idx: number) {
|
function clickCate(item: any, idx: number) {
|
||||||
if (currentCateIdx.value == idx) {
|
if (currentCateIdx.value == idx) {
|
||||||
currentCateIdx.value = -1
|
currentCateIdx.value = -1
|
||||||
@ -82,8 +90,25 @@ function clickCate(item: any, idx: number) {
|
|||||||
|
|
||||||
async function getBlogList() {
|
async function getBlogList() {
|
||||||
const res = await $http.blog.getBlogList({
|
const res = await $http.blog.getBlogList({
|
||||||
category:category.value,page_size:page_size.value,page_num:page_num.value
|
category: category.value, page_size: page_size.value, page_num: page_num.value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const list: Array<string> = []
|
||||||
|
// 对res.data.tags进行去重
|
||||||
|
res.data.forEach((i: any) => {
|
||||||
|
const t = getTags(i.tags)
|
||||||
|
t.forEach((ii: string) => {
|
||||||
|
if (!list.includes(ii)) list.push(ii)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
tagColorList.value = list.map((i: string) => {
|
||||||
|
return {
|
||||||
|
tag: i,
|
||||||
|
color: getRandomFromPalette()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
blogList.value = res.data
|
blogList.value = res.data
|
||||||
}
|
}
|
||||||
// 计算markdown的文字数量
|
// 计算markdown的文字数量
|
||||||
@ -92,28 +117,46 @@ function getSize(str: string) {
|
|||||||
return str.replace(reg, '').length;
|
return str.replace(reg, '').length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTags(str: any) {
|
// 生成一个随机的鲜艳颜色,和白色能对比
|
||||||
str = str.replaceAll(',', ',')
|
|
||||||
let tags = str.split(',').slice(0, 5)
|
function getRandomFromPalette(): string {
|
||||||
tags.forEach((tag: any, idx: number) => {
|
const hue = Math.floor(Math.random() * 360);
|
||||||
tags[idx] = tag.trim().slice(0, 3)
|
return hslToHex(hue, 80, 50);
|
||||||
});
|
|
||||||
return tags
|
|
||||||
}
|
|
||||||
async function getCateList() {
|
|
||||||
const res = await $http.blog.getCateList()
|
|
||||||
console.log('>>> --> getCateList --> res:', res.data)
|
|
||||||
cateList.value = res.data
|
|
||||||
total.value = cateList.value.reduce((acc: any, cur: any) => acc + cur.total, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
function hslToHex(h: number, s: number, l: number): any {
|
||||||
boxStyle.value = {
|
l /= 100;
|
||||||
height: `calc(100vh - ${nav.navH + 1}px)`,
|
const a = s * Math.min(l, 1 - l) / 100;
|
||||||
|
const f = (n: number) => {
|
||||||
|
const k = (n + h / 30) % 12;
|
||||||
|
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
||||||
|
return Math.round(255 * color).toString(16).padStart(2, '0');
|
||||||
}
|
}
|
||||||
getBlogList()
|
return `#${f(0)}${f(8)}${f(4)}`;
|
||||||
getCateList()
|
}
|
||||||
})
|
|
||||||
|
function getTags(str: any) {
|
||||||
|
str = str.replaceAll(',', ',')
|
||||||
|
let tags = str.split(',').slice(0, 5)
|
||||||
|
tags.forEach((tag: any, idx: number) => {
|
||||||
|
tags[idx] = tag.trim().slice(0, 4)
|
||||||
|
});
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
async function getCateList() {
|
||||||
|
const res = await $http.blog.getCateList()
|
||||||
|
console.log('>>> --> getCateList --> res:', res.data)
|
||||||
|
cateList.value = res.data
|
||||||
|
total.value = cateList.value.reduce((acc: any, cur: any) => acc + cur.total, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
boxStyle.value = {
|
||||||
|
height: `calc(100vh - ${nav.navH + 1}px)`,
|
||||||
|
}
|
||||||
|
getBlogList()
|
||||||
|
getCateList()
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -3609,6 +3609,11 @@ npm-run-all2@^8.0.4:
|
|||||||
shell-quote "^1.7.3"
|
shell-quote "^1.7.3"
|
||||||
which "^5.0.0"
|
which "^5.0.0"
|
||||||
|
|
||||||
|
nprogress@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1"
|
||||||
|
integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==
|
||||||
|
|
||||||
nth-check@^2.0.1:
|
nth-check@^2.0.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
|
resolved "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
|
||||||
|
|||||||
Reference in New Issue
Block a user