This commit is contained in:
vastxie
2025-05-31 02:28:46 +08:00
parent 0f7adc5c65
commit 86e2eecc1f
1808 changed files with 183083 additions and 86701 deletions

192
admin/src/router/index.ts Executable file
View File

@@ -0,0 +1,192 @@
import '@/assets/styles/nprogress.scss';
import { useNProgress } from '@vueuse/integrations/useNProgress';
import type { RouteRecordRaw } from 'vue-router';
import { createRouter, createWebHashHistory } from 'vue-router';
// 路由相关数据
import pinia from '@/store';
import useKeepAliveStore from '@/store/modules/keepAlive';
import useMenuStore from '@/store/modules/menu';
import useRouteStore from '@/store/modules/route';
import useSettingsStore from '@/store/modules/settings';
import useUserStore from '@/store/modules/user';
import {
asyncRoutes,
asyncRoutesByFilesystem,
constantRoutes,
constantRoutesByFilesystem,
} from './routes';
const { isLoading } = useNProgress();
const router = createRouter({
history: createWebHashHistory(import.meta.env.VITE_BASE_PATH),
routes:
useSettingsStore(pinia).settings.app.routeBaseOn === 'filesystem'
? constantRoutesByFilesystem
: (constantRoutes as RouteRecordRaw[]),
});
router.beforeEach(async (to, from, next) => {
const settingsStore = useSettingsStore();
const userStore = useUserStore();
const routeStore = useRouteStore();
const menuStore = useMenuStore();
settingsStore.settings.app.enableProgress && (isLoading.value = true);
// 是否已登录
if (userStore.isLogin) {
// 是否已根据权限动态生成并注册路由
if (routeStore.isGenerate) {
// 导航栏如果不是 single 模式,则需要根据 path 定位主导航的选中状态
settingsStore.settings.menu.menuMode !== 'single' && menuStore.setActived(to.path);
// 如果已登录状态下,进入登录页会强制跳转到主页
if (to.name === 'login') {
next({
path: settingsStore.settings.home.fullPath,
replace: true,
});
}
// 如果未开启主页,但进入的是主页,则会进入侧边栏导航第一个模块
else if (
!settingsStore.settings.home.enable &&
to.fullPath === settingsStore.settings.home.fullPath
) {
if (menuStore.sidebarMenus.length > 0) {
next({
path: menuStore.sidebarMenusFirstDeepestPath,
replace: true,
});
}
// 如果侧边栏导航第一个模块均无法命中,则还是进入主页
else {
next();
}
}
// 正常访问页面
else {
next();
}
} else {
// 获取用户权限
settingsStore.settings.app.enablePermission && (await userStore.getPermissions());
// 生成动态路由
switch (settingsStore.settings.app.routeBaseOn) {
case 'frontend':
routeStore.generateRoutesAtFront(asyncRoutes);
break;
// case 'backend':
// await routeStore.generateRoutesAtBack()
// break
case 'filesystem':
routeStore.generateRoutesAtFilesystem(asyncRoutesByFilesystem);
// 文件系统生成的路由,需要手动生成导航数据
switch (settingsStore.settings.menu.baseOn) {
case 'frontend':
menuStore.generateMenusAtFront();
break;
// case 'backend':
// await menuStore.generateMenusAtBack()
// break
}
break;
}
// 注册并记录路由数据
// 记录的数据会在登出时会使用到,不使用 router.removeRoute 是考虑配置的路由可能不一定有设置 name ,则通过调用 router.addRoute() 返回的回调进行删除
const removeRoutes: (() => void)[] = [];
routeStore.flatRoutes.forEach((route) => {
if (!/^(?:https?:|mailto:|tel:)/.test(route.path)) {
removeRoutes.push(router.addRoute(route as RouteRecordRaw));
}
});
if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
routeStore.flatSystemRoutes.forEach((route) => {
removeRoutes.push(router.addRoute(route as RouteRecordRaw));
});
}
routeStore.setCurrentRemoveRoutes(removeRoutes);
// 动态路由生成并注册后,重新进入当前路由
next({
path: to.path,
query: to.query,
replace: true,
});
}
} else {
if (to.name !== 'login') {
next({
name: 'login',
query: {
redirect: to.fullPath !== settingsStore.settings.home.fullPath ? to.fullPath : undefined,
},
});
} else {
next();
}
}
});
router.afterEach((to, from) => {
const settingsStore = useSettingsStore();
const keepAliveStore = useKeepAliveStore();
settingsStore.settings.app.enableProgress && (isLoading.value = false);
// 设置页面 title
if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
settingsStore.setTitle(to.meta.breadcrumbNeste?.at(-1)?.title ?? to.meta.title);
} else {
settingsStore.setTitle(to.meta.title);
}
/**
* 处理普通页面的缓存
*/
// 判断当前页面是否开启缓存,如果开启,则将当前页面的 name 信息存入 keep-alive 全局状态
if (to.meta.cache) {
const componentName = to.matched.at(-1)?.components?.default.name;
if (componentName) {
keepAliveStore.add(componentName);
} else {
// turbo-console-disable-next-line
console.warn('[Fantastic-admin] 该页面组件未设置组件名,会导致缓存失效,请检查');
}
}
// 判断离开页面是否开启缓存,如果开启,则根据缓存规则判断是否需要清空 keep-alive 全局状态里离开页面的 name 信息
if (from.meta.cache) {
const componentName = from.matched.at(-1)?.components?.default.name;
if (componentName) {
// 通过 meta.cache 判断针对哪些页面进行缓存
switch (typeof from.meta.cache) {
case 'string':
if (from.meta.cache !== to.name) {
keepAliveStore.remove(componentName);
}
break;
case 'object':
if (!from.meta.cache.includes(to.name as string)) {
keepAliveStore.remove(componentName);
}
break;
}
// 通过 meta.noCache 判断针对哪些页面不需要进行缓存
if (from.meta.noCache) {
switch (typeof from.meta.noCache) {
case 'string':
if (from.meta.noCache === to.name) {
keepAliveStore.remove(componentName);
}
break;
case 'object':
if (from.meta.noCache.includes(to.name as string)) {
keepAliveStore.remove(componentName);
}
break;
}
}
// 如果进入的是 reload 页面,则也将离开页面的缓存清空
if (to.name === 'reload') {
keepAliveStore.remove(componentName);
}
}
}
document.documentElement.scrollTop = 0;
});
export default router;

View File

@@ -0,0 +1,38 @@
import type { RouteRecordRaw } from 'vue-router';
function Layout() {
return import('@/layouts/index.vue');
}
const routes: RouteRecordRaw = {
path: '/app',
component: Layout,
redirect: '/app/classify',
name: 'AppMenu',
meta: {
title: '插件应用',
icon: 'tdesign:app',
},
children: [
{
path: 'classify',
name: 'AppMenuClassify',
component: () => import('@/views/app/classify.vue'),
meta: {
title: '分类列表',
icon: 'ph:list-fill',
},
},
{
path: 'application',
name: 'Application',
component: () => import('@/views/app/application.vue'),
meta: {
title: '应用列表',
icon: 'clarity:vmw-app-line',
},
},
],
};
export default routes;

View File

@@ -0,0 +1,47 @@
import type { RouteRecordRaw } from 'vue-router';
function Layout() {
return import('@/layouts/index.vue');
}
const routes: RouteRecordRaw = {
path: '/chat',
component: Layout,
redirect: '/chat/chat',
name: 'chatMenu',
meta: {
title: '数据管理',
icon: 'majesticons:data-line',
},
children: [
{
path: 'dashboard',
name: 'dashboardMenu',
component: () => import('@/views/users/index.vue'),
meta: {
title: '用户信息',
icon: 'fa6-solid:list-ul',
},
},
{
path: 'list',
name: 'chatMenuList',
component: () => import('@/views/chat/chat.vue'),
meta: {
title: '对话记录',
icon: 'material-symbols-light:chat-outline',
},
},
{
path: 'auto-reply',
name: 'ReplyMenuList',
component: () => import('@/views/sensitive/autpReply.vue'),
meta: {
title: '内容预设',
icon: 'ic:outline-question-answer',
},
},
],
};
export default routes;

View File

@@ -0,0 +1,35 @@
import type { RouteRecordRaw } from 'vue-router';
function Layout() {
return import('@/layouts/index.vue');
}
const routes: RouteRecordRaw = {
path: '/ai',
component: Layout,
redirect: '/ai/chat-key-list',
name: 'AiMenu',
meta: {
title: '模型管理',
icon: 'hugeicons:ai-book',
},
children: [
{
path: 'keys',
name: 'AiMenuKeys',
component: () => import('@/views/models/key.vue'),
meta: { title: '模型设置', icon: 'ph:open-ai-logo-light' },
},
{
path: 'baseSetting',
name: 'baseSetting',
component: () => import('@/views/models/baseSetting.vue'),
meta: {
title: '基础配置',
icon: 'lets-icons:setting-line',
},
},
],
};
export default routes;

View File

@@ -0,0 +1,56 @@
import type { RouteRecordRaw } from 'vue-router';
function Layout() {
return import('@/layouts/index.vue');
}
const routes: RouteRecordRaw = {
path: '/package',
component: Layout,
redirect: '/package/list',
name: 'packageMenu',
meta: {
title: '套餐管理',
icon: 'icon-park-outline:buy',
},
children: [
{
path: 'order-list',
name: 'OrderMenuList',
component: () => import('@/views/order/index.vue'),
meta: {
title: '订单列表',
icon: 'lets-icons:order',
},
},
{
path: 'account-log',
name: 'AccountLogMenu',
component: () => import('@/views/users/accountLog.vue'),
meta: {
title: '账户明细',
icon: 'carbon:account',
},
},
{
path: 'list',
name: 'packageMenuList',
component: () => import('@/views/package/package.vue'),
meta: {
title: '套餐设置',
icon: 'icon-park-outline:commodity',
},
},
{
path: 'crami',
name: 'cramiMenuList',
component: () => import('@/views/package/crami.vue'),
meta: {
title: '卡密管理',
icon: 'solar:passport-broken',
},
},
],
};
export default routes;

View File

@@ -0,0 +1,74 @@
import type { RouteRecordRaw } from 'vue-router';
function Layout() {
return import('@/layouts/index.vue');
}
const routes: RouteRecordRaw = {
path: '/pay',
component: Layout,
redirect: '/pay/hupijiao',
name: 'PayMenu',
meta: {
title: '支付管理',
icon: 'mingcute:card-pay-line',
},
children: [
{
path: 'wechat',
name: 'WechatConfig',
component: () => import('@/views/pay/wechat.vue'),
meta: {
title: '微信支付',
icon: 'ic:baseline-wechat',
},
},
{
path: 'duluPay',
name: 'DuluPayConfig',
component: () => import('@/views/pay/duluPay.vue'),
meta: {
title: '嘟噜支付',
icon: 'ic:outline-payment',
},
},
{
path: 'epay',
name: 'EpayConfig',
component: () => import('@/views/pay/epay.vue'),
meta: {
title: '易支付',
icon: 'uiw:pay',
},
},
{
path: 'mpay',
name: 'MpayConfig',
component: () => import('@/views/pay/mpay.vue'),
meta: {
title: '码支付',
icon: 'ant-design:pay-circle-outlined',
},
},
{
path: 'hupi',
name: 'HupioConfig',
component: () => import('@/views/pay/hupijiao.vue'),
meta: {
title: '虎皮椒支付',
icon: 'token:pay',
},
},
{
path: 'ltzf',
name: 'LtzfConfig',
component: () => import('@/views/pay/ltzf.vue'),
meta: {
title: '蓝兔支付',
icon: 'ph:rabbit',
},
},
],
};
export default routes;

View File

@@ -0,0 +1,56 @@
import type { RouteRecordRaw } from 'vue-router';
function Layout() {
return import('@/layouts/index.vue');
}
const routes: RouteRecordRaw = {
path: '/secure',
component: Layout,
redirect: '/secure/sensitive-baidu',
name: 'SecureMenu',
meta: {
title: '风控管理',
icon: 'ri:secure-payment-line',
},
children: [
{
path: 'identity-verification',
name: 'IdentityVerification',
component: () => import('@/views/sensitive/identityVerification.vue'),
meta: {
title: '风控安全配置',
icon: 'hugeicons:identification',
},
},
{
path: 'sensitive-violation',
name: 'SensitiveViolationLog',
component: () => import('@/views/sensitive/violation.vue'),
meta: {
title: '违规检测记录',
icon: 'tabler:ban',
},
},
{
path: 'sensitive-baidu',
name: 'SensitiveBaiduyun',
component: () => import('@/views/sensitive/baiduSensitive.vue'),
meta: {
title: '百度云敏感词',
icon: 'ri:baidu-line',
},
},
{
path: 'sensitive-custom',
name: 'SensitiveCuston',
component: () => import('@/views/sensitive/custom.vue'),
meta: {
title: '自定义敏感词',
icon: 'carbon:word-cloud',
},
},
],
};
export default routes;

View File

@@ -0,0 +1,65 @@
import type { RouteRecordRaw } from 'vue-router';
function Layout() {
return import('@/layouts/index.vue');
}
const routes: RouteRecordRaw = {
path: '/storage',
component: Layout,
redirect: '/storage/config',
name: 'StorageMenu',
meta: {
title: '存储配置',
icon: 'mingcute:storage-line',
},
children: [
{
path: 'localStorage',
name: 'LocalStorage',
component: () => import('@/views/storage/localStorage.vue'),
meta: {
title: '本地存储',
icon: 'icon-park-outline:cloud-storage',
},
},
{
path: 's3',
name: 'StorageS3',
component: () => import('@/views/storage/s3.vue'),
meta: {
title: 'S3存储',
icon: 'mdi:aws',
},
},
{
path: 'tencent',
name: 'StorageTencent',
component: () => import('@/views/storage/tencent.vue'),
meta: {
title: '腾讯云COS',
icon: 'teenyicons:cost-estimate-outline',
},
},
{
path: 'ali',
name: 'StorageAli',
component: () => import('@/views/storage/ali.vue'),
meta: {
title: '阿里云OSS',
icon: 'material-symbols:home-storage-outline',
},
},
// {
// path: 'chevereto',
// name: 'StorageChevereto',
// component: () => import('@/views/storage/chevereto.vue'),
// meta: {
// title: 'chevereto图床',
// icon: 'material-symbols:image-outline',
// },
// },
],
};
export default routes;

View File

@@ -0,0 +1,65 @@
import type { RouteRecordRaw } from 'vue-router';
function Layout() {
return import('@/layouts/index.vue');
}
const routes: RouteRecordRaw = {
path: '/system',
component: Layout,
redirect: '/system/base',
name: 'systemMenu',
meta: {
title: '系统管理',
icon: 'tdesign:system-2',
},
children: [
{
path: 'base-configuration',
name: 'ClientBaseConfig',
component: () => import('@/views/system/baseConfiguration.vue'),
meta: {
title: '基础配置',
icon: 'uil:setting',
},
},
{
path: 'points',
name: 'PointsDisplay',
component: () => import('@/views/package/points.vue'),
meta: {
title: '显示设置',
icon: 'mdi:show-outline',
},
},
{
path: 'notice',
name: 'systemMenuNotice',
component: () => import('@/views/system/notice.vue'),
meta: {
title: '公告设置',
icon: 'mdi:notice-board',
},
},
{
path: 'user-agreement',
name: 'userAgreement',
component: () => import('@/views/users/userAgreement.vue'),
meta: {
title: '用户协议',
icon: 'hugeicons:access',
},
},
{
path: 'baidu',
name: 'systemMenuBase',
component: () => import('@/views/system/baiduStatistics.vue'),
meta: {
title: '统计设置',
icon: 'wpf:statistics',
},
},
],
};
export default routes;

View File

@@ -0,0 +1,58 @@
import type { RouteRecordRaw } from 'vue-router';
function Layout() {
return import('@/layouts/index.vue');
}
const routes: RouteRecordRaw = {
path: '/user',
component: Layout,
redirect: '/user/dashboard',
name: 'userMenu',
meta: {
title: '访问管理',
icon: 'mdi:user-outline',
},
children: [
{
path: 'register',
name: 'systemMenuRegister',
component: () => import('@/views/users/register.vue'),
meta: {
title: '注册配置',
icon: 'ant-design:login-outlined',
},
},
{
path: 'email',
name: 'systemMenuEmail',
component: () => import('@/views/users/email.vue'),
meta: {
title: '邮件配置',
icon: 'material-symbols:mail-outline',
},
},
{
path: 'wechat',
name: 'systemMenuWechat',
component: () => import('@/views/users/wechat.vue'),
meta: {
title: '微信配置',
icon: 'la:weixin',
},
},
{
path: 'ali-phone',
name: 'AliPhoneMenu',
component: () => import('@/views/users/phone.vue'),
meta: {
title: '短信配置',
icon: 'tabler:message',
},
},
],
};
export default routes;

126
admin/src/router/routes.ts Executable file
View File

@@ -0,0 +1,126 @@
import generatedRoutes from 'virtual:generated-pages';
import { setupLayouts } from 'virtual:meta-layouts';
import type { RouteRecordRaw } from 'vue-router';
import AppMenu from './modules/app.menu';
import ChatMenu from './modules/chat.menu';
import AiMenu from './modules/model.menu';
import PackageMenu from './modules/package.menu';
import PayMenu from './modules/pay.menu';
import SecureMenu from './modules/secure.menu';
import StorageMenu from './modules/storage.menu';
import SystemMenu from './modules/system.menu';
import UserMenu from './modules/user.menu';
import type { Route } from '#/global';
import useSettingsStore from '@/store/modules/settings';
import Home from '@/views/index.vue';
// 固定路由(默认路由)
const constantRoutes: RouteRecordRaw[] = [
{
path: '/login',
name: 'login',
component: () => import('@/views/login.vue'),
meta: {
title: '登录',
},
},
{
path: '/:all(.*)*',
name: 'notFound',
component: () => import('@/views/[...all].vue'),
meta: {
title: '找不到页面',
},
},
];
// 系统路由
const systemRoutes: RouteRecordRaw[] = [
{
path: '/',
component: () => import('@/layouts/index.vue'),
meta: {
title: () => useSettingsStore().settings.home.title,
breadcrumb: false,
},
children: [
{
path: '',
name: 'home',
component: Home,
meta: {
title: () => useSettingsStore().settings.home.title,
breadcrumb: false,
},
},
{
path: 'reload',
name: 'reload',
component: () => import('@/views/reload.vue'),
meta: {
title: '重新加载',
breadcrumb: false,
},
},
{
path: 'setting',
name: 'personalSetting',
component: () => import('@/views/personal/setting.vue'),
meta: {
title: '个人设置',
cache: 'personalEditPassword',
},
},
{
path: 'edit/password',
name: 'personalEditPassword',
component: () => import('@/views/personal/edit.password.vue'),
meta: {
title: '修改密码',
},
},
],
},
];
// 动态路由(异步路由、导航栏路由)
const asyncRoutes: Route.recordMainRaw[] = [
{
// meta: {
// title: '演示',
// icon: 'sidebar-default',
// },
children: [
SystemMenu,
UserMenu,
AiMenu,
ChatMenu,
AppMenu,
SecureMenu,
StorageMenu,
PackageMenu,
PayMenu,
],
},
];
const constantRoutesByFilesystem = generatedRoutes.filter((item) => {
return item.meta?.enabled !== false && item.meta?.constant === true;
});
const asyncRoutesByFilesystem = setupLayouts(
generatedRoutes.filter((item) => {
return (
item.meta?.enabled !== false && item.meta?.constant !== true && item.meta?.layout !== false
);
}),
);
export {
asyncRoutes,
asyncRoutesByFilesystem,
constantRoutes,
constantRoutesByFilesystem,
systemRoutes,
};