create-secra 0.1.11 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/antd-adapter-template/README.md +24 -0
  2. package/antd-adapter-template/index.html +16 -0
  3. package/antd-adapter-template/package-lock.json +3420 -0
  4. package/antd-adapter-template/package.json +20 -47
  5. package/antd-adapter-template/src/app/App.tsx +7 -0
  6. package/antd-adapter-template/src/app/layouts/AppLayout.tsx +163 -0
  7. package/antd-adapter-template/src/app/router.ts +26 -0
  8. package/antd-adapter-template/src/app/routes/RouteTitleSync.tsx +26 -0
  9. package/antd-adapter-template/src/app/routes/dynamic-routes.ts +52 -0
  10. package/antd-adapter-template/src/app/routes/route-modules.ts +4 -0
  11. package/antd-adapter-template/src/app/routes/static-routes.tsx +110 -0
  12. package/antd-adapter-template/src/app/routes/types.ts +9 -0
  13. package/antd-adapter-template/src/features/auth/api/auth-api.ts +61 -0
  14. package/antd-adapter-template/src/features/auth/auth-store.ts +125 -0
  15. package/antd-adapter-template/src/features/auth/auth-types.ts +29 -0
  16. package/antd-adapter-template/src/features/auth/authorization.ts +46 -0
  17. package/antd-adapter-template/src/features/auth/use-auth.ts +10 -0
  18. package/antd-adapter-template/src/main.tsx +79 -0
  19. package/antd-adapter-template/src/pages/dashboard/DashboardPage.tsx +105 -0
  20. package/antd-adapter-template/src/pages/errors/ForbiddenPage.tsx +36 -0
  21. package/antd-adapter-template/src/pages/errors/NotFoundPage.tsx +36 -0
  22. package/antd-adapter-template/src/pages/home/HomePage.tsx +129 -0
  23. package/antd-adapter-template/src/pages/login/LoginPage.tsx +128 -0
  24. package/antd-adapter-template/src/pages/permission-test/PermissionTestPage.tsx +55 -0
  25. package/antd-adapter-template/src/pages/restricted/RestrictedDemoPage.tsx +17 -0
  26. package/antd-adapter-template/src/shared/kernel/app-kernel.ts +10 -0
  27. package/antd-adapter-template/src/shared/request/client.ts +46 -0
  28. package/antd-adapter-template/src/shared/request/contracts.ts +6 -0
  29. package/antd-adapter-template/src/shared/request/kv-adapter.ts +14 -0
  30. package/antd-adapter-template/src/shared/request/kv-backend.ts +244 -0
  31. package/antd-adapter-template/src/shared/request/ky-browser-stub.ts +6 -0
  32. package/antd-adapter-template/src/shared/request/undici-browser-stub.ts +4 -0
  33. package/antd-adapter-template/src/styles/global.css +185 -0
  34. package/antd-adapter-template/src/vite-env.d.ts +2 -0
  35. package/antd-adapter-template/tsconfig.app.json +10 -13
  36. package/antd-adapter-template/tsconfig.json +7 -2
  37. package/antd-adapter-template/tsconfig.node.json +6 -16
  38. package/antd-adapter-template/vite.config.ts +24 -0
  39. package/bin/index.mjs +29 -5
  40. package/package.json +2 -2
  41. package/template/apps/core/src/main.tsx +34 -13
  42. package/template/package.json +6 -2
  43. package/template/packages/sdk/package.json +3 -0
  44. package/template/packages/sdk/src/request/index.ts +1 -1
  45. package/template/pnpm-lock.yaml +67 -88
  46. package/antd-adapter-template/apps/core/index.html +0 -13
  47. package/antd-adapter-template/apps/core/package.json +0 -18
  48. package/antd-adapter-template/apps/core/public/favicon.ico +0 -1
  49. package/antd-adapter-template/apps/core/public/favicon.svg +0 -1
  50. package/antd-adapter-template/apps/core/public/logo.svg +0 -1
  51. package/antd-adapter-template/apps/core/src/api/auth.ts +0 -49
  52. package/antd-adapter-template/apps/core/src/assets/react.svg +0 -1
  53. package/antd-adapter-template/apps/core/src/components/AntdGlobalProvider.tsx +0 -87
  54. package/antd-adapter-template/apps/core/src/components/AntdRootLayout.tsx +0 -10
  55. package/antd-adapter-template/apps/core/src/components/layout.tsx +0 -387
  56. package/antd-adapter-template/apps/core/src/guards/auth-route-guard.ts +0 -45
  57. package/antd-adapter-template/apps/core/src/main.tsx +0 -65
  58. package/antd-adapter-template/apps/core/src/pages/auth/components/account-login-fields.tsx +0 -60
  59. package/antd-adapter-template/apps/core/src/pages/auth/components/phone-login-fields.tsx +0 -60
  60. package/antd-adapter-template/apps/core/src/pages/auth/login.tsx +0 -169
  61. package/antd-adapter-template/apps/core/src/pages/index.tsx +0 -156
  62. package/antd-adapter-template/apps/core/src/router.ts +0 -42
  63. package/antd-adapter-template/apps/core/src/shims/use-sync-external-store-shim.ts +0 -3
  64. package/antd-adapter-template/apps/core/src/theme/theme.css +0 -48
  65. package/antd-adapter-template/apps/core/src/types/crypto-js.d.ts +0 -5
  66. package/antd-adapter-template/apps/core/src/utils/index.ts +0 -12
  67. package/antd-adapter-template/apps/core/src/utils/md5.ts +0 -6
  68. package/antd-adapter-template/apps/core/tsconfig.app.json +0 -11
  69. package/antd-adapter-template/apps/core/tsconfig.json +0 -13
  70. package/antd-adapter-template/apps/core/tsconfig.node.json +0 -7
  71. package/antd-adapter-template/apps/core/vite.config.ts +0 -118
  72. package/antd-adapter-template/eslint.config.js +0 -23
  73. package/antd-adapter-template/packages/sdk/.swcrc +0 -18
  74. package/antd-adapter-template/packages/sdk/package.json +0 -52
  75. package/antd-adapter-template/packages/sdk/src/build/index.ts +0 -28
  76. package/antd-adapter-template/packages/sdk/src/build/plugins/auto-import.ts +0 -46
  77. package/antd-adapter-template/packages/sdk/src/build/plugins/bundle-analyzer.ts +0 -33
  78. package/antd-adapter-template/packages/sdk/src/build/plugins/remove-console.ts +0 -23
  79. package/antd-adapter-template/packages/sdk/src/build/plugins/unocss.ts +0 -202
  80. package/antd-adapter-template/packages/sdk/src/build/plugins/unplugin-icon.ts +0 -43
  81. package/antd-adapter-template/packages/sdk/src/components/i18n-switch-dropdown.tsx +0 -139
  82. package/antd-adapter-template/packages/sdk/src/components/index.ts +0 -2
  83. package/antd-adapter-template/packages/sdk/src/components/theme-switch-dropdown.tsx +0 -131
  84. package/antd-adapter-template/packages/sdk/src/hooks/auth/core.ts +0 -101
  85. package/antd-adapter-template/packages/sdk/src/hooks/auth/index.ts +0 -139
  86. package/antd-adapter-template/packages/sdk/src/hooks/auth/with-auth.tsx +0 -41
  87. package/antd-adapter-template/packages/sdk/src/hooks/index.ts +0 -1
  88. package/antd-adapter-template/packages/sdk/src/i18n/index.ts +0 -150
  89. package/antd-adapter-template/packages/sdk/src/index.ts +0 -11
  90. package/antd-adapter-template/packages/sdk/src/request/index.ts +0 -436
  91. package/antd-adapter-template/packages/sdk/src/storage/README.md +0 -30
  92. package/antd-adapter-template/packages/sdk/src/storage/index.ts +0 -57
  93. package/antd-adapter-template/packages/sdk/src/styles/reset.css +0 -111
  94. package/antd-adapter-template/packages/sdk/src/theme/index.ts +0 -466
  95. package/antd-adapter-template/packages/sdk/tsconfig.json +0 -16
  96. package/antd-adapter-template/pnpm-workspace.yaml +0 -3
  97. package/antd-adapter-template/turbo.json +0 -17
@@ -1,60 +0,0 @@
1
- import { LockOutlined, MobileOutlined } from '@ant-design/icons';
2
- import { ProFormCaptcha, ProFormText } from '@ant-design/pro-components';
3
- import { App } from 'antd';
4
- import { useTranslation } from "react-i18next";
5
-
6
- export function PhoneLoginFields() {
7
- const { message, modal, notification } = App.useApp();
8
- void modal;
9
- void notification;
10
- const { t } = useTranslation();
11
-
12
- return (
13
- <>
14
- <ProFormText
15
- fieldProps={{
16
- size: 'large',
17
- prefix: <MobileOutlined className="prefixIcon" />,
18
- }}
19
- name="mobile"
20
- placeholder={t("secraAdmin.login.mobilePlaceholder")}
21
- rules={[
22
- {
23
- required: true,
24
- message: t("secraAdmin.login.mobileRequired"),
25
- },
26
- {
27
- pattern: /^1\d{10}$/,
28
- message: t("secraAdmin.login.mobileInvalid"),
29
- },
30
- ]}
31
- />
32
- <ProFormCaptcha
33
- fieldProps={{
34
- size: 'large',
35
- prefix: <LockOutlined className="prefixIcon" />,
36
- }}
37
- captchaProps={{
38
- size: 'large',
39
- }}
40
- placeholder={t("secraAdmin.login.captchaPlaceholder")}
41
- captchaTextRender={(timing, count) => {
42
- if (timing) {
43
- return t("secraAdmin.login.captchaButtonTiming", { count });
44
- }
45
- return t("secraAdmin.login.captchaButton");
46
- }}
47
- name="captcha"
48
- rules={[
49
- {
50
- required: true,
51
- message: t("secraAdmin.login.captchaRequired"),
52
- },
53
- ]}
54
- onGetCaptcha={async () => {
55
- message.success(t("secraAdmin.login.captchaSuccess"));
56
- }}
57
- />
58
- </>
59
- );
60
- }
@@ -1,169 +0,0 @@
1
- import {
2
- AlipayCircleOutlined,
3
- TaobaoCircleOutlined,
4
- WeiboCircleOutlined,
5
- } from '@ant-design/icons';
6
- import {
7
- LoginForm,
8
- ProFormCheckbox,
9
- } from '@ant-design/pro-components';
10
- import { cache, I18nSwitchDropdown, ThemeSwitchDropdown, useThemeSwitch } from '@vlian/sdk';
11
- import { setToken } from '@vlian/sdk/hooks/auth';
12
- import {App, Form, Space, Tabs, Typography} from 'antd';
13
- import { useState } from 'react';
14
- import { redirect, useNavigate } from 'react-router-dom';
15
- import { useTranslation } from 'react-i18next';
16
- import { login } from '../../api/auth';
17
- import { md5 } from '../../utils/md5';
18
- import { AccountLoginFields } from './components/account-login-fields';
19
- import { PhoneLoginFields } from './components/phone-login-fields';
20
-
21
- type LoginType = 'phone' | 'account';
22
- type LoginFormValues = {
23
- username?: string;
24
- password?: string;
25
- mobile?: string;
26
- captcha?: string;
27
- };
28
-
29
- const ACCESS_TOKEN_KEY = "auth-access-token";
30
- const REFRESH_TOKEN_KEY = "auth-refresh-token";
31
- const USER_INFO_KEY = "auth-user-info";
32
-
33
- export default function LoginPage() {
34
- const [loginType, setLoginType] = useState<LoginType>('account');
35
- const [form] = Form.useForm<LoginFormValues>();
36
- const navigate = useNavigate();
37
- const { message, modal, notification } = App.useApp();
38
- void modal;
39
- const { resolvedMode } = useThemeSwitch();
40
- const { t } = useTranslation();
41
- const isDark = resolvedMode === 'dark';
42
-
43
- const onFinish = async (values: LoginFormValues): Promise<boolean> => {
44
- if (loginType !== "account") {
45
- message.warning(t("secraAdmin.login.phoneLoginNotReady"));
46
- return false;
47
- }
48
- const identifier = values.username?.trim();
49
- const password = values.password ?? "";
50
- if (!identifier || !password) {
51
- message.warning(t("secraAdmin.login.loginFailedCheckCredentials"));
52
- return false;
53
- }
54
-
55
- try {
56
- const response = await login({
57
- identifier,
58
- password: md5(password),
59
- });
60
-
61
- await Promise.all([
62
- setToken(response.access_token, { key: ACCESS_TOKEN_KEY }),
63
- setToken(response.refresh_token, { key: REFRESH_TOKEN_KEY }),
64
- cache.localstorage.set(USER_INFO_KEY, response.user),
65
- ]);
66
- notification.success({
67
- message: t("secraAdmin.login.loginSuccessTitle"),
68
- description: t("secraAdmin.login.loginWelcomeUser", { username: identifier }),
69
- placement: "topRight",
70
- });
71
- navigate("/", { replace: true });
72
- return true;
73
- } catch (error) {
74
- const errorText = error instanceof Error ? error.message.trim() : "";
75
- message.error(errorText || t("secraAdmin.login.loginFailedCheckCredentials"));
76
- return false;
77
- }
78
- };
79
-
80
- return (
81
- <div
82
- className={`relative min-h-screen overflow-hidden flex items-center justify-center px-4 py-6 transition-colors ${
83
- isDark
84
- ? 'bg-[radial-gradient(circle_at_12%_20%,rgba(59,130,246,0.16)_0%,transparent_38%),radial-gradient(circle_at_85%_80%,rgba(16,185,129,0.12)_0%,transparent_35%),linear-gradient(135deg,#020617_0%,#0f172a_45%,#0b1225_100%)]'
85
- : 'bg-[radial-gradient(circle_at_12%_20%,rgba(59,130,246,0.2)_0%,transparent_38%),radial-gradient(circle_at_85%_80%,rgba(16,185,129,0.16)_0%,transparent_35%),linear-gradient(135deg,#f8fafc_0%,#f1f5f9_45%,#e0f2fe_100%)]'
86
- }`}
87
- >
88
- <div
89
- className={`absolute right-4 top-4 z-[2] flex items-center gap-1 rounded-full border px-1 py-1 backdrop-blur-md ${
90
- isDark ? 'border-slate-700 bg-slate-900/70' : 'border-slate-200 bg-white/75'
91
- }`}
92
- >
93
- <I18nSwitchDropdown storageType="localstorage" storageKey="sdk-lang" />
94
- <ThemeSwitchDropdown storageType="localstorage" storageKey="theme-mode" />
95
- </div>
96
- <div className={`absolute top-[-120px] right-[-80px] h-[320px] w-[320px] rounded-full blur-[22px] ${isDark ? 'bg-blue-500/15' : 'bg-blue-400/20'}`} />
97
- <div className={`absolute bottom-[-140px] left-[-80px] h-[280px] w-[280px] rounded-full blur-[18px] ${isDark ? 'bg-emerald-500/15' : 'bg-emerald-400/20'}`} />
98
- <div
99
- className={`relative z-[1] w-full max-w-[460px] rounded-3xl border px-[18px] py-[22px] backdrop-blur-md transition-colors ${
100
- isDark
101
- ? 'border-slate-700/90 bg-slate-900/78 shadow-[0_24px_56px_rgba(2,6,23,0.5)]'
102
- : 'border-slate-200/90 bg-white/88 shadow-[0_24px_56px_rgba(15,23,42,0.14)]'
103
- }`}
104
- >
105
- <LoginForm
106
- form={form}
107
- preserve={false}
108
- logo="/logo.svg"
109
- title={<Typography.Title level={3} className="!mb-0">Secra Admin</Typography.Title>}
110
- subTitle={t("secraAdmin.login.subtitle")}
111
- onFinish={onFinish}
112
- actions={
113
- <Space>
114
- <Typography.Text>{t("secraAdmin.login.otherLoginMethods")}</Typography.Text>
115
- <Typography.Text><AlipayCircleOutlined className="ml-4 text-2xl align-middle cursor-pointer transition-colors hover:text-blue-500" /></Typography.Text>
116
- <Typography.Text><TaobaoCircleOutlined className="ml-4 text-2xl align-middle cursor-pointer transition-colors hover:text-orange-500" /></Typography.Text>
117
- <Typography.Text><WeiboCircleOutlined className="ml-4 text-2xl align-middle cursor-pointer transition-colors hover:text-rose-500" /></Typography.Text>
118
- </Space>
119
- }
120
- >
121
- <Tabs
122
- centered
123
- activeKey={loginType}
124
- onChange={(activeKey) => {
125
- const nextType = activeKey as LoginType;
126
- setLoginType(nextType);
127
- if (nextType === "account") {
128
- form.resetFields(["mobile", "captcha"]);
129
- return;
130
- }
131
- form.resetFields(["username", "password"]);
132
- }}
133
- items={[
134
- { key: 'account', label: t("secraAdmin.login.accountTab") },
135
- { key: 'phone', label: t("secraAdmin.login.phoneTab") },
136
- ]}
137
- />
138
- {loginType === 'account' && (
139
- <AccountLoginFields />
140
- )}
141
- {loginType === 'phone' && (
142
- <PhoneLoginFields />
143
- )}
144
- <div className="mb-6">
145
- <ProFormCheckbox noStyle name="autoLogin">
146
- {t("secraAdmin.login.autoLogin")}
147
- </ProFormCheckbox>
148
- <a className="float-right">{t("secraAdmin.login.forgotPassword")}</a>
149
- </div>
150
- </LoginForm>
151
- </div>
152
- </div>
153
- );
154
- }
155
-
156
- // 进入之前处理 如果有登录则跳转到首页
157
- // eslint-disable-next-line react-refresh/only-export-components
158
- export async function loader() {
159
- const [accessToken, refreshToken] = await Promise.all([
160
- cache.localstorage.get<string>(ACCESS_TOKEN_KEY),
161
- cache.localstorage.get<string>(REFRESH_TOKEN_KEY),
162
- ]);
163
-
164
- if (accessToken || refreshToken) {
165
- return redirect('/');
166
- }
167
-
168
- return null;
169
- }
@@ -1,156 +0,0 @@
1
- import { useMemo, type CSSProperties } from "react";
2
- import { Button as AntdButton, Input, Space, Typography } from "antd";
3
- import { I18nSwitchDropdown, ThemeSwitchDropdown, useUnifiedThemePreset, type UnifiedThemePreset } from "@vlian/sdk";
4
- import { withAuth } from "@vlian/sdk/hooks/auth";
5
- import { useTranslation } from "react-i18next";
6
-
7
- const THEME_PRESETS: UnifiedThemePreset[] = [
8
- {
9
- key: "ocean",
10
- label: "Ocean",
11
- primaryColor: "#1677ff",
12
- },
13
- {
14
- key: "emerald",
15
- label: "Emerald",
16
- primaryColor: "#10b981",
17
- },
18
- {
19
- key: "rose",
20
- label: "Rose",
21
- primaryColor: "#e11d48",
22
- },
23
- ];
24
-
25
- const Home = () => {
26
- const { t } = useTranslation();
27
- const { theme, mode, resolvedMode, setTheme, presetKey, activePreset, applyPreset, setUnifiedPrimaryColor } = useUnifiedThemePreset(THEME_PRESETS, {
28
- defaultPresetKey: THEME_PRESETS[0].key,
29
- borderRadius: 8,
30
- });
31
-
32
- const palette = useMemo(
33
- () =>
34
- resolvedMode === "dark"
35
- ? {
36
- text: "#e2e8f0",
37
- textMuted: "#94a3b8",
38
- background: "linear-gradient(180deg, #0f172a 0%, #020617 100%)",
39
- cardBg: "#0b1225",
40
- cardBorder: "#1f2a44",
41
- }
42
- : {
43
- text: "#0f172a",
44
- textMuted: "#475569",
45
- background: "linear-gradient(180deg, #f8fafc 0%, #ffffff 100%)",
46
- cardBg: "#ffffff",
47
- cardBorder: "#e5e7eb",
48
- },
49
- [resolvedMode],
50
- );
51
-
52
- const cardStyle: CSSProperties = useMemo(
53
- () => ({
54
- border: `1px solid ${palette.cardBorder}`,
55
- borderRadius: 12,
56
- padding: 16,
57
- background: palette.cardBg,
58
- boxShadow: resolvedMode === "dark" ? "0 8px 28px rgba(2, 6, 23, 0.5)" : "0 6px 24px rgba(15, 23, 42, 0.06)",
59
- }),
60
- [palette.cardBg, palette.cardBorder, resolvedMode],
61
- );
62
-
63
- return (
64
- <main style={{ minHeight: "100vh", padding: "40px 20px", background: palette.background, color: palette.text, position: "relative" }}>
65
- <div
66
- style={{
67
- position: "absolute",
68
- right: 16,
69
- top: 16,
70
- zIndex: 1,
71
- display: "flex",
72
- alignItems: "center",
73
- gap: 4,
74
- borderRadius: 999,
75
- border: `1px solid ${palette.cardBorder}`,
76
- padding: 4,
77
- background: resolvedMode === "dark" ? "rgba(15, 23, 42, 0.65)" : "rgba(255, 255, 255, 0.75)",
78
- backdropFilter: "blur(8px)",
79
- }}
80
- >
81
- <I18nSwitchDropdown storageType="localstorage" storageKey="sdk-lang" />
82
- <ThemeSwitchDropdown storageType="localstorage" storageKey="theme-mode" />
83
- </div>
84
- <section style={{ maxWidth: 980, margin: "0 auto", display: "grid", gap: 14 }}>
85
- <div style={cardStyle}>
86
- <h1 style={{ margin: 0, fontSize: 30 }}>{t("secraAdmin.home.title")}</h1>
87
- <p style={{ margin: "8px 0 0", color: palette.textMuted }}>
88
- {t("secraAdmin.home.currentMode")}:<b>{mode}</b>({t("secraAdmin.home.resolvedMode")}:<b>{resolvedMode}</b>)
89
- </p>
90
- </div>
91
-
92
- <div style={cardStyle}>
93
- <h3 style={{ marginTop: 0 }}>{t("secraAdmin.home.modeSwitchTitle")}</h3>
94
- <Space wrap>
95
- {(["light", "dark", "system"] as const).map((nextMode) => (
96
- <AntdButton
97
- key={nextMode}
98
- type={mode === nextMode ? "primary" : "default"}
99
- onClick={() =>
100
- setTheme((prev) => {
101
- const previous = prev ?? {};
102
- return { ...previous, mode: nextMode };
103
- })
104
- }
105
- >
106
- {t(`secraAdmin.home.mode.${nextMode}`)}
107
- </AntdButton>
108
- ))}
109
- </Space>
110
- </div>
111
-
112
- <div style={cardStyle}>
113
- <h3 style={{ marginTop: 0 }}>{t("secraAdmin.home.unifiedThemeTitle")}</h3>
114
- <Space wrap>
115
- {THEME_PRESETS.map((preset) => (
116
- <AntdButton key={preset.key} type={presetKey === preset.key ? "primary" : "default"} onClick={() => applyPreset(preset.key)}>
117
- {preset.label}
118
- </AntdButton>
119
- ))}
120
- <Input
121
- style={{ width: 180 }}
122
- value={theme.primaryColor ?? activePreset?.primaryColor ?? "#1677ff"}
123
- onChange={(event) => {
124
- setUnifiedPrimaryColor(event.target.value);
125
- }}
126
- placeholder="#1677ff"
127
- />
128
- </Space>
129
- </div>
130
-
131
- <div style={cardStyle}>
132
- <h3 style={{ marginTop: 0 }}>{t("secraAdmin.home.previewTitle")}</h3>
133
- <Space wrap>
134
- <AntdButton type="primary">Antd Primary</AntdButton>
135
- <AntdButton>Antd Default</AntdButton>
136
- <AntdButton type="dashed">Antd Dashed</AntdButton>
137
- <AntdButton type="text">Antd Text</AntdButton>
138
- </Space>
139
- </div>
140
-
141
- <div style={cardStyle}>
142
- <h3 style={{ marginTop: 0 }}>{t("secraAdmin.home.currentUnifiedThemeTitle")}</h3>
143
- <Typography.Paragraph style={{ marginTop: 10, marginBottom: 0, color: palette.textMuted }}>
144
- {t("secraAdmin.home.themePreset")}:<b>{activePreset?.label ?? "-"}</b>,primaryColor:<b>{theme.primaryColor}</b>
145
- </Typography.Paragraph>
146
- </div>
147
- </section>
148
- </main>
149
- );
150
- };
151
-
152
- const HomeWithAuth = withAuth(Home);
153
-
154
- HomeWithAuth.displayName = "Home";
155
-
156
- export default HomeWithAuth;
@@ -1,42 +0,0 @@
1
- import type { RouteConfig } from "@vlian/framework/core"
2
- import { loader as LoginLoader } from "./pages/auth/login";
3
-
4
- const LoginPage = lazy(() => import("./pages/auth/login"));
5
- const HomePage = lazy(() => import("./pages/index"));
6
-
7
- export const getRoutes = async (): Promise<RouteConfig[]> => {
8
- return [
9
- {
10
- path: "/auth/login",
11
- name: "authLogin",
12
- page: async () => ({
13
- default: LoginPage,
14
- loader: LoginLoader,
15
- }),
16
- handle: {
17
- title: "登录",
18
- order: 0,
19
- },
20
- },
21
- {
22
- path: "/",
23
- name: "home",
24
- page: async () => ({
25
- default: HomePage,
26
- }),
27
- handle: {
28
- title: "首页",
29
- order: 1,
30
- }
31
- },
32
- {
33
- path: "/test",
34
- name: "Test",
35
- layout: () => import('./components/layout.tsx'),
36
- handle: {
37
- title: "测试",
38
- order: 2,
39
- }
40
- }
41
- ]
42
- }
@@ -1,3 +0,0 @@
1
- import { useSyncExternalStore as reactUseSyncExternalStore } from 'react';
2
-
3
- export const useSyncExternalStore = reactUseSyncExternalStore;
@@ -1,48 +0,0 @@
1
- :root {
2
- --background: 0 0% 100%;
3
- --foreground: 222.2 84% 4.9%;
4
- --card: 0 0% 100%;
5
- --card-foreground: 222.2 84% 4.9%;
6
- --popover: 0 0% 100%;
7
- --popover-foreground: 222.2 84% 4.9%;
8
- --primary: 221.2 83.2% 53.3%;
9
- --primary-foreground: 210 40% 98%;
10
- --secondary: 210 40% 96.1%;
11
- --secondary-foreground: 222.2 47.4% 11.2%;
12
- --muted: 210 40% 96.1%;
13
- --muted-foreground: 215.4 16.3% 46.9%;
14
- --accent: 210 40% 96.1%;
15
- --accent-foreground: 222.2 47.4% 11.2%;
16
- --destructive: 0 84.2% 60.2%;
17
- --destructive-foreground: 210 40% 98%;
18
- --border: 214.3 31.8% 91.4%;
19
- --input: 214.3 31.8% 91.4%;
20
- --ring: 221.2 83.2% 53.3%;
21
- }
22
-
23
- .dark {
24
- --background: 222.2 84% 4.9%;
25
- --foreground: 210 40% 98%;
26
- --card: 222.2 84% 4.9%;
27
- --card-foreground: 210 40% 98%;
28
- --popover: 222.2 84% 4.9%;
29
- --popover-foreground: 210 40% 98%;
30
- --primary: 217.2 91.2% 59.8%;
31
- --primary-foreground: 222.2 47.4% 11.2%;
32
- --secondary: 217.2 32.6% 17.5%;
33
- --secondary-foreground: 210 40% 98%;
34
- --muted: 217.2 32.6% 17.5%;
35
- --muted-foreground: 215 20.2% 65.1%;
36
- --accent: 217.2 32.6% 17.5%;
37
- --accent-foreground: 210 40% 98%;
38
- --destructive: 0 62.8% 30.6%;
39
- --destructive-foreground: 210 40% 98%;
40
- --border: 217.2 32.6% 17.5%;
41
- --input: 217.2 32.6% 17.5%;
42
- --ring: 224.3 76.3% 48%;
43
- }
44
-
45
- body {
46
- background-color: hsl(var(--background));
47
- color: hsl(var(--foreground));
48
- }
@@ -1,5 +0,0 @@
1
- declare module "crypto-js/md5" {
2
- const md5: (input: string) => { toString: () => string };
3
- export default md5;
4
- }
5
-
@@ -1,12 +0,0 @@
1
- type LogMethod = (...args: unknown[]) => void;
2
-
3
- /**
4
- * Compatibility logger for framework code that imports `@/utils`.
5
- */
6
- export const logger: Record<'info' | 'warn' | 'error' | 'debug', LogMethod> = {
7
- info: (...args) => console.info(...args),
8
- warn: (...args) => console.warn(...args),
9
- error: (...args) => console.error(...args),
10
- debug: (...args) => console.debug(...args),
11
- };
12
-
@@ -1,6 +0,0 @@
1
- import md5Impl from "crypto-js/md5";
2
-
3
- export const md5 = (input: string): string => {
4
- return md5Impl(input).toString();
5
- };
6
-
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.app.json",
3
- "compilerOptions": {
4
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5
- "baseUrl": ".",
6
- "paths": {
7
- "@/*": ["./src/*"]
8
- }
9
- },
10
- "include": ["src"]
11
- }
@@ -1,13 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "baseUrl": ".",
4
- "paths": {
5
- "@/*": ["./src/*"]
6
- }
7
- },
8
- "files": [],
9
- "references": [
10
- { "path": "./tsconfig.app.json" },
11
- { "path": "./tsconfig.node.json" }
12
- ]
13
- }
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.node.json",
3
- "compilerOptions": {
4
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo"
5
- },
6
- "include": ["vite.config.ts"]
7
- }
@@ -1,118 +0,0 @@
1
- import { defineConfig } from 'vite'
2
- import react from '@vitejs/plugin-react'
3
- import Inspect from 'vite-plugin-inspect'
4
- import path from 'node:path';
5
- import {
6
- setupAutoImport,
7
- setupUnocss,
8
- setupUnPluginIcon,
9
- setupRemoveConsole,
10
- include,
11
- } from '@vlian/sdk/build';
12
-
13
- // https://vite.dev/config/
14
- export default defineConfig(({ mode }) => {
15
- void mode;
16
-
17
- return {
18
- plugins: [
19
- react(),
20
- // 开发环境:打包检查工具(可选,需要 vite@^6.0.0)
21
- // 注意:vite-plugin-inspect 在某些情况下可能不兼容,如遇错误请注释掉
22
- ...(process.env.NODE_ENV === 'development' ? [Inspect()] : []),
23
-
24
- // 生产环境:移除 console
25
- setupRemoveConsole(),
26
-
27
- // 自动导入
28
- setupAutoImport(),
29
-
30
- // UnoCSS(需要指定本地图标路径)
31
- setupUnocss(
32
- path.join(process.cwd(), 'src/assets/svg-icon'),
33
- {
34
- // 覆盖 content 配置,扫描主应用代码
35
- content: {
36
- filesystem: ['./src/**/*.{tsx,ts,jsx,js}'],
37
- },
38
- }
39
- ),
40
-
41
- // 图标插件(SVG 支持)
42
- ...setupUnPluginIcon(
43
- path.join(process.cwd(), 'src/assets/svg-icon')
44
- ),
45
-
46
- // 打包分析(可选,建议仅在需要时启用)
47
- // setupBundleAnalyzer({
48
- // filename: '../../dist/main/stats.html',
49
- // open: true,
50
- // }),
51
- ],
52
- resolve: {
53
- alias: {
54
- '@': path.resolve(__dirname, './src'),
55
- 'use-sync-external-store/shim': path.resolve(__dirname, './src/shims/use-sync-external-store-shim.ts'),
56
- 'use-sync-external-store/shim/index.js': path.resolve(__dirname, './src/shims/use-sync-external-store-shim.ts'),
57
- },
58
- preserveSymlinks: false,
59
- },
60
- // 依赖优化
61
- optimizeDeps: {
62
- include: [...include],
63
- // exclude: ["@vlian/sdk", "@vlian/sdk/request", "@vlian/sdk/build", "lightningcss"],
64
- },
65
- server: {
66
- host: '127.0.0.1',
67
- port: 5173,
68
- open: true,
69
- },
70
- build: {
71
- outDir: '../../dist/core',
72
- sourcemap: false,
73
- manifest: true,
74
- // 确保 CSS 被提取到单独文件
75
- cssCodeSplit: true,
76
- chunkSizeWarningLimit: 700,
77
- modulePreload: {
78
- // Keep HTML preload list focused to avoid over-eager first-wave requests.
79
- resolveDependencies: (_filename, deps, context) => {
80
- if (context.hostType !== 'html') return deps;
81
- return deps.filter(
82
- (dep) =>
83
- dep.includes('rolldown-runtime') ||
84
- dep.includes('/index-') ||
85
- dep.includes('/index.'),
86
- );
87
- },
88
- },
89
- rollupOptions: {
90
- // Node-only adapter dependency used by framework optional path.
91
- // Keep it external to avoid browser bundle resolution failure.
92
- external: ['undici'],
93
- output: {
94
- manualChunks(id) {
95
- if (id.includes('node_modules')) {
96
- if (id.includes('/node_modules/@ant-design/pro-components/')) return 'pro-vendor';
97
- if (
98
- id.includes('/node_modules/react/') ||
99
- id.includes('/node_modules/react-dom/') ||
100
- id.includes('/node_modules/scheduler/')
101
- ) return 'react-vendor';
102
- if (id.includes('/node_modules/@ant-design/icons/')) return 'antd-icons-vendor';
103
- if (
104
- id.includes('/node_modules/antd/') ||
105
- (id.includes('/node_modules/@ant-design/') && !id.includes('/node_modules/@ant-design/pro-components/'))
106
- ) return 'antd-vendor';
107
- if (id.includes('@vlian/framework/dist/request/')) return 'framework-request-vendor';
108
- if (id.includes('@vlian/framework/dist/state/')) return 'framework-state-vendor';
109
- if (id.includes('@vlian/framework/dist/core/')) return 'framework-core-vendor';
110
- if (id.includes('@vlian/framework')) return 'framework-misc-vendor';
111
- if (id.includes('i18next') || id.includes('react-i18next')) return 'i18n-vendor';
112
- }
113
- },
114
- },
115
- },
116
- },
117
- };
118
- })