+
{tabs.map((tab, index) => (
-
+
);
}
-
-export default Login;
diff --git a/src/pages/sys/others/calendar/calendar-event-form.tsx b/src/pages/sys/others/calendar/calendar-event-form.tsx
index d8870ca..f912b16 100644
--- a/src/pages/sys/others/calendar/calendar-event-form.tsx
+++ b/src/pages/sys/others/calendar/calendar-event-form.tsx
@@ -97,6 +97,7 @@ export default function CalendarEventForm({
console.log("Validate Failed:", info);
});
}}
+ forceRender
>
diff --git a/src/router/hooks/use-route-to-menu.tsx b/src/router/hooks/use-route-to-menu.tsx
index fba9bd8..620b626 100644
--- a/src/router/hooks/use-route-to-menu.tsx
+++ b/src/router/hooks/use-route-to-menu.tsx
@@ -1,6 +1,7 @@
import type { GetProp, MenuProps } from "antd";
import type { AppRouteObject } from "#/router";
+import * as Icons from "@ant-design/icons";
import { useCallback } from "react";
import { useTranslation } from "react-i18next";
@@ -15,11 +16,22 @@ type MenuItem = GetProp[number];
const renderIcon = (icon: string | React.ReactNode): React.ReactNode => {
if (typeof icon !== "string") return icon;
- return icon.startsWith("ic") ? (
-
- ) : (
-
- );
+ // 判断是否为 iconify 格式
+ if (icon.includes(":")) {
+ return ;
+ }
+
+ // Ant Design 图标(如 "CaretLeftOutlined")
+ if (icon in Icons) {
+ const AntdIcon = (Icons as any)[icon];
+ return ;
+ }
+
+ // 自定义 svg
+ if (icon.includes("ic")) {
+ return ;
+ }
+ return null;
};
/**
diff --git a/src/router/index.tsx b/src/router/index.tsx
index ade7358..dd9c4eb 100644
--- a/src/router/index.tsx
+++ b/src/router/index.tsx
@@ -5,7 +5,7 @@ import { ErrorBoundary } from "react-error-boundary";
import { Navigate, createHashRouter } from "react-router";
import { RouterProvider } from "react-router/dom";
-import DashboardLayout from "@/layouts/dashboard";
+import DefaultLayout from "@/layouts/default";
import PageError from "@/pages/sys/error/PageError";
import Login from "@/pages/sys/login/Login";
import { usePermissionRoutes } from "@/router/hooks";
@@ -38,7 +38,7 @@ export default function Router() {
path: "/",
element: (
-
+
),
children: [
diff --git a/src/store/settingStore.ts b/src/store/settingStore.ts
index 43afe6c..fe0b1de 100644
--- a/src/store/settingStore.ts
+++ b/src/store/settingStore.ts
@@ -15,6 +15,7 @@ type SettingsType = {
fontFamily: string;
fontSize: number;
direction: "ltr" | "rtl";
+ guide: boolean;
};
type SettingStore = {
@@ -40,6 +41,7 @@ const useSettingStore = create()(
fontFamily: FontFamilyPreset.openSans,
fontSize: Number(typographyTokens.fontSize.sm),
direction: "ltr",
+ guide: false,
},
actions: {
setSettings: (settings) => {
diff --git a/vite.config.ts b/vite.config.ts
index e463ef4..6eeb9d7 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,5 +1,7 @@
+import fs from "node:fs";
///
import path from "node:path";
+
import react from "@vitejs/plugin-react";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
@@ -10,14 +12,15 @@ import { defineConfig, loadEnv } from "vite";
import { createHtmlPlugin } from "vite-plugin-html";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import tsconfigPaths from "vite-tsconfig-paths";
-// ... existing imports ...
export default defineConfig(({ mode }) => {
// 加载环境变量
const { VITE_PORT, VITE_TITLE, VITE_APP_BASE_PATH, VITE_LOCAL_API_URL } = loadEnv(mode, process.cwd(), "");
- const base = VITE_APP_BASE_PATH || "/";
+ const base = VITE_APP_BASE_PATH ?? "/";
const isProduction = mode === "production";
+ // 显式读取 package.json(避免 ESM 导入问题)
+ const packageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, "package.json"), "utf-8"));
return {
// 基础路径
base,
@@ -26,10 +29,10 @@ export default defineConfig(({ mode }) => {
server: {
open: true,
host: true,
- port: Number(VITE_PORT) || 3001,
+ port: Number(VITE_PORT) ?? 3001,
proxy: {
"/api": {
- // target: VITE_API_URL || 'http://localhost:3000',
+ // target: VITE_API_URL || "http://localhost:3000",
target: VITE_LOCAL_API_URL || "http://localhost:3000",
changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, ''),
@@ -37,6 +40,13 @@ export default defineConfig(({ mode }) => {
},
},
},
+ // 获取前段项目所需的依赖版本信息
+ define: {
+ __APP_DEPS__: JSON.stringify({
+ dependencies: packageJson.dependencies,
+ devDependencies: packageJson.devDependencies,
+ }),
+ },
// 测试用例配置
test: {
@@ -208,6 +218,189 @@ export default defineConfig(({ mode }) => {
"vendor-utils": ["axios", "dayjs", "i18next", "zustand", "@iconify/react"],
"vendor-charts": ["apexcharts", "react-apexcharts"],
},
+ // 分类配置
+ chunkFileNames: "assets/js/[name]-[hash].js",
+ entryFileNames: "assets/js/[name]-[hash].js",
+ assetFileNames: "assets/[ext]/[name]-[hash].[ext]",
+ // // 更精细的手动分块策略
+ // manualChunks: (id) => {
+ // // 分离核心框架代码
+ // if (id.includes("node_modules/react") || id.includes("node_modules/react-dom")) {
+ // return "vendor-react";
+ // }
+ // // 分离路由相关
+ // if (id.includes("react-router") || id.includes("history")) {
+ // return "vendor-router";
+ // }
+ // // 分离复杂组件库
+ // if (id.includes("antd") || id.includes("@ant-design") || id.includes("framer-motion")) {
+ // return "vendor-ui";
+ // }
+ // // 分离可视化相关
+ // if (
+ // id.includes("apexcharts") ||
+ // id.includes("react-apexcharts") ||
+ // id.includes("d3") ||
+ // id.includes("nivo")
+ // ) {
+ // return "vendor-charts";
+ // }
+ // // 分离国际化相关
+ // if (id.includes("i18next") || id.includes("react-i18next")) {
+ // return "vendor-i18n";
+ // }
+ // // 分离状态管理
+ // if (id.includes("zustand") || id.includes("redux")) {
+ // return "vendor-state";
+ // }
+ // // 分离工具库
+ // if (id.includes("lodash") || id.includes("ramda") || id.includes("dayjs") || id.includes("axios")) {
+ // return "vendor-utils";
+ // }
+ // // 分离富文本编辑器
+ // if (id.includes("react-quill") || id.includes("quill")) {
+ // return "vendor-editor";
+ // }
+ // },
+ // // 优化文件命名策略
+ // chunkFileNames: (chunkInfo) => {
+ // // 对异步 chunk 使用不同的命名策略
+ // if (chunkInfo.isDynamicEntry) {
+ // return "assets/dynamic/[name]-[hash].js";
+ // }
+ // return "assets/chunks/[name]-[hash].js";
+ // },
+ // // 使用 contenthash 替代 hash 提升缓存效率
+ // entryFileNames: "assets/js/[name]-[hash].js",
+ // assetFileNames: "assets/js/[name]-[hash].js",
+
+ // manualChunks: (id) => {
+ // // 核心框架
+ // if (/[\\/]node_modules[\\/](react|react-dom|scheduler|use-sync-external-store)/.test(id)) {
+ // return "vendor-react";
+ // }
+
+ // // 路由系统
+ // if (/[\\/]node_modules[\\/](react-router|history|@remix-run)/.test(id)) {
+ // return "vendor-router";
+ // }
+
+ // // UI 组件库
+ // if (/[\\/]node_modules[\\/](antd|@ant-design|framer-motion|@dnd-kit)/.test(id)) {
+ // return "vendor-ui";
+ // }
+
+ // // 数据可视化
+ // if (/[\\/]node_modules[\\/](apexcharts|d3|nivo|victory)/.test(id)) {
+ // return "vendor-charts";
+ // }
+
+ // // 状态管理
+ // if (/[\\/]node_modules[\\/](zustand|redux|mobx|@tanstack)/.test(id)) {
+ // return "vendor-state";
+ // }
+
+ // // 国际化
+ // if (/[\\/]node_modules[\\/](i18next|react-i18next)/.test(id)) {
+ // return "vendor-i18n";
+ // }
+
+ // // 富文本编辑器
+ // if (/[\\/]node_modules[\\/](react-quill|quill|slate)/.test(id)) {
+ // return "vendor-editor";
+ // }
+
+ // // 工具库
+ // if (/[\\/]node_modules[\\/](lodash|ramda|dayjs|axios|clsx|classnames)/.test(id)) {
+ // return "vendor-utils";
+ // }
+
+ // // Markdown 处理
+ // if (/[\\/]node_modules[\\/](react-markdown|remark|rehype|unified)/.test(id)) {
+ // return "vendor-markdown";
+ // }
+
+ // // 日历组件
+ // if (/[\\/]node_modules[\\/]@fullcalendar/.test(id)) {
+ // return "vendor-calendar";
+ // }
+ // },
+
+ // // 文件输出策略
+ // entryFileNames: "assets/js/[name]-[hash:8].js",
+ // chunkFileNames: (chunkInfo) => {
+ // if (chunkInfo.isDynamicEntry) {
+ // return "assets/async/[name]-[hash:8].js";
+ // }
+ // if (chunkInfo.name.startsWith("vendor-")) {
+ // return "assets/vendor/[name]-[hash:8].js";
+ // }
+ // return "assets/chunks/[name]-[hash:8].js";
+ // },
+
+ // assetFileNames: ({ names }) => {
+ // // const ext = names?.split(".").pop()?.toLowerCase() || "misc";
+ // const firstName = Array.isArray(names) ? names[0] : names;
+ // const ext = firstName?.split(".").pop()?.toLowerCase() || "misc";
+ // // return `assets/${ext}/[name]-[hash][extname]`;
+ // const dirMap = {
+ // css: "css",
+ // svg: "images",
+ // png: "images",
+ // jpg: "images",
+ // jpeg: "images",
+ // webp: "images",
+ // gif: "images",
+ // woff2: "fonts",
+ // woff: "fonts",
+ // ttf: "fonts",
+ // eot: "fonts",
+ // otf: "fonts",
+ // };
+ // return `assets/${dirMap[ext] || "misc"}/[name]-[hash:8][extname]`;
+ // },
+
+ // // 高级优化配置
+ // compact: true,
+ // generatedCode: {
+ // preset: "es2015",
+ // arrowFunctions: true,
+ // constBindings: true,
+ // },
+ // minifyInternalExports: true,
+
+ // // 优化后的资源处理配置
+ // assetFileNames: (assetInfo) => {
+ // if (!assetInfo.fileName) return "assets/[name]-[hash][extname]";
+ // // 使用现代路径解析方式
+ // const { name: fileName, ext: fileExt } = path.parse(assetInfo.fileName);
+ // const extType = fileExt.toLowerCase().replace(".", "");
+ // // 基于类型的目录映射
+ // const typeDirs = {
+ // svg: "images",
+ // png: "images",
+ // jpg: "images",
+ // jpeg: "images",
+ // webp: "images",
+ // gif: "images",
+ // woff: "fonts",
+ // woff2: "fonts",
+ // eot: "fonts",
+ // ttf: "fonts",
+ // otf: "fonts",
+ // css: "css",
+ // };
+ // // 获取分类目录
+ // const dir = typeDirs[extType] || "misc";
+ // const hash = "[hash:10]"; // 缩短 hash 长度
+ // // 处理带路径的资源
+ // const baseName = path.basename(fileName, fileExt);
+ // // 特殊处理 CSS 文件
+ // if (extType === "css") {
+ // return `assets/${dir}/[name]-${hash}${fileExt}`;
+ // }
+ // return `assets/${dir}/${baseName}-${hash}${fileExt}`;
+ // },
},
},
},