linyuanjie před 4 měsíci
rodič
revize
81c875ad3c
33 změnil soubory, kde provedl 594 přidání a 971 odebrání
  1. 0 1
      .env
  2. 1 4
      .env.development
  3. 4 3
      .env.production
  4. 4 4
      src/components/chart/chart.tsx
  5. 20 20
      src/components/chart/styles.css.ts
  6. 60 60
      src/components/chart/useChart.ts
  7. 0 23
      src/pages/dashboard/analysis/analysis-card.tsx
  8. 0 41
      src/pages/dashboard/analysis/analysis-news.tsx
  9. 0 55
      src/pages/dashboard/analysis/analysis-order-timeline.tsx
  10. 0 48
      src/pages/dashboard/analysis/analysis-tasks.tsx
  11. 0 23
      src/pages/dashboard/analysis/analysis-traffic-card.tsx
  12. 0 157
      src/pages/dashboard/analysis/index.tsx
  13. 67 0
      src/pages/dashboard/components/area-download.tsx
  14. 6 6
      src/pages/dashboard/components/banner-card.tsx
  15. 0 0
      src/pages/dashboard/components/carousel-card.tsx
  16. 3 3
      src/pages/dashboard/components/conversion_applications.tsx
  17. 19 19
      src/pages/dashboard/components/current-download.tsx
  18. 110 0
      src/pages/dashboard/components/new-invoice.tsx
  19. 10 10
      src/pages/dashboard/components/top-authors.tsx
  20. 35 35
      src/pages/dashboard/components/top-installed.tsx
  21. 32 32
      src/pages/dashboard/components/top-related.tsx
  22. 21 21
      src/pages/dashboard/components/total-card.tsx
  23. 88 7
      src/pages/dashboard/index.tsx
  24. 0 67
      src/pages/dashboard/workbench/area-download.tsx
  25. 0 91
      src/pages/dashboard/workbench/index.tsx
  26. 0 110
      src/pages/dashboard/workbench/new-invoice.tsx
  27. 32 64
      src/router/hooks/use-permission-routes.tsx
  28. 3 18
      src/router/index.tsx
  29. 35 0
      src/router/routes/modules/constantRoutes.tsx
  30. 37 41
      src/router/routes/modules/dashboard.tsx
  31. 1 1
      src/store/userStore.ts
  32. 3 4
      src/vite-env.d.ts
  33. 3 3
      vite.config.ts

+ 0 - 1
.env

@@ -1,2 +1 @@
 # 权限路由模式 permission | module
-VITE_APP_ROUTER_MODE = permission

+ 1 - 4
.env.development

@@ -1,7 +1,4 @@
-VITE_APP_BASE_API=/api
-VITE_APP_HOMEPAGE=/dashboard/index
-VITE_APP_BASE_PATH=/
-
 VITE_APP_ENV = dev
 VITE_APP_URL = https://pre.yaoeasier.com/
 VITE_APP_TITLE = 要易业务管理系统
+VITE_APP_HOMEPAGE = /dashboard/index

+ 4 - 3
.env.production

@@ -1,3 +1,4 @@
-VITE_APP_BASE_API=/api
-VITE_APP_HOMEPAGE=/dashboard/index
-VITE_APP_BASE_PATH=/
+VITE_APP_ENV = production
+VITE_APP_URL = https://backend.yaoeasier.com/
+VITE_APP_TITLE = 要易业务管理系统
+VITE_APP_HOMEPAGE = /dashboard/index

+ 4 - 4
src/components/chart/chart.tsx

@@ -1,8 +1,8 @@
-import { memo } from "react";
-import ApexChart from "react-apexcharts";
-import { chartWrapper } from "./styles.css";
+import { memo } from 'react';
+import ApexChart from 'react-apexcharts';
+import { chartWrapper } from './styles.css';
 
-import type { Props as ApexChartProps } from "react-apexcharts";
+import type { Props as ApexChartProps } from 'react-apexcharts';
 
 function Chart(props: ApexChartProps) {
 	return (

+ 20 - 20
src/components/chart/styles.css.ts

@@ -1,54 +1,54 @@
-import { themeVars } from "@/theme/theme.css";
-import { rgbAlpha } from "@/utils/theme";
-import { globalStyle } from "@vanilla-extract/css";
-import { style } from "@vanilla-extract/css";
+import { themeVars } from '@/theme/theme.css';
+import { rgbAlpha } from '@/utils/theme';
+import { globalStyle } from '@vanilla-extract/css';
+import { style } from '@vanilla-extract/css';
 
-export const chartWrapper = style({}, "apexcharts-wrapper");
+export const chartWrapper = style({}, 'apexcharts-wrapper');
 
 // TOOLTIP
 globalStyle(`${chartWrapper} .apexcharts-tooltip`, {
 	color: themeVars.colors.text.primary,
 	borderRadius: themeVars.borderRadius.lg,
-	backdropFilter: "blur(6px)",
+	backdropFilter: 'blur(6px)',
 	backgroundColor: rgbAlpha(themeVars.colors.background.paperChannel, 0.8),
-	boxShadow: themeVars.shadows.card,
+	boxShadow: themeVars.shadows.card
 });
 
 globalStyle(`${chartWrapper} .apexcharts-tooltip-title`, {
-	textAlign: "center",
-	fontWeight: "bold",
-	backgroundColor: themeVars.colors.background.neutral,
+	textAlign: 'center',
+	fontWeight: 'bold',
+	backgroundColor: themeVars.colors.background.neutral
 });
 
 // TOOLTIP X
 globalStyle(`${chartWrapper} .apexcharts-xaxistooltip`, {
 	color: themeVars.colors.text.primary,
 	borderRadius: themeVars.borderRadius.lg,
-	backdropFilter: "blur(6px)",
-	borderColor: "transparent",
+	backdropFilter: 'blur(6px)',
+	borderColor: 'transparent',
 	boxShadow: themeVars.shadows.card,
-	backgroundColor: themeVars.colors.background.paper,
+	backgroundColor: themeVars.colors.background.paper
 });
 
 globalStyle(`${chartWrapper} .apexcharts-xaxistooltip::before`, {
-	borderBottomColor: rgbAlpha(themeVars.colors.background.paperChannel, 0.8),
+	borderBottomColor: rgbAlpha(themeVars.colors.background.paperChannel, 0.8)
 });
 
 globalStyle(`${chartWrapper} .apexcharts-xaxistooltip::after`, {
-	borderBottomColor: themeVars.colors.background.paper,
+	borderBottomColor: themeVars.colors.background.paper
 });
 
 // LEGEND
 globalStyle(`${chartWrapper} .apexcharts-legend`, {
-	padding: 0,
+	padding: 0
 });
 
 globalStyle(`${chartWrapper} .apexcharts-legend-series`, {
-	display: "inline-flex !important",
-	alignItems: "center",
+	display: 'inline-flex !important',
+	alignItems: 'center'
 });
 
 globalStyle(`${chartWrapper} .apexcharts-legend-text`, {
-	lineHeight: "18px",
-	textTransform: "capitalize",
+	lineHeight: '18px',
+	textTransform: 'capitalize'
 });

+ 60 - 60
src/components/chart/useChart.ts

@@ -1,28 +1,28 @@
-import { themeVars } from "@/theme/theme.css";
-import { removePx } from "@/utils/theme";
-import type { ApexOptions } from "apexcharts";
-import { mergeDeepRight } from "ramda";
+import { themeVars } from '@/theme/theme.css';
+import { removePx } from '@/utils/theme';
+import type { ApexOptions } from 'apexcharts';
+import { mergeDeepRight } from 'ramda';
 
-import { useSettings } from "@/store/settingStore";
-import { breakpointsTokens } from "@/theme/tokens/breakpoints";
-import { paletteColors, presetsColors } from "@/theme/tokens/color";
+import { useSettings } from '@/store/settingStore';
+import { breakpointsTokens } from '@/theme/tokens/breakpoints';
+import { paletteColors, presetsColors } from '@/theme/tokens/color';
 
 export default function useChart(options: ApexOptions) {
 	const { themeColorPresets } = useSettings();
 
 	const LABEL_TOTAL = {
 		show: true,
-		label: "Total",
+		label: 'Total',
 		color: themeVars.colors.text.secondary,
 		fontSize: themeVars.typography.fontSize.sm,
-		lineHeight: themeVars.typography.lineHeight.tight,
+		lineHeight: themeVars.typography.lineHeight.tight
 	};
 
 	const LABEL_VALUE = {
 		offsetY: 8,
 		color: themeVars.colors.text.primary,
 		fontSize: themeVars.typography.fontSize.sm,
-		lineHeight: themeVars.typography.lineHeight.tight,
+		lineHeight: themeVars.typography.lineHeight.tight
 	};
 
 	const baseOptions: ApexOptions = {
@@ -38,7 +38,7 @@ export default function useChart(options: ApexOptions) {
 			paletteColors.warning.light,
 			paletteColors.info.light,
 			paletteColors.error.light,
-			paletteColors.success.light,
+			paletteColors.success.light
 		],
 
 		// Chart
@@ -46,47 +46,47 @@ export default function useChart(options: ApexOptions) {
 			toolbar: { show: false },
 			zoom: { enabled: false },
 			foreColor: themeVars.colors.text.disabled,
-			fontFamily: themeVars.typography.fontFamily.primary,
+			fontFamily: themeVars.typography.fontFamily.primary
 		},
 
 		// States
 		states: {
 			hover: {
 				filter: {
-					type: "lighten",
-					value: 0.04,
-				},
+					type: 'lighten',
+					value: 0.04
+				}
 			},
 			active: {
 				filter: {
-					type: "darken",
-					value: 0.88,
-				},
-			},
+					type: 'darken',
+					value: 0.88
+				}
+			}
 		},
 
 		// Fill
 		fill: {
 			opacity: 1,
 			gradient: {
-				type: "vertical",
+				type: 'vertical',
 				shadeIntensity: 0,
 				opacityFrom: 0.4,
 				opacityTo: 0,
-				stops: [0, 100],
-			},
+				stops: [0, 100]
+			}
 		},
 
 		// Datalabels
 		dataLabels: {
-			enabled: false,
+			enabled: false
 		},
 
 		// Stroke
 		stroke: {
 			width: 3,
-			curve: "smooth",
-			lineCap: "round",
+			curve: 'smooth',
+			lineCap: 'round'
 		},
 
 		// Grid
@@ -95,46 +95,46 @@ export default function useChart(options: ApexOptions) {
 			borderColor: themeVars.colors.background.neutral,
 			xaxis: {
 				lines: {
-					show: false,
-				},
-			},
+					show: false
+				}
+			}
 		},
 
 		// Xaxis
 		xaxis: {
 			axisBorder: { show: false },
-			axisTicks: { show: false },
+			axisTicks: { show: false }
 		},
 
 		// Markers
 		markers: {
-			size: 0,
+			size: 0
 		},
 
 		// Tooltip
 		tooltip: {
 			theme: undefined,
 			x: {
-				show: true,
-			},
+				show: true
+			}
 		},
 
 		// Legend
 		legend: {
 			show: true,
 			fontSize: themeVars.typography.fontSize.sm,
-			position: "top",
-			horizontalAlign: "right",
+			position: 'top',
+			horizontalAlign: 'right',
 			markers: {
-				strokeWidth: 0,
+				strokeWidth: 0
 			},
 			fontWeight: 500,
 			itemMargin: {
-				horizontal: 8,
+				horizontal: 8
 			},
 			labels: {
-				colors: themeVars.colors.text.primary,
-			},
+				colors: themeVars.colors.text.primary
+			}
 		},
 
 		// plotOptions
@@ -142,9 +142,9 @@ export default function useChart(options: ApexOptions) {
 			// Bar
 			bar: {
 				borderRadius: 4,
-				columnWidth: "28%",
-				borderRadiusApplication: "end",
-				borderRadiusWhenStacked: "last",
+				columnWidth: '28%',
+				borderRadiusApplication: 'end',
+				borderRadiusWhenStacked: 'last'
 			},
 
 			// Pie + Donut
@@ -153,40 +153,40 @@ export default function useChart(options: ApexOptions) {
 					labels: {
 						show: true,
 						value: LABEL_VALUE,
-						total: LABEL_TOTAL,
-					},
-				},
+						total: LABEL_TOTAL
+					}
+				}
 			},
 
 			// Radialbar
 			radialBar: {
 				track: {
-					strokeWidth: "100%",
+					strokeWidth: '100%'
 				},
 				dataLabels: {
 					value: LABEL_VALUE,
-					total: LABEL_TOTAL,
-				},
+					total: LABEL_TOTAL
+				}
 			},
 
 			// Radar
 			radar: {
 				polygons: {
-					fill: { colors: ["transparent"] },
+					fill: { colors: ['transparent'] },
 					strokeColors: themeVars.colors.background.neutral,
-					connectorColors: themeVars.colors.background.neutral,
-				},
+					connectorColors: themeVars.colors.background.neutral
+				}
 			},
 
 			// polarArea
 			polarArea: {
 				rings: {
-					strokeColor: themeVars.colors.background.neutral,
+					strokeColor: themeVars.colors.background.neutral
 				},
 				spokes: {
-					connectorColors: themeVars.colors.background.neutral,
-				},
-			},
+					connectorColors: themeVars.colors.background.neutral
+				}
+			}
 		},
 
 		// Responsive
@@ -195,17 +195,17 @@ export default function useChart(options: ApexOptions) {
 				// sm
 				breakpoint: removePx(breakpointsTokens.sm),
 				options: {
-					plotOptions: { bar: { columnWidth: "40%" } },
-				},
+					plotOptions: { bar: { columnWidth: '40%' } }
+				}
 			},
 			{
 				// md
 				breakpoint: removePx(breakpointsTokens.md),
 				options: {
-					plotOptions: { bar: { columnWidth: "32%" } },
-				},
-			},
-		],
+					plotOptions: { bar: { columnWidth: '32%' } }
+				}
+			}
+		]
 	};
 
 	return mergeDeepRight(baseOptions, options) as ApexOptions;

+ 0 - 23
src/pages/dashboard/analysis/analysis-card.tsx

@@ -1,23 +0,0 @@
-import type { CSSProperties } from "react";
-
-type Props = {
-	cover: string;
-	subtitle: string;
-	title: string;
-	style?: CSSProperties;
-};
-
-export default function AnalysisCard({ cover, subtitle, title, style }: Props) {
-	return (
-		<div
-			className="flex flex-col items-center rounded-2xl py-10"
-			style={{
-				...style,
-			}}
-		>
-			<img src={cover} alt="" />
-			<span className="text-3xl font-bold">{title}</span>
-			<span className="text-sm">{subtitle}</span>
-		</div>
-	);
-}

+ 0 - 41
src/pages/dashboard/analysis/analysis-news.tsx

@@ -1,41 +0,0 @@
-import { faker } from "@faker-js/faker";
-import { Avatar, List } from "antd";
-
-const data = [
-	{
-		title: faker.lorem.words(),
-	},
-	{
-		title: faker.lorem.words(),
-	},
-	{
-		title: faker.lorem.words(),
-	},
-	{
-		title: faker.lorem.words(),
-	},
-];
-export default function AnalysisNews() {
-	return (
-		<List
-			size="small"
-			pagination={{ position: "bottom", align: "end" }}
-			dataSource={data}
-			renderItem={(item) => (
-				<List.Item>
-					<List.Item.Meta
-						avatar={
-							<Avatar
-								shape="square"
-								size={48}
-								src={faker.image.urlLoremFlickr()}
-							/>
-						}
-						title={item.title}
-						description={faker.lorem.sentence()}
-					/>
-				</List.Item>
-			)}
-		/>
-	);
-}

+ 0 - 55
src/pages/dashboard/analysis/analysis-order-timeline.tsx

@@ -1,55 +0,0 @@
-import { themeVars } from "@/theme/theme.css";
-import { Timeline, Typography } from "antd";
-
-export default function AnalysisOrderTimeline() {
-	return (
-		<Timeline
-			items={[
-				{
-					color: themeVars.colors.palette.primary.default,
-					children: (
-						<div className="flex flex-col">
-							<Typography.Text strong>1983, orders,$4220</Typography.Text>
-							<Typography.Text type="secondary" className="text-xs">
-								08 Oct 2023 7:19 PM
-							</Typography.Text>
-						</div>
-					),
-				},
-				{
-					color: themeVars.colors.palette.info.default,
-					children: (
-						<div className="flex flex-col">
-							<Typography.Text strong>Order #37745 from September</Typography.Text>
-							<Typography.Text type="secondary" className="text-xs">
-								06 Oct 2023 5:19 PM
-							</Typography.Text>
-						</div>
-					),
-				},
-				{
-					color: themeVars.colors.palette.warning.default,
-					children: (
-						<div className="flex flex-col">
-							<Typography.Text strong>New order placed #XF-2356</Typography.Text>
-							<Typography.Text type="secondary" className="text-xs">
-								05 Oct 2023 4:19 PM
-							</Typography.Text>
-						</div>
-					),
-				},
-				{
-					color: themeVars.colors.palette.error.default,
-					children: (
-						<div className="flex flex-col">
-							<Typography.Text strong>New order placed #XF-2346</Typography.Text>
-							<Typography.Text type="secondary" className="text-xs">
-								04 Oct 2023 3:19 PM
-							</Typography.Text>
-						</div>
-					),
-				},
-			]}
-		/>
-	);
-}

+ 0 - 48
src/pages/dashboard/analysis/analysis-tasks.tsx

@@ -1,48 +0,0 @@
-import { faker } from "@faker-js/faker";
-import { Checkbox, List } from "antd";
-
-import { IconButton, Iconify } from "@/components/icon";
-
-export default function AnalysisTasks() {
-	const data = [
-		{
-			task: faker.lorem.words(),
-			checked: false,
-		},
-		{
-			task: faker.lorem.words(),
-			checked: true,
-		},
-		{
-			task: faker.lorem.words(),
-			checked: false,
-		},
-		{
-			task: faker.lorem.words(),
-			checked: false,
-		},
-	];
-	return (
-		<List
-			size="small"
-			dataSource={data}
-			renderItem={(item) => (
-				<List.Item
-					actions={[
-						<IconButton key={item.task}>
-							<Iconify icon="fontisto:more-v-a" />
-						</IconButton>,
-					]}
-				>
-					<Checkbox
-						onChange={(e) => {
-							item.checked = e.target.checked;
-						}}
-					>
-						{item.task}
-					</Checkbox>
-				</List.Item>
-			)}
-		/>
-	);
-}

+ 0 - 23
src/pages/dashboard/analysis/analysis-traffic-card.tsx

@@ -1,23 +0,0 @@
-import { themeVars } from "@/theme/theme.css";
-import type { ReactNode } from "react";
-
-type Props = {
-	icon: ReactNode;
-	title: string;
-	subtitle: string;
-};
-
-export default function AnalysisTrafficCard({ icon, title, subtitle }: Props) {
-	return (
-		<div
-			className="flex flex-col items-center rounded py-5"
-			style={{
-				border: `1px solid rgba(${themeVars.colors.palette.gray["500Channel"]}, .2)`,
-			}}
-		>
-			<div>{icon}</div>
-			<span className="text-2xl font-bold">{title}</span>
-			<span className="text-sm text-text-secondary">{subtitle}</span>
-		</div>
-	);
-}

+ 0 - 157
src/pages/dashboard/analysis/index.tsx

@@ -1,157 +0,0 @@
-import glass_bag from "@/assets/images/glass/ic_glass_bag.png";
-import glass_buy from "@/assets/images/glass/ic_glass_buy.png";
-import glass_message from "@/assets/images/glass/ic_glass_message.png";
-import glass_users from "@/assets/images/glass/ic_glass_users.png";
-import { Iconify } from "@/components/icon";
-import ChartBar from "@/pages/components/chart/view/chart-bar";
-import ChartMixed from "@/pages/components/chart/view/chart-mixed";
-import ChartPie from "@/pages/components/chart/view/chart-pie";
-import ChartRadar from "@/pages/components/chart/view/chart-radar";
-import { themeVars } from "@/theme/theme.css";
-import { Card, Col, Row, Typography } from "antd";
-import AnalysisCard from "./analysis-card";
-import AnalysisNews from "./analysis-news";
-import AnalysisOrderTimeline from "./analysis-order-timeline";
-import AnalysisTasks from "./analysis-tasks";
-import AnalysisTrafficCard from "./analysis-traffic-card";
-
-function Analysis() {
-	return (
-		<div className="p-2">
-			<Typography.Title level={2}>Hi, Welcome back 👋</Typography.Title>
-			<Row gutter={[16, 16]} justify="center">
-				<Col lg={6} md={12} span={24}>
-					<AnalysisCard
-						cover={glass_bag}
-						title="714k"
-						subtitle="Weekly Sales"
-						style={{
-							color: themeVars.colors.palette.success.dark,
-							backgroundColor: `rgba(${themeVars.colors.palette.success.defaultChannel}, .2)`,
-						}}
-					/>
-				</Col>
-				<Col lg={6} md={12} span={24}>
-					<AnalysisCard
-						cover={glass_users}
-						title="1.35m"
-						subtitle="New Users"
-						style={{
-							color: themeVars.colors.palette.info.dark,
-							backgroundColor: `rgba(${themeVars.colors.palette.info.defaultChannel}, .2)`,
-						}}
-					/>
-				</Col>
-				<Col lg={6} md={12} span={24}>
-					<AnalysisCard
-						cover={glass_buy}
-						title="1.72m"
-						subtitle="New Orders"
-						style={{
-							color: themeVars.colors.palette.warning.dark,
-							backgroundColor: `rgba(${themeVars.colors.palette.warning.defaultChannel}, .2)`,
-						}}
-					/>
-				</Col>
-				<Col lg={6} md={12} span={24}>
-					<AnalysisCard
-						cover={glass_message}
-						title="234"
-						subtitle="Bug Reports"
-						style={{
-							color: themeVars.colors.palette.error.dark,
-							backgroundColor: `rgba(${themeVars.colors.palette.error.defaultChannel}, .2)`,
-						}}
-					/>
-				</Col>
-			</Row>
-
-			<Row gutter={[16, 16]} className="mt-8" justify="center">
-				<Col span={24} lg={12} xl={16}>
-					<Card title="Website Visits">
-						<ChartMixed />
-					</Card>
-				</Col>
-				<Col span={24} lg={12} xl={8}>
-					<Card title="Current Visits">
-						<ChartPie />
-					</Card>
-				</Col>
-			</Row>
-
-			<Row gutter={[16, 16]} className="mt-8" justify="center">
-				<Col span={24} lg={12} xl={16}>
-					<Card title="Conversion Rates">
-						<ChartBar />
-					</Card>
-				</Col>
-				<Col span={24} lg={12} xl={8}>
-					<Card title="Current Subject">
-						<ChartRadar />
-					</Card>
-				</Col>
-			</Row>
-
-			<Row gutter={[16, 16]} className="mt-8">
-				<Col span={24} lg={12} xl={16}>
-					<Card title="News">
-						<AnalysisNews />
-					</Card>
-				</Col>
-				<Col span={24} lg={12} xl={8}>
-					<Card title="Order Timeline">
-						<AnalysisOrderTimeline />
-					</Card>
-				</Col>
-			</Row>
-
-			<Row gutter={[16, 16]} className="my-8">
-				<Col span={24} lg={12} xl={8}>
-					<Card title="Traffic by Site">
-						<Row gutter={[16, 16]}>
-							<Col span={12}>
-								<AnalysisTrafficCard
-									icon={<Iconify icon="bxl:facebook" size={32} color="#1877f2" />}
-									title="1.95k"
-									subtitle="FaceBook"
-								/>
-							</Col>
-
-							<Col span={12}>
-								<AnalysisTrafficCard
-									icon={<Iconify icon="ant-design:google-outlined" size={32} color="#df3e30" />}
-									title="9.12k"
-									subtitle="Google"
-								/>
-							</Col>
-
-							<Col span={12}>
-								<AnalysisTrafficCard
-									icon={<Iconify icon="eva:linkedin-fill" size={32} color="#006097" />}
-									title="6.98k"
-									subtitle="Linkedin"
-								/>
-							</Col>
-
-							<Col span={12}>
-								<AnalysisTrafficCard
-									icon={<Iconify icon="eva:twitter-fill" size={32} color="#1c9cea" />}
-									title="8.49k"
-									subtitle="Twitter"
-								/>
-							</Col>
-						</Row>
-					</Card>
-				</Col>
-
-				<Col span={24} lg={12} xl={16}>
-					<Card title="Tasks">
-						<AnalysisTasks />
-					</Card>
-				</Col>
-			</Row>
-		</div>
-	);
-}
-
-export default Analysis;

+ 67 - 0
src/pages/dashboard/components/area-download.tsx

@@ -0,0 +1,67 @@
+import { Select, Typography } from 'antd';
+import { useState } from 'react';
+
+import Card from '@/components/card';
+import Chart from '@/components/chart/chart';
+import useChart from '@/components/chart/useChart';
+
+export default function AreaDownload() {
+	const [year, setYear] = useState('2023');
+	const series: Record<string, ApexAxisChartSeries> = {
+		'2022': [
+			{ name: 'China', data: [10, 41, 35, 51, 49, 61, 69, 91, 148, 35, 51] },
+			{ name: 'America', data: [10, 34, 13, 56, 77, 88, 99, 45, 13, 56, 77] }
+		],
+
+		'2023': [
+			{ name: 'China', data: [51, 35, 41, 10, 91, 69, 62, 148, 91, 35, 51] },
+			{ name: 'America', data: [56, 13, 34, 10, 77, 99, 88, 45, 13, 56, 77] }
+		]
+	};
+	return (
+		<Card className="flex-col">
+			<header className="flex w-full justify-between self-start">
+				<Typography.Title level={5}>Area Installed</Typography.Title>
+				<Select
+					size="small"
+					defaultValue={year}
+					onChange={(value) => setYear(value)}
+					options={[
+						{ value: 2023, label: '2023' },
+						{ value: 2022, label: '2022' }
+					]}
+				/>
+			</header>
+			<main className="w-full">
+				<ChartArea series={series[year]} />
+			</main>
+		</Card>
+	);
+}
+
+function ChartArea({ series }: { series: ApexAxisChartSeries }) {
+	const chartOptions = useChart({
+		xaxis: {
+			type: 'category',
+			categories: [
+				'Jan',
+				'Feb',
+				'Mar',
+				'Apr',
+				'May',
+				'Jun',
+				'Jut',
+				'Aug',
+				'Sep',
+				'Oct',
+				'Nov',
+				'Dec'
+			]
+		},
+		tooltip: {}
+	});
+
+	return (
+		<Chart type="area" series={series} options={chartOptions} height={300} />
+	);
+}

+ 6 - 6
src/pages/dashboard/workbench/banner-card.tsx → src/pages/dashboard/components/banner-card.tsx

@@ -1,8 +1,8 @@
-import Character3 from "@/assets/images/characters/character_3.png";
-import { Iconify } from "@/components/icon";
-import { useUserInfo } from "@/store/userStore";
-import { themeVars } from "@/theme/theme.css";
-import { Col, Row } from "antd";
+import Character3 from '@/assets/images/characters/character_3.png';
+import { Iconify } from '@/components/icon';
+import { useUserInfo } from '@/store/userStore';
+import { themeVars } from '@/theme/theme.css';
+import { Col, Row } from 'antd';
 
 export default function BannerCard() {
 	const { username } = useUserInfo();
@@ -37,7 +37,7 @@ export default function BannerCard() {
 					type="button"
 					className="font-mediumtext-black m-auto flex items-center justify-center rounded-lg px-2 py-1 shadow-none md:m-0"
 					style={{ backgroundColor: themeVars.colors.palette.primary.default, color: themeVars.colors.common.white }}
-					onClick={() => window.open("https://discord.gg/fXemAXVNDa")}
+					onClick={() => window.open('https://discord.gg/fXemAXVNDa')}
 				>
 					<Iconify icon="carbon:logo-discord" size={24} />
 					<span className="ml-2 font-black">Join Discord</span>

+ 0 - 0
src/pages/dashboard/workbench/carousel-card.tsx → src/pages/dashboard/components/carousel-card.tsx


+ 3 - 3
src/pages/dashboard/workbench/conversion_applications.tsx → src/pages/dashboard/components/conversion_applications.tsx

@@ -1,6 +1,6 @@
-import { Iconify } from "@/components/icon";
-import { themeVars } from "@/theme/theme.css";
-import { Progress } from "antd";
+import { Iconify } from '@/components/icon';
+import { themeVars } from '@/theme/theme.css';
+import { Progress } from 'antd';
 
 export function Conversion() {
 	return (

+ 19 - 19
src/pages/dashboard/workbench/current-download.tsx → src/pages/dashboard/components/current-download.tsx

@@ -1,8 +1,8 @@
-import { Typography } from "antd";
+import { Typography } from 'antd';
 
-import Card from "@/components/card";
-import Chart from "@/components/chart/chart";
-import useChart from "@/components/chart/useChart";
+import Card from '@/components/card';
+import Chart from '@/components/chart/chart';
+import useChart from '@/components/chart/useChart';
 
 export default function CurrentDownload() {
 	return (
@@ -20,36 +20,36 @@ export default function CurrentDownload() {
 const series = [44, 55, 13, 43];
 function ChartDonut() {
 	const chartOptions = useChart({
-		labels: ["Mac", "Window", "IOS", "Android"],
+		labels: ['Mac', 'Window', 'IOS', 'Android'],
 		stroke: {
-			show: false,
+			show: false
 		},
 		legend: {
-			position: "bottom",
-			horizontalAlign: "center",
+			position: 'bottom',
+			horizontalAlign: 'center'
 		},
 		tooltip: {
-			fillSeriesColor: false,
+			fillSeriesColor: false
 		},
 		chart: {
-			width: 240,
+			width: 240
 		},
 		plotOptions: {
 			pie: {
 				donut: {
-					size: "90%",
+					size: '90%',
 					labels: {
 						total: {
-							fontSize: "12px",
+							fontSize: '12px'
 						},
 						value: {
-							fontSize: "18px",
-							fontWeight: 700,
-						},
-					},
-				},
-			},
-		},
+							fontSize: '18px',
+							fontWeight: 700
+						}
+					}
+				}
+			}
+		}
 	});
 
 	return (

+ 110 - 0
src/pages/dashboard/components/new-invoice.tsx

@@ -0,0 +1,110 @@
+import { Space, Tag, Typography } from 'antd';
+import Table, { type ColumnsType } from 'antd/es/table';
+
+import Card from '@/components/card';
+import { IconButton, Iconify } from '@/components/icon';
+import Scrollbar from '@/components/scrollbar';
+
+interface DataType {
+	key: string;
+	id: string;
+	category: string;
+	price: string;
+	status: string;
+}
+
+export default function NewInvoice() {
+	const columns: ColumnsType<DataType> = [
+		{
+			title: 'InvoiceId',
+			dataIndex: 'id',
+			key: 'id',
+			render: (text) => <span>{text}</span>
+		},
+		{
+			title: 'Category',
+			dataIndex: 'category',
+			key: 'category'
+		},
+		{
+			title: 'Price',
+			dataIndex: 'price',
+			key: 'price',
+			render: (text) => <span>{text}</span>
+		},
+		{
+			title: 'Status',
+			key: 'status',
+			dataIndex: 'status',
+			render: (_status) => {
+				const status = _status as string;
+				let color = 'success';
+				if (status === 'Progress') color = 'gold';
+				if (status === 'Out of Date') color = 'red';
+				return <Tag color={color}>{status}</Tag>;
+			}
+		},
+		{
+			title: 'Action',
+			key: 'action',
+			render: () => (
+				<Space size="middle">
+					<IconButton>
+						<Iconify icon="fontisto:more-v-a" />
+					</IconButton>
+				</Space>
+			)
+		}
+	];
+
+	const data: DataType[] = [
+		{
+			key: '1',
+			id: 'INV-1990',
+			category: 'Android',
+			price: '$83.74',
+			status: 'Paid'
+		},
+		{
+			key: '2',
+			id: 'INV-1991',
+			category: 'Mac',
+			price: '$97.14',
+			status: 'Out of Date'
+		},
+		{
+			key: '3',
+			id: 'INV-1992',
+			category: 'Windows',
+			price: '$68.71',
+			status: 'Progress'
+		},
+		{
+			key: '4',
+			id: 'INV-1993',
+			category: 'Android',
+			price: '$85.21',
+			status: 'Paid'
+		},
+		{
+			key: '5',
+			id: 'INV-1994',
+			category: 'Mac',
+			price: '$53.17',
+			status: 'Paid'
+		}
+	];
+
+	return (
+		<Card className="flex-col">
+			<header className="self-start">
+				<Typography.Title level={5}>New Invoice</Typography.Title>
+			</header>
+			<main className="w-full">
+				<Scrollbar>
+					<Table columns={columns} dataSource={data} />
+				</Scrollbar>
+			</main>
+		</Card>
+	);
+}

+ 10 - 10
src/pages/dashboard/workbench/top-authors.tsx → src/pages/dashboard/components/top-authors.tsx

@@ -1,8 +1,8 @@
-import Card from "@/components/card";
-import { Iconify } from "@/components/icon";
-import { themeVars } from "@/theme/theme.css";
-import { faker } from "@faker-js/faker";
-import { Typography } from "antd";
+import Card from '@/components/card';
+import { Iconify } from '@/components/icon';
+import { themeVars } from '@/theme/theme.css';
+import { faker } from '@faker-js/faker';
+import { Typography } from 'antd';
 
 export default function TopAuthor() {
 	const getTrophyIconColor = (index: number) => {
@@ -10,18 +10,18 @@ export default function TopAuthor() {
 			case 1:
 				return {
 					color: themeVars.colors.palette.info.default,
-					bg: `rgba(${themeVars.colors.palette.info.defaultChannel}, .4)`,
+					bg: `rgba(${themeVars.colors.palette.info.defaultChannel}, .4)`
 				};
 			case 2: {
 				return {
 					color: themeVars.colors.palette.error.default,
-					bg: `rgba(${themeVars.colors.palette.error.defaultChannel}, .4)`,
+					bg: `rgba(${themeVars.colors.palette.error.defaultChannel}, .4)`
 				};
 			}
 			default:
 				return {
 					color: themeVars.colors.palette.success.default,
-					bg: `rgba(${themeVars.colors.palette.success.defaultChannel}, .4)`,
+					bg: `rgba(${themeVars.colors.palette.success.defaultChannel}, .4)`
 				};
 		}
 	};
@@ -31,7 +31,7 @@ export default function TopAuthor() {
 				<Typography.Title level={5}>Top Authors</Typography.Title>
 			</header>
 			<main className="w-full">
-				{new Array(3).fill("").map((_, index) => (
+				{new Array(3).fill('').map((_, index) => (
 					// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
 					<div key={index} className="mb-4 flex">
 						<img src={faker.image.avatarGitHub()} alt="" className="h-10 w-10 rounded-full" />
@@ -46,7 +46,7 @@ export default function TopAuthor() {
 						<div
 							className="ml-auto flex h-10 w-10 items-center justify-center rounded-full"
 							style={{
-								background: getTrophyIconColor(index).bg,
+								background: getTrophyIconColor(index).bg
 							}}
 						>
 							<Iconify icon="solar:cup-star-bold" size={24} color={getTrophyIconColor(index).color} />

+ 35 - 35
src/pages/dashboard/workbench/top-installed.tsx → src/pages/dashboard/components/top-installed.tsx

@@ -1,53 +1,53 @@
-import { Typography } from "antd";
-import type { ReactNode } from "react";
+import { Typography } from 'antd';
+import type { ReactNode } from 'react';
 
-import Card from "@/components/card";
-import { Iconify } from "@/components/icon";
+import Card from '@/components/card';
+import { Iconify } from '@/components/icon';
 
 const dataSource = [
 	{
-		country: "Germany",
-		iconify: "twemoji:flag-germany",
-		android: "9.91k",
-		windows: "1.95k",
-		ios: "1.95k",
+		country: 'Germany',
+		iconify: 'twemoji:flag-germany',
+		android: '9.91k',
+		windows: '1.95k',
+		ios: '1.95k'
 	},
 	{
-		country: "China",
-		iconify: "twemoji:flag-china",
-		android: "1.95k",
-		windows: "9.25k",
-		ios: "7.95k",
+		country: 'China',
+		iconify: 'twemoji:flag-china',
+		android: '1.95k',
+		windows: '9.25k',
+		ios: '7.95k'
 	},
 	{
-		country: "Australia",
-		iconify: "twemoji:flag-australia",
-		android: "3.91k",
-		windows: "2.95k",
-		ios: "4.95k",
+		country: 'Australia',
+		iconify: 'twemoji:flag-australia',
+		android: '3.91k',
+		windows: '2.95k',
+		ios: '4.95k'
 	},
 	{
-		country: "France",
-		iconify: "twemoji:flag-france",
-		android: "3.28k",
-		windows: "2.29k",
-		ios: "8.95k",
+		country: 'France',
+		iconify: 'twemoji:flag-france',
+		android: '3.28k',
+		windows: '2.29k',
+		ios: '8.95k'
 	},
 	{
-		country: "USA",
-		iconify: "twemoji:flag-united-states",
-		android: "8.81k",
-		windows: "7.05k",
-		ios: "4.35k",
-	},
+		country: 'USA',
+		iconify: 'twemoji:flag-united-states',
+		android: '8.81k',
+		windows: '7.05k',
+		ios: '4.35k'
+	}
 ];
 
 const platformIcon = (platform: string) => {
 	let iconify: ReactNode;
-	if (platform === "android") {
+	if (platform === 'android') {
 		iconify = <Iconify icon="uiw:android" />;
 	}
-	if (platform === "windows") {
+	if (platform === 'windows') {
 		iconify = <Iconify icon="mingcute:windows-fill" />;
 	}
 	iconify = <Iconify icon="wpf:mac-os" />;
@@ -67,17 +67,17 @@ export default function TopInstalled() {
 						<span className="mx-2 font-medium">{item.country}</span>
 						<div className="ml-auto flex">
 							<div className="flex items-center justify-center">
-								{platformIcon("android")}
+								{platformIcon('android')}
 								{item.android}
 							</div>
 
 							<div className="mx-2 flex items-center justify-center">
-								{platformIcon("windows")}
+								{platformIcon('windows')}
 								{item.windows}
 							</div>
 
 							<div className="flex items-center justify-center">
-								{platformIcon("ios")}
+								{platformIcon('ios')}
 								{item.ios}
 							</div>
 						</div>

+ 32 - 32
src/pages/dashboard/workbench/top-related.tsx → src/pages/dashboard/components/top-related.tsx

@@ -1,52 +1,52 @@
-import { Tag, Typography } from "antd";
+import { Tag, Typography } from 'antd';
 
-import Card from "@/components/card";
-import { Iconify } from "@/components/icon";
-import Scrollbar from "@/components/scrollbar";
-import { themeVars } from "@/theme/theme.css";
-import { Rate } from "antd";
+import Card from '@/components/card';
+import { Iconify } from '@/components/icon';
+import Scrollbar from '@/components/scrollbar';
+import { themeVars } from '@/theme/theme.css';
+import { Rate } from 'antd';
 
 const dataSource = [
 	{
 		logo: <Iconify icon="logos:chrome" size={24} />,
-		title: "Chrome",
-		platform: "Mac",
-		type: "free",
+		title: 'Chrome',
+		platform: 'Mac',
+		type: 'free',
 		star: 4,
-		reviews: "9.91k",
+		reviews: '9.91k'
 	},
 	{
 		logo: <Iconify icon="logos:google-drive" size={24} />,
-		title: "Drive",
-		platform: "Mac",
-		type: "free",
+		title: 'Drive',
+		platform: 'Mac',
+		type: 'free',
 		star: 3.5,
-		reviews: "1.95k",
+		reviews: '1.95k'
 	},
 	{
 		logo: <Iconify icon="logos:dropbox" size={24} />,
-		title: "Dropbox",
-		platform: "Windows",
-		type: "$66.71",
+		title: 'Dropbox',
+		platform: 'Windows',
+		type: '$66.71',
 		star: 4.5,
-		reviews: "9.12k",
+		reviews: '9.12k'
 	},
 	{
 		logo: <Iconify icon="logos:slack-icon" size={24} />,
-		title: "Slack",
-		platform: "Mac",
-		type: "free",
+		title: 'Slack',
+		platform: 'Mac',
+		type: 'free',
 		star: 3.5,
-		reviews: "6.98k",
+		reviews: '6.98k'
 	},
 	{
 		logo: <Iconify icon="logos:discord-icon" size={24} />,
-		title: "Discord",
-		platform: "Windows",
-		type: "$52.17",
+		title: 'Discord',
+		platform: 'Windows',
+		type: '$52.17',
 		star: 0.5,
-		reviews: "8.49k",
-	},
+		reviews: '8.49k'
+	}
 ];
 export default function TopRelated() {
 	return (
@@ -62,9 +62,9 @@ export default function TopRelated() {
 								className="mr-2 flex items-center justify-center"
 								style={{
 									background: `rgba(${themeVars.colors.background.defaultChannel}, .4)`,
-									borderRadius: "12px",
-									width: "48px",
-									height: "48px",
+									borderRadius: '12px',
+									width: '48px',
+									height: '48px'
 								}}
 							>
 								{item.logo}
@@ -73,13 +73,13 @@ export default function TopRelated() {
 							<div className="flex flex-col">
 								<span className="font-medium">{item.title}</span>
 								<div className="flex items-center justify-center text-gray">
-									{item.platform === "Mac" ? (
+									{item.platform === 'Mac' ? (
 										<Iconify icon="wpf:mac-os" size={12} />
 									) : (
 										<Iconify icon="mingcute:windows-fill" size={12} />
 									)}
 									<span className="mx-1 text-xs font-light">{item.platform}</span>
-									<Tag color={item.type === "free" ? "green" : "red"}>{item.type}</Tag>
+									<Tag color={item.type === 'free' ? 'green' : 'red'}>{item.type}</Tag>
 								</div>
 							</div>
 

+ 21 - 21
src/pages/dashboard/workbench/total-card.tsx → src/pages/dashboard/components/total-card.tsx

@@ -1,7 +1,7 @@
-import Card from "@/components/card";
-import Chart from "@/components/chart/chart";
-import useChart from "@/components/chart/useChart";
-import { SvgIcon } from "@/components/icon";
+import Card from '@/components/card';
+import Chart from '@/components/chart/chart';
+import useChart from '@/components/chart/useChart';
+import { SvgIcon } from '@/components/icon';
 
 type Props = {
 	title: string;
@@ -15,7 +15,7 @@ export default function TotalCard({
 	increase,
 	count,
 	percent,
-	chartData,
+	chartData
 }: Props) {
 	return (
 		<Card>
@@ -28,7 +28,7 @@ export default function TotalCard({
 						<SvgIcon icon="ic_decline" size={24} color="rgb(255, 86, 48)" />
 					)}
 					<div className="ml-2">
-						<span>{increase ? "+" : "-"}</span>
+						<span>{increase ? '+' : '-'}</span>
 						<span>{percent}</span>
 					</div>
 				</div>
@@ -43,42 +43,42 @@ export default function TotalCard({
 function ChartLine({ data }: { data: number[] }) {
 	const series = [
 		{
-			name: "",
-			data,
-		},
+			name: '',
+			data
+		}
 	];
 	const chartOptions = useChart({
 		tooltip: {
 			x: {
-				show: false,
-			},
+				show: false
+			}
 		},
 		xaxis: {
 			labels: {
 				show: false,
-				showDuplicates: false,
+				showDuplicates: false
 			},
 			tooltip: {
-				enabled: false,
+				enabled: false
 			},
 			crosshairs: {
-				show: false,
-			},
+				show: false
+			}
 		},
 		yaxis: {
 			labels: {
-				show: false,
+				show: false
 			},
 			tooltip: {
-				enabled: false,
+				enabled: false
 			},
 			crosshairs: {
-				show: false,
-			},
+				show: false
+			}
 		},
 		grid: {
-			show: false,
-		},
+			show: false
+		}
 	});
 
 	return (

+ 88 - 7
src/pages/dashboard/index.tsx

@@ -1,9 +1,90 @@
-import React, { memo } from 'react'
+import { Col, Row, Space } from 'antd'
+import AreaDownload from './components/area-download'
+import BannerCard from './components/banner-card'
+import { Applications, Conversion } from './components/conversion_applications'
+import CurrentDownload from './components/current-download'
+import NewInvoice from './components/new-invoice'
+import TopAuthor from './components/top-authors'
+import TopInstalled from './components/top-installed'
+import TopRelated from './components/top-related'
+import TotalCard from './components/total-card'
 
-const index = memo(() => {
-  console.log('render home')
-  debugger
-  return <div>主页origin</div>
-})
+function Workbench() {
+  return (
+    <div className='p-2'>
+      <Row gutter={[16, 16]} justify='center'>
+        <Col span={24} lg={16}>
+          <BannerCard />
+        </Col>
+        <Col span={24} lg={8}>
+          <Space direction='vertical' size='large' className='h-full w-full justify-center'>
+            <Conversion />
+            <Applications />
+          </Space>
+        </Col>
+      </Row>
 
-export default index
+      <Row gutter={[16, 16]} className='mt-4' justify='center'>
+        <Col span={24} md={8}>
+          <TotalCard
+            title='Total Active Users'
+            increase
+            count='18,765'
+            percent='2.6%'
+            chartData={[22, 8, 35, 50, 82, 84, 77, 12, 87, 43]}
+          />
+        </Col>
+
+        <Col span={24} md={8}>
+          <TotalCard
+            title='Total Installed'
+            increase
+            count='4,876'
+            percent='0.2%'
+            chartData={[45, 52, 38, 24, 33, 26, 21, 20, 6]}
+          />
+        </Col>
+
+        <Col span={24} md={8}>
+          <TotalCard
+            title='Total Downloads'
+            increase={false}
+            count='678'
+            percent='0.1%'
+            chartData={[35, 41, 62, 42, 13, 18, 29, 37, 36]}
+          />
+        </Col>
+      </Row>
+
+      <Row gutter={[16, 16]} className='mt-4' justify='center'>
+        <Col span={24} md={12} lg={8}>
+          <CurrentDownload />
+        </Col>
+        <Col span={24} md={12} lg={16}>
+          <AreaDownload />
+        </Col>
+      </Row>
+
+      <Row gutter={[16, 16]} className='mt-4' justify='center'>
+        <Col span={24} md={12} lg={16}>
+          <NewInvoice />
+        </Col>
+        <Col span={24} md={12} lg={8}>
+          <TopRelated />
+        </Col>
+      </Row>
+
+      <Row gutter={[16, 16]} className='mt-4' justify='center'>
+        <Col span={24} md={12}>
+          <TopInstalled />
+        </Col>
+
+        <Col span={24} md={12}>
+          <TopAuthor />
+        </Col>
+      </Row>
+    </div>
+  )
+}
+
+export default Workbench

+ 0 - 67
src/pages/dashboard/workbench/area-download.tsx

@@ -1,67 +0,0 @@
-import { Select, Typography } from "antd";
-import { useState } from "react";
-
-import Card from "@/components/card";
-import Chart from "@/components/chart/chart";
-import useChart from "@/components/chart/useChart";
-
-export default function AreaDownload() {
-	const [year, setYear] = useState("2023");
-	const series: Record<string, ApexAxisChartSeries> = {
-		"2022": [
-			{ name: "China", data: [10, 41, 35, 51, 49, 61, 69, 91, 148, 35, 51] },
-			{ name: "America", data: [10, 34, 13, 56, 77, 88, 99, 45, 13, 56, 77] },
-		],
-
-		"2023": [
-			{ name: "China", data: [51, 35, 41, 10, 91, 69, 62, 148, 91, 35, 51] },
-			{ name: "America", data: [56, 13, 34, 10, 77, 99, 88, 45, 13, 56, 77] },
-		],
-	};
-	return (
-		<Card className="flex-col">
-			<header className="flex w-full justify-between self-start">
-				<Typography.Title level={5}>Area Installed</Typography.Title>
-				<Select
-					size="small"
-					defaultValue={year}
-					onChange={(value) => setYear(value)}
-					options={[
-						{ value: 2023, label: "2023" },
-						{ value: 2022, label: "2022" },
-					]}
-				/>
-			</header>
-			<main className="w-full">
-				<ChartArea series={series[year]} />
-			</main>
-		</Card>
-	);
-}
-
-function ChartArea({ series }: { series: ApexAxisChartSeries }) {
-	const chartOptions = useChart({
-		xaxis: {
-			type: "category",
-			categories: [
-				"Jan",
-				"Feb",
-				"Mar",
-				"Apr",
-				"May",
-				"Jun",
-				"Jut",
-				"Aug",
-				"Sep",
-				"Oct",
-				"Nov",
-				"Dec",
-			],
-		},
-		tooltip: {},
-	});
-
-	return (
-		<Chart type="area" series={series} options={chartOptions} height={300} />
-	);
-}

+ 0 - 91
src/pages/dashboard/workbench/index.tsx

@@ -1,91 +0,0 @@
-import { Col, Row, Space } from "antd";
-
-import AreaDownload from "./area-download";
-import BannerCard from "./banner-card";
-import { Applications, Conversion } from "./conversion_applications";
-import CurrentDownload from "./current-download";
-import NewInvoice from "./new-invoice";
-import TopAuthor from "./top-authors";
-import TopInstalled from "./top-installed";
-import TopRelated from "./top-related";
-import TotalCard from "./total-card";
-
-function Workbench() {
-	return (
-		<div className="p-2">
-			<Row gutter={[16, 16]} justify="center">
-				<Col span={24} lg={16}>
-					<BannerCard />
-				</Col>
-				<Col span={24} lg={8}>
-					<Space direction="vertical" size="large" className="h-full w-full justify-center">
-						<Conversion />
-						<Applications />
-					</Space>
-				</Col>
-			</Row>
-
-			<Row gutter={[16, 16]} className="mt-4" justify="center">
-				<Col span={24} md={8}>
-					<TotalCard
-						title="Total Active Users"
-						increase
-						count="18,765"
-						percent="2.6%"
-						chartData={[22, 8, 35, 50, 82, 84, 77, 12, 87, 43]}
-					/>
-				</Col>
-
-				<Col span={24} md={8}>
-					<TotalCard
-						title="Total Installed"
-						increase
-						count="4,876"
-						percent="0.2%"
-						chartData={[45, 52, 38, 24, 33, 26, 21, 20, 6]}
-					/>
-				</Col>
-
-				<Col span={24} md={8}>
-					<TotalCard
-						title="Total Downloads"
-						increase={false}
-						count="678"
-						percent="0.1%"
-						chartData={[35, 41, 62, 42, 13, 18, 29, 37, 36]}
-					/>
-				</Col>
-			</Row>
-
-			<Row gutter={[16, 16]} className="mt-4" justify="center">
-				<Col span={24} md={12} lg={8}>
-					<CurrentDownload />
-				</Col>
-				<Col span={24} md={12} lg={16}>
-					<AreaDownload />
-				</Col>
-			</Row>
-
-			<Row gutter={[16, 16]} className="mt-4" justify="center">
-				<Col span={24} md={12} lg={16}>
-					<NewInvoice />
-				</Col>
-				<Col span={24} md={12} lg={8}>
-					<TopRelated />
-				</Col>
-			</Row>
-
-			<Row gutter={[16, 16]} className="mt-4" justify="center">
-				<Col span={24} md={12}>
-					<TopInstalled />
-				</Col>
-
-				<Col span={24} md={12}>
-					<TopAuthor />
-				</Col>
-			</Row>
-		</div>
-	);
-}
-
-export default Workbench;

+ 0 - 110
src/pages/dashboard/workbench/new-invoice.tsx

@@ -1,110 +0,0 @@
-import { Space, Tag, Typography } from "antd";
-import Table, { type ColumnsType } from "antd/es/table";
-
-import Card from "@/components/card";
-import { IconButton, Iconify } from "@/components/icon";
-import Scrollbar from "@/components/scrollbar";
-
-interface DataType {
-	key: string;
-	id: string;
-	category: string;
-	price: string;
-	status: string;
-}
-
-export default function NewInvoice() {
-	const columns: ColumnsType<DataType> = [
-		{
-			title: "InvoiceId",
-			dataIndex: "id",
-			key: "id",
-			render: (text) => <span>{text}</span>,
-		},
-		{
-			title: "Category",
-			dataIndex: "category",
-			key: "category",
-		},
-		{
-			title: "Price",
-			dataIndex: "price",
-			key: "price",
-			render: (text) => <span>{text}</span>,
-		},
-		{
-			title: "Status",
-			key: "status",
-			dataIndex: "status",
-			render: (_status) => {
-				const status = _status as string;
-				let color = "success";
-				if (status === "Progress") color = "gold";
-				if (status === "Out of Date") color = "red";
-				return <Tag color={color}>{status}</Tag>;
-			},
-		},
-		{
-			title: "Action",
-			key: "action",
-			render: () => (
-				<Space size="middle">
-					<IconButton>
-						<Iconify icon="fontisto:more-v-a" />
-					</IconButton>
-				</Space>
-			),
-		},
-	];
-
-	const data: DataType[] = [
-		{
-			key: "1",
-			id: "INV-1990",
-			category: "Android",
-			price: "$83.74",
-			status: "Paid",
-		},
-		{
-			key: "2",
-			id: "INV-1991",
-			category: "Mac",
-			price: "$97.14",
-			status: "Out of Date",
-		},
-		{
-			key: "3",
-			id: "INV-1992",
-			category: "Windows",
-			price: "$68.71",
-			status: "Progress",
-		},
-		{
-			key: "4",
-			id: "INV-1993",
-			category: "Android",
-			price: "$85.21",
-			status: "Paid",
-		},
-		{
-			key: "5",
-			id: "INV-1994",
-			category: "Mac",
-			price: "$53.17",
-			status: "Paid",
-		},
-	];
-
-	return (
-		<Card className="flex-col">
-			<header className="self-start">
-				<Typography.Title level={5}>New Invoice</Typography.Title>
-			</header>
-			<main className="w-full">
-				<Scrollbar>
-					<Table columns={columns} dataSource={data} />
-				</Scrollbar>
-			</main>
-		</Card>
-	);
-}

+ 32 - 64
src/router/hooks/use-permission-routes.tsx

@@ -2,54 +2,17 @@ import { lazy, Suspense, useMemo } from 'react'
 import { Navigate, Outlet } from 'react-router'
 import { useUserMenu } from '@/store/userStore'
 import type { AppRouteObject } from '#/router'
-import type { Permission } from '#/entity'
-import { BasicStatus, MenuType, PermissionType } from '#/enum'
+import { PermissionType } from '#/enum'
 import { isEmpty } from 'ramda'
 import { clone } from 'ramda'
-import { flattenTrees } from '@/utils/tree'
 import { Tag } from 'antd'
 import { Iconify } from '@/components/icon'
 import { CircleLoading } from '@/components/loading'
-import { getRoutesFromModules } from '../utils'
 
 const ENTRY_PATH = '/src/pages'
 const PAGES = import.meta.glob('/src/pages/**/*.tsx')
-// console.log(PAGES)
 export const loadComponentFromPath = (path: string) => PAGES[`${ENTRY_PATH}${path}.tsx`]
 
-/**
- * Build complete route path by traversing from current permission to root
- * @param {Permission} permission - current permission
- * @param {Permission[]} flattenedPermissions - flattened permission array
- * @param {string[]} segments - route segments accumulator
- * @returns {string} normalized complete route path
- */
-function buildCompleteRoute(
-  permission: Permission,
-  flattenedPermissions: Permission[],
-  segments: string[] = []
-): string {
-  // debugger
-  // Add current route segment
-  segments.unshift(permission.path)
-
-  // Base case: reached root permission
-  if (permission.parentId === '-1') {
-    // debugger
-    return segments.join('')
-  }
-
-  // Find parent and continue recursion
-  const parent = flattenedPermissions.find((p) => p.id === permission.parentId)
-  if (!parent) {
-    debugger
-    console.warn(`Parent permission not found for ID: ${permission.parentId}`)
-    return `/${segments.join('/')}`
-  }
-
-  return buildCompleteRoute(parent, flattenedPermissions, segments)
-}
-
 // Components
 function NewFeatureTag() {
   return (
@@ -87,15 +50,8 @@ const createBaseRoute = (menuItem: API.MenuChildrenItem, completeRoute: string):
   return baseRoute
 }
 
-const createCatalogueRoute = (
-  menuItem: API.MenuChildrenItem,
-  flattenedPermissions: API.MenuChildrenItem[]
-): AppRouteObject => {
-  // 1.创建基础路由
-  // console.log(buildCompleteRoute(permission, flattenedPermissions))
-  // console.log('0000', menuItem.path)
+const createCatalogueRoute = (menuItem: API.MenuChildrenItem): AppRouteObject => {
   const baseRoute = createBaseRoute(menuItem, menuItem.path)
-  // console.log(baseRoute)
   if (baseRoute.meta) {
     baseRoute.meta.hideTab = true
   }
@@ -109,7 +65,7 @@ const createCatalogueRoute = (
     )
   }
 
-  baseRoute.children = transformMenuToRoutes(children, flattenedPermissions)
+  baseRoute.children = transformMenuToRoutes(children)
 
   if (!isEmpty(children)) {
     baseRoute.children.unshift({
@@ -121,11 +77,7 @@ const createCatalogueRoute = (
   return baseRoute
 }
 
-const createMenuRoute = (
-  menuItem: API.MenuChildrenItem,
-  flattenedPermissions: API.MenuChildrenItem[]
-): AppRouteObject => {
-  // console.log('1111', menuItem.path)
+const createMenuRoute = (menuItem: API.MenuChildrenItem): AppRouteObject => {
   const baseRoute = createBaseRoute(menuItem, menuItem.path)
   if (menuItem.path) {
     const Element = lazy(loadComponentFromPath(menuItem.path) as any)
@@ -144,17 +96,14 @@ const createMenuRoute = (
   return baseRoute
 }
 
-function transformMenuToRoutes(
-  menu: API.MenuChildrenItem[],
-  flattenedPermissions: API.MenuChildrenItem[]
-): AppRouteObject[] {
+function transformMenuToRoutes(menu: API.MenuChildrenItem[]): AppRouteObject[] {
   return menu.map((menuItem) => {
     // 创建目录路由
     if (menuItem.type === PermissionType.CATALOGUE) {
-      return createCatalogueRoute(menuItem, flattenedPermissions)
+      return createCatalogueRoute(menuItem)
     }
     // 创建菜单路由
-    return createMenuRoute(menuItem, flattenedPermissions)
+    return createMenuRoute(menuItem)
   })
 }
 
@@ -168,21 +117,40 @@ const addTypeToMenu = (menu: API.MenuResult) => {
     }
   })
 }
+
+// 导出一个函数usePermissionRoutes
 export function usePermissionRoutes() {
   const menu = useUserMenu()
 
   return useMemo(() => {
     if (!menu) return []
 
+    // 克隆menu
     const menuCopy = clone(menu)
     // 给menu的每一级添加type, type为0, 即为目录, type为1, 即为菜单, 只有最后一级才是菜单
     addTypeToMenu(menuCopy)
     console.log('添加type后的菜单', menuCopy)
-
-    const flattenedMenu = flattenTrees(menuCopy) // 拍平成功
-
-    console.log('~~~~~~', transformMenuToRoutes(menuCopy, flattenedMenu))
-    // 将菜单转换成路由
-    return transformMenuToRoutes(menuCopy, flattenedMenu)
+    const constantMenu: API.MenuResult = [
+      {
+        id: 'dashboard',
+        parentId: '-1',
+        weight: 0,
+        name: '首页',
+        path: '/dashboard/index',
+        visible: true,
+        isKeepAlive: false,
+        sortOrder: 1,
+        icon: 'fengxiankongzhi1',
+        menuType: 'MENU',
+        label: '',
+        type: 1,
+        title: '首页',
+        isHide: false
+      }
+    ]
+    menuCopy.unshift(...constantMenu)
+
+    console.log('动态路由:', transformMenuToRoutes(menuCopy))
+    return transformMenuToRoutes(menuCopy)
   }, [menu])
 }

+ 3 - 18
src/router/index.tsx

@@ -1,4 +1,3 @@
-import { lazy, Suspense } from 'react'
 import { ErrorBoundary } from 'react-error-boundary'
 import { createHashRouter, Navigate, type RouteObject } from 'react-router'
 import { RouterProvider } from 'react-router/dom'
@@ -6,15 +5,13 @@ import type { AppRouteObject } from '#/router'
 import ProtectedRoute from '@/router/components/protected-route'
 import { usePermissionRoutes } from '@/router/hooks'
 import { ERROR_ROUTE } from '@/router/routes/error-routes'
+// import HOME_ROUTE from '@/router/routes/modules/constantRoutes'
 import DashboardLayout from '@/layouts/dashboard'
-import { loadComponentFromPath } from './hooks/use-permission-routes'
 import PageError from '@/pages/sys/error/PageError'
 import Login from '@/pages/sys/login/Login'
-import { CircleLoading } from '@/components/loading'
 
 const { VITE_APP_HOMEPAGE: HOMEPAGE } = import.meta.env
 
-// 公共路由
 const PUBLIC_ROUTE: AppRouteObject = {
   path: '/login',
   element: (
@@ -32,20 +29,7 @@ const NO_MATCHED_ROUTE: AppRouteObject = {
 export default function Router() {
   console.log('Router render')
   const permissionRoutes = usePermissionRoutes()
-  console.log(permissionRoutes)
-  // console.log(HOMEPAGE)
 
-  const HOMEPAGEElement = lazy(loadComponentFromPath('/dashboard/index') as any)
-  const HOMEPAGE_ROUTE = {
-    path: '/dashboard/index',
-    element: (
-      <Suspense fallback={<CircleLoading />}>
-        <HOMEPAGEElement />
-      </Suspense>
-    ),
-    order: 0,
-    meta: {}
-  } as AppRouteObject
   // 动态路由
   const PROTECTED_ROUTE: AppRouteObject = {
     path: '/',
@@ -55,7 +39,8 @@ export default function Router() {
       </ProtectedRoute>
     ),
     children: [
-      { index: true, element: <Navigate to={'/system//auth/user/index'} replace /> },
+      { index: true, element: <Navigate to={HOMEPAGE} replace /> },
+      // HOME_ROUTE,
       ...permissionRoutes
     ]
   }

+ 35 - 0
src/router/routes/modules/constantRoutes.tsx

@@ -0,0 +1,35 @@
+import { lazy, Suspense } from 'react'
+import { Navigate, Outlet } from 'react-router'
+import type { AppRouteObject } from '#/router'
+import { SvgIcon } from '@/components/icon'
+import { CircleLoading } from '@/components/loading'
+
+const HomePage = lazy(() => import('@/pages/dashboard/index'))
+
+const dashboard: AppRouteObject = {
+  order: 1,
+  path: '/dashboard',
+  element: (
+    <Suspense fallback={<CircleLoading />}>
+      <Outlet />
+    </Suspense>
+  ),
+  meta: {
+    label: 'sys.menu.dashboard',
+    icon: <SvgIcon icon='ic-analysis' className='ant-menu-item-icon' size='24' />,
+    key: '/dashboard'
+  },
+  children: [
+    {
+      index: true,
+      element: <Navigate to='/dashboard/index' replace />
+    },
+    {
+      path: '/dashboard/index',
+      element: <HomePage />,
+      meta: { label: 'sys.menu.workbench', key: '/dashboard/index' }
+    }
+  ]
+}
+
+export default dashboard

+ 37 - 41
src/router/routes/modules/dashboard.tsx

@@ -1,45 +1,41 @@
-import { Suspense, lazy } from 'react';
-import { Navigate, Outlet } from 'react-router';
+import { lazy, Suspense } from 'react'
+import { Navigate, Outlet } from 'react-router'
+import type { AppRouteObject } from '#/router'
+import { SvgIcon } from '@/components/icon'
+import { CircleLoading } from '@/components/loading'
 
-import { SvgIcon } from '@/components/icon';
-import { CircleLoading } from '@/components/loading';
-
-import type { AppRouteObject } from '#/router';
-
-const HomePage = lazy(() => import('@/pages/dashboard/workbench'));
-const Analysis = lazy(() => import('@/pages/dashboard/analysis'));
+const HomePage = lazy(() => import('@/pages/dashboard'))
+const Analysis = lazy(() => import('@/pages/dashboard/analysis'))
 
 const dashboard: AppRouteObject = {
-	order: 1,
-	path: 'dashboard',
-	element: (
-		<Suspense fallback={<CircleLoading />}>
-			<Outlet />
-		</Suspense>
-	),
-	meta: {
-		label: 'sys.menu.dashboard',
-		icon: (
-			<SvgIcon icon="ic-analysis" className="ant-menu-item-icon" size="24" />
-		),
-		key: '/dashboard'
-	},
-	children: [
-		{
-			index: true,
-			element: <Navigate to="workbench" replace />
-		},
-		{
-			path: 'workbench',
-			element: <HomePage />,
-			meta: { label: 'sys.menu.workbench', key: '/dashboard/workbench' }
-		},
-		{
-			path: 'analysis',
-			element: <Analysis />,
-			meta: { label: 'sys.menu.analysis', key: '/dashboard/analysis' }
-		}
-	]
-};
+  order: 1,
+  path: 'dashboard',
+  element: (
+    <Suspense fallback={<CircleLoading />}>
+      <Outlet />
+    </Suspense>
+  ),
+  meta: {
+    label: 'sys.menu.dashboard',
+    icon: <SvgIcon icon='ic-analysis' className='ant-menu-item-icon' size='24' />,
+    key: '/dashboard'
+  },
+  children: [
+    {
+      index: true,
+      element: <Navigate to='workbench' replace />
+    },
+    {
+      path: 'workbench',
+      element: <HomePage />,
+      meta: { label: 'sys.menu.workbench', key: '/dashboard/workbench' }
+    },
+    {
+      path: 'analysis',
+      element: <Analysis />,
+      meta: { label: 'sys.menu.analysis', key: '/dashboard/analysis' }
+    }
+  ]
+}
 
-export default dashboard;
+export default dashboard

+ 1 - 1
src/store/userStore.ts

@@ -223,7 +223,7 @@ export const useLoginByUsername = () => {
             // await loadRoutes(router)
             await getMenuFn()
             // 跳转到首页
-            navigate('/system/auth/user/index')
+            navigate('/dashboard/index')
             resolve()
           } else {
             reject('获取用户信息失败')

+ 3 - 4
src/vite-env.d.ts

@@ -1,11 +1,10 @@
 /// <reference types="vite/client" />
 
 interface ImportMetaEnv {
-  readonly VITE_APP_ROUTER_MODE: 'permission' | 'module'
-  readonly VITE_APP_BASE_API: string
-  readonly VITE_APP_HOMEPAGE: string
-  readonly VITE_APP_BASE_PATH: string
   readonly VITE_APP_ENV: 'dev' | 'production'
+  readonly VITE_APP_URL: string
+  readonly VITE_APP_TITLE: string
+  readonly VITE_APP_HOMEPAGE: string
 }
 
 interface ImportMeta {

+ 3 - 3
vite.config.ts

@@ -8,12 +8,12 @@ import path from 'node:path'
 import { visualizer } from 'rollup-plugin-visualizer'
 
 export default defineConfig(({ mode }) => {
-  const env = loadEnv(mode, process.cwd(), '')
-  const base = env.VITE_APP_BASE_PATH || '/'
+  // const env = loadEnv(mode, process.cwd(), '')
+  // const base = env.VITE_APP_BASE_PATH || '/'
   const isProduction = mode === 'production'
 
   return {
-    base,
+    base: '/',
     plugins: [
       react({
         // 添加 React 插件的优化配置