create-secra 0.1.11 → 1.0.2
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.
- package/README.md +15 -5
- package/README.zh-CN.md +15 -4
- package/antd-adapter-template/README.md +24 -0
- package/antd-adapter-template/index.html +16 -0
- package/antd-adapter-template/package-lock.json +3420 -0
- package/antd-adapter-template/package.json +20 -47
- package/antd-adapter-template/src/app/App.tsx +7 -0
- package/antd-adapter-template/src/app/layouts/AppLayout.tsx +163 -0
- package/antd-adapter-template/src/app/router.ts +26 -0
- package/antd-adapter-template/src/app/routes/RouteTitleSync.tsx +26 -0
- package/antd-adapter-template/src/app/routes/dynamic-routes.ts +52 -0
- package/antd-adapter-template/src/app/routes/route-modules.ts +4 -0
- package/antd-adapter-template/src/app/routes/static-routes.tsx +110 -0
- package/antd-adapter-template/src/app/routes/types.ts +9 -0
- package/antd-adapter-template/src/features/auth/api/auth-api.ts +61 -0
- package/antd-adapter-template/src/features/auth/auth-store.ts +125 -0
- package/antd-adapter-template/src/features/auth/auth-types.ts +29 -0
- package/antd-adapter-template/src/features/auth/authorization.ts +46 -0
- package/antd-adapter-template/src/features/auth/use-auth.ts +10 -0
- package/antd-adapter-template/src/main.tsx +79 -0
- package/antd-adapter-template/src/pages/dashboard/DashboardPage.tsx +105 -0
- package/antd-adapter-template/src/pages/errors/ForbiddenPage.tsx +36 -0
- package/antd-adapter-template/src/pages/errors/NotFoundPage.tsx +36 -0
- package/antd-adapter-template/src/pages/home/HomePage.tsx +129 -0
- package/antd-adapter-template/src/pages/login/LoginPage.tsx +128 -0
- package/antd-adapter-template/src/pages/permission-test/PermissionTestPage.tsx +55 -0
- package/antd-adapter-template/src/pages/restricted/RestrictedDemoPage.tsx +17 -0
- package/antd-adapter-template/src/shared/kernel/app-kernel.ts +10 -0
- package/antd-adapter-template/src/shared/request/client.ts +46 -0
- package/antd-adapter-template/src/shared/request/contracts.ts +6 -0
- package/antd-adapter-template/src/shared/request/kv-adapter.ts +14 -0
- package/antd-adapter-template/src/shared/request/kv-backend.ts +244 -0
- package/antd-adapter-template/src/shared/request/ky-browser-stub.ts +6 -0
- package/antd-adapter-template/src/shared/request/undici-browser-stub.ts +4 -0
- package/antd-adapter-template/src/styles/global.css +185 -0
- package/antd-adapter-template/src/vite-env.d.ts +2 -0
- package/antd-adapter-template/tsconfig.app.json +10 -13
- package/antd-adapter-template/tsconfig.json +7 -2
- package/antd-adapter-template/tsconfig.node.json +6 -16
- package/antd-adapter-template/vite.config.ts +24 -0
- package/bin/index.mjs +32 -14
- package/package.json +2 -2
- package/{antd-adapter-template/apps/core → template}/index.html +2 -3
- package/template/package.json +14 -46
- package/template/src/App.tsx +48 -0
- package/template/src/main.tsx +36 -0
- package/template/src/styles.css +163 -0
- package/template/src/vite-env.d.ts +1 -0
- package/template/tsconfig.app.json +0 -5
- package/template/tsconfig.node.json +0 -5
- package/template/vite.config.ts +6 -0
- package/antd-adapter-template/apps/core/package.json +0 -18
- package/antd-adapter-template/apps/core/public/favicon.ico +0 -1
- package/antd-adapter-template/apps/core/public/favicon.svg +0 -1
- package/antd-adapter-template/apps/core/public/logo.svg +0 -1
- package/antd-adapter-template/apps/core/src/api/auth.ts +0 -49
- package/antd-adapter-template/apps/core/src/assets/react.svg +0 -1
- package/antd-adapter-template/apps/core/src/components/AntdGlobalProvider.tsx +0 -87
- package/antd-adapter-template/apps/core/src/components/AntdRootLayout.tsx +0 -10
- package/antd-adapter-template/apps/core/src/components/layout.tsx +0 -387
- package/antd-adapter-template/apps/core/src/guards/auth-route-guard.ts +0 -45
- package/antd-adapter-template/apps/core/src/main.tsx +0 -65
- package/antd-adapter-template/apps/core/src/pages/auth/components/account-login-fields.tsx +0 -60
- package/antd-adapter-template/apps/core/src/pages/auth/components/phone-login-fields.tsx +0 -60
- package/antd-adapter-template/apps/core/src/pages/auth/login.tsx +0 -169
- package/antd-adapter-template/apps/core/src/pages/index.tsx +0 -156
- package/antd-adapter-template/apps/core/src/router.ts +0 -42
- package/antd-adapter-template/apps/core/src/shims/use-sync-external-store-shim.ts +0 -3
- package/antd-adapter-template/apps/core/src/theme/theme.css +0 -48
- package/antd-adapter-template/apps/core/src/types/crypto-js.d.ts +0 -5
- package/antd-adapter-template/apps/core/src/utils/index.ts +0 -12
- package/antd-adapter-template/apps/core/src/utils/md5.ts +0 -6
- package/antd-adapter-template/apps/core/tsconfig.app.json +0 -11
- package/antd-adapter-template/apps/core/tsconfig.json +0 -13
- package/antd-adapter-template/apps/core/tsconfig.node.json +0 -7
- package/antd-adapter-template/apps/core/vite.config.ts +0 -118
- package/antd-adapter-template/eslint.config.js +0 -23
- package/antd-adapter-template/packages/sdk/.swcrc +0 -18
- package/antd-adapter-template/packages/sdk/package.json +0 -52
- package/antd-adapter-template/packages/sdk/src/build/index.ts +0 -28
- package/antd-adapter-template/packages/sdk/src/build/plugins/auto-import.ts +0 -46
- package/antd-adapter-template/packages/sdk/src/build/plugins/bundle-analyzer.ts +0 -33
- package/antd-adapter-template/packages/sdk/src/build/plugins/remove-console.ts +0 -23
- package/antd-adapter-template/packages/sdk/src/build/plugins/unocss.ts +0 -202
- package/antd-adapter-template/packages/sdk/src/build/plugins/unplugin-icon.ts +0 -43
- package/antd-adapter-template/packages/sdk/src/components/i18n-switch-dropdown.tsx +0 -139
- package/antd-adapter-template/packages/sdk/src/components/index.ts +0 -2
- package/antd-adapter-template/packages/sdk/src/components/theme-switch-dropdown.tsx +0 -131
- package/antd-adapter-template/packages/sdk/src/hooks/auth/core.ts +0 -101
- package/antd-adapter-template/packages/sdk/src/hooks/auth/index.ts +0 -139
- package/antd-adapter-template/packages/sdk/src/hooks/auth/with-auth.tsx +0 -41
- package/antd-adapter-template/packages/sdk/src/hooks/index.ts +0 -1
- package/antd-adapter-template/packages/sdk/src/i18n/index.ts +0 -150
- package/antd-adapter-template/packages/sdk/src/index.ts +0 -11
- package/antd-adapter-template/packages/sdk/src/request/index.ts +0 -436
- package/antd-adapter-template/packages/sdk/src/storage/README.md +0 -30
- package/antd-adapter-template/packages/sdk/src/storage/index.ts +0 -57
- package/antd-adapter-template/packages/sdk/src/styles/reset.css +0 -111
- package/antd-adapter-template/packages/sdk/src/theme/index.ts +0 -466
- package/antd-adapter-template/packages/sdk/tsconfig.json +0 -16
- package/antd-adapter-template/pnpm-workspace.yaml +0 -3
- package/antd-adapter-template/turbo.json +0 -17
- package/template/apps/core/index.html +0 -13
- package/template/apps/core/package.json +0 -18
- package/template/apps/core/public/favicon.ico +0 -1
- package/template/apps/core/public/favicon.svg +0 -1
- package/template/apps/core/public/logo.svg +0 -1
- package/template/apps/core/src/assets/react.svg +0 -1
- package/template/apps/core/src/main.tsx +0 -14
- package/template/apps/core/src/pages/index.tsx +0 -84
- package/template/apps/core/src/router.ts +0 -19
- package/template/apps/core/src/types/auto-imports.d.ts +0 -130
- package/template/apps/core/tsconfig.app.json +0 -7
- package/template/apps/core/tsconfig.json +0 -7
- package/template/apps/core/tsconfig.node.json +0 -7
- package/template/apps/core/vite.config.ts +0 -86
- package/template/eslint.config.js +0 -23
- package/template/packages/sdk/.swcrc +0 -18
- package/template/packages/sdk/package-lock.json +0 -1621
- package/template/packages/sdk/package.json +0 -32
- package/template/packages/sdk/src/build/index.ts +0 -33
- package/template/packages/sdk/src/build/plugins/auto-import.ts +0 -47
- package/template/packages/sdk/src/build/plugins/bundle-analyzer.ts +0 -35
- package/template/packages/sdk/src/build/plugins/remove-console.ts +0 -21
- package/template/packages/sdk/src/build/plugins/unocss.ts +0 -113
- package/template/packages/sdk/src/build/plugins/unplugin-icon.ts +0 -43
- package/template/packages/sdk/src/index.ts +0 -1
- package/template/packages/sdk/src/request/index.ts +0 -341
- package/template/packages/sdk/src/styles/reset.css +0 -111
- package/template/packages/sdk/tsconfig.json +0 -16
- package/template/pnpm-lock.yaml +0 -8055
- package/template/pnpm-workspace.yaml +0 -3
- package/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,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,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,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
|
-
})
|