openxiangda 1.0.62 → 1.0.63
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/package.json +1 -1
- package/packages/sdk/dist/runtime/index.cjs +276 -13
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +1 -1
- package/packages/sdk/dist/runtime/index.d.ts +1 -1
- package/packages/sdk/dist/runtime/index.mjs +276 -13
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/react.cjs +277 -13
- package/packages/sdk/dist/runtime/react.cjs.map +1 -1
- package/packages/sdk/dist/runtime/react.d.mts +60 -7
- package/packages/sdk/dist/runtime/react.d.ts +60 -7
- package/packages/sdk/dist/runtime/react.mjs +277 -13
- package/packages/sdk/dist/runtime/react.mjs.map +1 -1
- package/templates/openxiangda-react-spa/src/app/router.tsx +12 -1
- package/templates/openxiangda-react-spa/src/layouts/AdminShell.tsx +374 -96
- package/templates/openxiangda-react-spa/src/layouts/PublicShell.tsx +25 -3
- package/templates/openxiangda-react-spa/src/layouts/UserShell.tsx +101 -22
- package/templates/openxiangda-react-spa/src/pages/admin/RuntimeWorkspacePage.tsx +203 -41
- package/templates/openxiangda-react-spa/src/pages/defaults/DataRoutePage.tsx +80 -11
- package/templates/openxiangda-react-spa/src/pages/defaults/FilePreviewRoutePage.tsx +67 -14
- package/templates/openxiangda-react-spa/src/pages/defaults/FormRoutePage.tsx +153 -30
- package/templates/openxiangda-react-spa/src/pages/portal/UserPortalPage.tsx +53 -14
- package/templates/openxiangda-react-spa/src/pages/public/PublicHomePage.tsx +46 -6
- package/templates/openxiangda-react-spa/src/pages/states/NotFoundPage.tsx +26 -11
- package/templates/openxiangda-react-spa/src/shared/mac-admin.tsx +332 -0
- package/templates/openxiangda-react-spa/src/styles/index.css +42 -4
- package/templates/openxiangda-react-spa/tailwind.config.cjs +8 -0
- package/templates/openxiangda-react-spa/vite.config.ts +31 -0
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FileText, LogIn, Shield, Workflow } from "lucide-react";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
3
|
import { useNavigate, useParams } from "react-router-dom";
|
|
3
4
|
import { StandardFormPage } from "openxiangda";
|
|
4
5
|
import { normalizeRuntimeFormSchema } from "openxiangda/runtime";
|
|
5
|
-
import { useOpenXiangda } from "openxiangda/runtime/react";
|
|
6
|
+
import { useOpenXiangda, useRuntimeAuth } from "openxiangda/runtime/react";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
MacDiagnosticPanel,
|
|
10
|
+
MacPageHeader,
|
|
11
|
+
MacPanel,
|
|
12
|
+
MacPrimaryButton,
|
|
13
|
+
MacStatePage,
|
|
14
|
+
MacStatusPill,
|
|
15
|
+
} from "@/shared/mac-admin";
|
|
6
16
|
import {
|
|
7
17
|
defaultPageOverrides,
|
|
8
18
|
resolveDefaultPageOverride,
|
|
@@ -11,14 +21,28 @@ import {
|
|
|
11
21
|
|
|
12
22
|
type Mode = "submit" | "detail" | "process";
|
|
13
23
|
|
|
24
|
+
type PageError = {
|
|
25
|
+
message: string;
|
|
26
|
+
status?: number;
|
|
27
|
+
type: "unauthenticated" | "forbidden" | "unknown";
|
|
28
|
+
};
|
|
29
|
+
|
|
14
30
|
export function FormRoutePage({ mode }: { mode: Mode }) {
|
|
15
31
|
const { appType = "", formUuid = "", formInstId = "" } = useParams();
|
|
16
32
|
const navigate = useNavigate();
|
|
17
33
|
const runtime = useOpenXiangda();
|
|
18
34
|
const [schema, setSchema] = useState<any>(null);
|
|
19
|
-
const [error, setError] = useState<
|
|
35
|
+
const [error, setError] = useState<PageError | null>(null);
|
|
20
36
|
|
|
21
37
|
useEffect(() => {
|
|
38
|
+
if (runtime.error && !runtime.data) {
|
|
39
|
+
setError({
|
|
40
|
+
message: runtime.error.message,
|
|
41
|
+
status: runtime.error.status,
|
|
42
|
+
type: runtime.error.type === "unauthenticated" || runtime.error.type === "forbidden" ? runtime.error.type : "unknown",
|
|
43
|
+
});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
22
46
|
let disposed = false;
|
|
23
47
|
const load = async () => {
|
|
24
48
|
try {
|
|
@@ -26,27 +50,29 @@ export function FormRoutePage({ mode }: { mode: Mode }) {
|
|
|
26
50
|
`${runtime.servicePrefix}/openxiangda-api/v1/apps/${encodeURIComponent(appType)}/forms/${encodeURIComponent(formUuid)}`,
|
|
27
51
|
{ credentials: "include", headers: { accept: "application/json" } },
|
|
28
52
|
);
|
|
29
|
-
const payload = await response.json();
|
|
53
|
+
const payload = await response.json().catch(() => null);
|
|
30
54
|
if (!response.ok || payload?.code >= 400) {
|
|
31
|
-
throw
|
|
55
|
+
throw createPageError(response.status, payload?.code, payload?.message || "表单 schema 加载失败");
|
|
32
56
|
}
|
|
33
57
|
const normalizedSchema = normalizeRuntimeFormSchema(payload, { appType, formUuid });
|
|
34
58
|
if (!normalizedSchema) {
|
|
35
|
-
throw
|
|
59
|
+
throw createPageError(undefined, undefined, "表单 schema 格式暂不支持或没有可渲染字段");
|
|
60
|
+
}
|
|
61
|
+
if (!disposed) {
|
|
62
|
+
setSchema(normalizedSchema);
|
|
63
|
+
setError(null);
|
|
36
64
|
}
|
|
37
|
-
if (!disposed) setSchema(normalizedSchema);
|
|
38
65
|
} catch (err) {
|
|
39
|
-
if (!disposed)
|
|
66
|
+
if (!disposed) {
|
|
67
|
+
setError(normalizePageError(err));
|
|
68
|
+
}
|
|
40
69
|
}
|
|
41
70
|
};
|
|
42
71
|
void load();
|
|
43
72
|
return () => {
|
|
44
73
|
disposed = true;
|
|
45
74
|
};
|
|
46
|
-
}, [appType, formUuid, runtime.fetchImpl, runtime.servicePrefix]);
|
|
47
|
-
|
|
48
|
-
if (error) return <DefaultState title="加载失败" description={error} />;
|
|
49
|
-
if (!schema) return <DefaultState title="加载中" description="正在读取表单配置" />;
|
|
75
|
+
}, [appType, formUuid, runtime.data, runtime.error, runtime.fetchImpl, runtime.servicePrefix]);
|
|
50
76
|
|
|
51
77
|
const formType = String(schema?.template?.formType || schema?.formMeta?.formType || "").toLowerCase();
|
|
52
78
|
const isProcessForm = mode === "process" || formType === "process" || formType === "flow";
|
|
@@ -61,6 +87,11 @@ export function FormRoutePage({ mode }: { mode: Mode }) {
|
|
|
61
87
|
: isProcessForm
|
|
62
88
|
? "process-submit"
|
|
63
89
|
: "form-submit";
|
|
90
|
+
const pageCopy = useMemo(() => resolvePageCopy(overrideKind, mode), [mode, overrideKind]);
|
|
91
|
+
|
|
92
|
+
if (error) return <DefaultErrorState error={error} />;
|
|
93
|
+
if (!schema) return <DefaultLoadingState description="正在读取表单配置、字段权限和默认值。" title="加载默认页" />;
|
|
94
|
+
|
|
64
95
|
const Override = resolveDefaultPageOverride(defaultPageOverrides, overrideKind, formUuid);
|
|
65
96
|
const defaultNode = (
|
|
66
97
|
<StandardFormPage
|
|
@@ -80,28 +111,120 @@ export function FormRoutePage({ mode }: { mode: Mode }) {
|
|
|
80
111
|
);
|
|
81
112
|
|
|
82
113
|
return (
|
|
83
|
-
<div className="
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
114
|
+
<div className="min-w-0 space-y-5">
|
|
115
|
+
<MacPageHeader
|
|
116
|
+
description={pageCopy.description}
|
|
117
|
+
eyebrow={pageCopy.eyebrow}
|
|
118
|
+
meta={
|
|
119
|
+
<>
|
|
120
|
+
<MacStatusPill tone={isProcessForm ? "amber" : "blue"}>{isProcessForm ? "流程表单" : "普通表单"}</MacStatusPill>
|
|
121
|
+
<MacStatusPill tone="emerald">后端权限兜底</MacStatusPill>
|
|
122
|
+
<MacStatusPill tone="violet">override ready</MacStatusPill>
|
|
123
|
+
</>
|
|
124
|
+
}
|
|
125
|
+
title={pageCopy.title}
|
|
126
|
+
/>
|
|
127
|
+
<MacPanel className="min-w-0 overflow-hidden">
|
|
128
|
+
{Override ? (
|
|
129
|
+
<Override
|
|
130
|
+
appType={appType}
|
|
131
|
+
defaultNode={defaultNode}
|
|
132
|
+
formInstId={formInstId}
|
|
133
|
+
formUuid={formUuid}
|
|
134
|
+
kind={overrideKind}
|
|
135
|
+
schema={schema}
|
|
136
|
+
/>
|
|
137
|
+
) : (
|
|
138
|
+
defaultNode
|
|
139
|
+
)}
|
|
140
|
+
</MacPanel>
|
|
141
|
+
<MacDiagnosticPanel data={{ appType, formInstId, formUuid, kind: overrideKind }} title="默认页上下文" />
|
|
96
142
|
</div>
|
|
97
143
|
);
|
|
98
144
|
}
|
|
99
145
|
|
|
100
|
-
function
|
|
146
|
+
function DefaultLoadingState({ description, title }: { description: string; title: string }) {
|
|
101
147
|
return (
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
<
|
|
105
|
-
|
|
148
|
+
<MacStatePage
|
|
149
|
+
description={description}
|
|
150
|
+
icon={<FileText size={24} />}
|
|
151
|
+
status="LOADING"
|
|
152
|
+
title={title}
|
|
153
|
+
/>
|
|
106
154
|
);
|
|
107
155
|
}
|
|
156
|
+
|
|
157
|
+
function DefaultErrorState({ error }: { error: PageError }) {
|
|
158
|
+
const auth = useRuntimeAuth();
|
|
159
|
+
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (error.type === "unauthenticated") {
|
|
162
|
+
void auth.redirectToLogin({ replace: true });
|
|
163
|
+
}
|
|
164
|
+
}, [auth, error.type]);
|
|
165
|
+
|
|
166
|
+
if (error.type === "unauthenticated") {
|
|
167
|
+
return (
|
|
168
|
+
<MacStatePage
|
|
169
|
+
actions={<MacPrimaryButton onClick={() => void auth.redirectToLogin({ replace: true })}>去登录</MacPrimaryButton>}
|
|
170
|
+
description="当前没有有效登录态,正在跳转到登录页。"
|
|
171
|
+
icon={<LogIn size={24} />}
|
|
172
|
+
status="401"
|
|
173
|
+
title="需要登录"
|
|
174
|
+
/>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<MacStatePage
|
|
180
|
+
description={error.message || "请确认当前用户是否拥有表单、流程或数据访问权限。"}
|
|
181
|
+
icon={error.type === "forbidden" ? <Shield size={24} /> : <Workflow size={24} />}
|
|
182
|
+
status={error.type === "forbidden" ? "403" : String(error.status || "ERROR")}
|
|
183
|
+
title={error.type === "forbidden" ? "无权访问默认页" : "默认页加载失败"}
|
|
184
|
+
/>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function resolvePageCopy(kind: DefaultPageKind, mode: Mode) {
|
|
189
|
+
if (kind === "process-submit") {
|
|
190
|
+
return {
|
|
191
|
+
description: "使用 SDK 内置提交页发起流程,审批预览和字段权限由后端接口决定。",
|
|
192
|
+
eyebrow: "Process Submit",
|
|
193
|
+
title: "发起流程",
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (kind === "process-detail" || mode === "process") {
|
|
197
|
+
return {
|
|
198
|
+
description: "查看流程详情、审批动作和流程节点信息,操作权限由后端判定。",
|
|
199
|
+
eyebrow: "Process Detail",
|
|
200
|
+
title: "流程详情",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (kind === "form-detail") {
|
|
204
|
+
return {
|
|
205
|
+
description: "查看表单详情、变更记录和字段权限状态。",
|
|
206
|
+
eyebrow: "Form Detail",
|
|
207
|
+
title: "表单详情",
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
description: "使用 SDK 内置提交页提交普通表单,提交成功后跳转到详情页。",
|
|
212
|
+
eyebrow: "Form Submit",
|
|
213
|
+
title: "发起表单",
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function createPageError(status: number | undefined, code: number | string | undefined, message: string): PageError {
|
|
218
|
+
const normalizedCode = typeof code === "string" ? Number(code) : code;
|
|
219
|
+
if (status === 401 || normalizedCode === 401) return { message, status, type: "unauthenticated" };
|
|
220
|
+
if (status === 403 || normalizedCode === 403) return { message, status, type: "forbidden" };
|
|
221
|
+
return { message, status, type: "unknown" };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function normalizePageError(error: unknown): PageError {
|
|
225
|
+
if (error && typeof error === "object" && "type" in error) return error as PageError;
|
|
226
|
+
return {
|
|
227
|
+
message: error instanceof Error ? error.message : String(error),
|
|
228
|
+
type: "unknown",
|
|
229
|
+
};
|
|
230
|
+
}
|
|
@@ -1,27 +1,66 @@
|
|
|
1
|
-
import { FileText, Inbox, Search } from "lucide-react";
|
|
1
|
+
import { ArrowRight, FileText, Inbox, Search, Sparkles } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
MacListItem,
|
|
5
|
+
MacMetricCard,
|
|
6
|
+
MacPageHeader,
|
|
7
|
+
MacPanel,
|
|
8
|
+
MacStatusPill,
|
|
9
|
+
} from "@/shared/mac-admin";
|
|
2
10
|
|
|
3
11
|
const entries = [
|
|
4
|
-
{
|
|
5
|
-
{
|
|
6
|
-
{
|
|
12
|
+
{ desc: "打开常用表单和流程", icon: FileText, title: "发起申请", tone: "blue" as const },
|
|
13
|
+
{ desc: "处理审批与办理任务", icon: Inbox, title: "我的待办", tone: "amber" as const },
|
|
14
|
+
{ desc: "查看有权限的数据列表", icon: Search, title: "数据查询", tone: "emerald" as const },
|
|
7
15
|
];
|
|
8
16
|
|
|
9
17
|
export function UserPortalPage() {
|
|
10
18
|
return (
|
|
11
|
-
<div className="space-y-5">
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
<div className="min-w-0 space-y-5">
|
|
20
|
+
<MacPageHeader
|
|
21
|
+
description="面向普通用户的入口页,和后台共享同一套登录、权限与 SDK。你可以把这里替换成业务门户、移动端入口或任务中心。"
|
|
22
|
+
eyebrow="User Portal"
|
|
23
|
+
meta={
|
|
24
|
+
<>
|
|
25
|
+
<MacStatusPill tone="emerald">真实用户身份</MacStatusPill>
|
|
26
|
+
<MacStatusPill tone="blue">页面 code 权限</MacStatusPill>
|
|
27
|
+
<MacStatusPill tone="violet">可自定义路由</MacStatusPill>
|
|
28
|
+
</>
|
|
29
|
+
}
|
|
30
|
+
title="用户门户"
|
|
31
|
+
/>
|
|
16
32
|
<section className="grid gap-4 md:grid-cols-3">
|
|
17
33
|
{entries.map(entry => (
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
<MacMetricCard
|
|
35
|
+
caption={entry.desc}
|
|
36
|
+
icon={entry.icon}
|
|
37
|
+
key={entry.title}
|
|
38
|
+
label={entry.title}
|
|
39
|
+
tone={entry.tone}
|
|
40
|
+
value={<span className="text-base">进入</span>}
|
|
41
|
+
/>
|
|
23
42
|
))}
|
|
24
43
|
</section>
|
|
44
|
+
<MacPanel title="最近任务" description="示例内容来自 starter,真实项目可以接入流程待办或业务表单。">
|
|
45
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
46
|
+
{entries.map(entry => (
|
|
47
|
+
<MacListItem key={entry.title} tone={entry.tone}>
|
|
48
|
+
<div className="flex items-center justify-between gap-3">
|
|
49
|
+
<div className="min-w-0">
|
|
50
|
+
<div className="truncate text-sm font-semibold text-slate-950">{entry.title}</div>
|
|
51
|
+
<div className="mt-1 truncate text-xs text-slate-500">{entry.desc}</div>
|
|
52
|
+
</div>
|
|
53
|
+
<ArrowRight className="shrink-0 text-slate-400" size={17} />
|
|
54
|
+
</div>
|
|
55
|
+
</MacListItem>
|
|
56
|
+
))}
|
|
57
|
+
<MacListItem icon={<Sparkles size={17} />} tone="violet">
|
|
58
|
+
<div className="text-sm leading-6 text-slate-600">
|
|
59
|
+
AI 可以通过一次性登录链接,以不同真实用户身份验证门户权限和页面状态。
|
|
60
|
+
</div>
|
|
61
|
+
</MacListItem>
|
|
62
|
+
</div>
|
|
63
|
+
</MacPanel>
|
|
25
64
|
</div>
|
|
26
65
|
);
|
|
27
66
|
}
|
|
@@ -1,10 +1,50 @@
|
|
|
1
|
+
import { FileCheck2, Globe2, LockKeyhole, MessageSquareText } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
MacListItem,
|
|
5
|
+
MacPageHeader,
|
|
6
|
+
MacPanel,
|
|
7
|
+
MacStatusPill,
|
|
8
|
+
} from "@/shared/mac-admin";
|
|
9
|
+
|
|
1
10
|
export function PublicHomePage() {
|
|
2
11
|
return (
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
<div className="min-w-0 space-y-5">
|
|
13
|
+
<MacPageHeader
|
|
14
|
+
description="公开表单、公开查询和 ticket 状态页都在 PublicShell 下实现,不再依赖旧平台导航参数。公开链路仍需要后端接口校验。"
|
|
15
|
+
eyebrow="Public Runtime"
|
|
16
|
+
meta={
|
|
17
|
+
<>
|
|
18
|
+
<MacStatusPill tone="blue">guest</MacStatusPill>
|
|
19
|
+
<MacStatusPill tone="emerald">publicAccess</MacStatusPill>
|
|
20
|
+
<MacStatusPill tone="amber">ticket 状态</MacStatusPill>
|
|
21
|
+
</>
|
|
22
|
+
}
|
|
23
|
+
title="公开访问"
|
|
24
|
+
/>
|
|
25
|
+
<MacPanel title="公开链路" description="这些是 starter 默认提供的公开页能力方向。">
|
|
26
|
+
<div className="grid gap-3 md:grid-cols-3">
|
|
27
|
+
<MacListItem icon={<Globe2 size={17} />} tone="blue">
|
|
28
|
+
<div className="text-sm font-semibold text-slate-950">公开页面</div>
|
|
29
|
+
<div className="mt-1 text-xs text-slate-500">用于无需登录的说明、查询或入口。</div>
|
|
30
|
+
</MacListItem>
|
|
31
|
+
<MacListItem icon={<FileCheck2 size={17} />} tone="emerald">
|
|
32
|
+
<div className="text-sm font-semibold text-slate-950">公开表单</div>
|
|
33
|
+
<div className="mt-1 text-xs text-slate-500">通过后端公开访问策略提交数据。</div>
|
|
34
|
+
</MacListItem>
|
|
35
|
+
<MacListItem icon={<LockKeyhole size={17} />} tone="amber">
|
|
36
|
+
<div className="text-sm font-semibold text-slate-950">Ticket 校验</div>
|
|
37
|
+
<div className="mt-1 text-xs text-slate-500">文件预览、邀请链接等短期凭证入口。</div>
|
|
38
|
+
</MacListItem>
|
|
39
|
+
</div>
|
|
40
|
+
</MacPanel>
|
|
41
|
+
<MacPanel title="公开提示">
|
|
42
|
+
<MacListItem icon={<MessageSquareText size={17} />} tone="violet">
|
|
43
|
+
<div className="text-sm leading-6 text-slate-600">
|
|
44
|
+
公开访问不是绕过权限。默认模板只负责展示与路由,最终是否允许访问仍由平台接口和公开访问配置决定。
|
|
45
|
+
</div>
|
|
46
|
+
</MacListItem>
|
|
47
|
+
</MacPanel>
|
|
48
|
+
</div>
|
|
9
49
|
);
|
|
10
50
|
}
|
|
@@ -1,16 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Compass, Home } from "lucide-react";
|
|
2
|
+
import { useParams } from "react-router-dom";
|
|
3
|
+
|
|
4
|
+
import { MacPrimaryButton, MacSecondaryButton, MacStatePage } from "@/shared/mac-admin";
|
|
2
5
|
|
|
3
6
|
export function NotFoundPage() {
|
|
7
|
+
const { appType = process.env.OPENXIANGDA_APP_TYPE || "" } = useParams();
|
|
8
|
+
const home = appType ? `/view/${appType}/admin` : "/";
|
|
9
|
+
|
|
4
10
|
return (
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
<MacStatePage
|
|
12
|
+
actions={
|
|
13
|
+
<>
|
|
14
|
+
<MacSecondaryButton onClick={() => window.history.back()}>
|
|
15
|
+
<Compass size={17} />
|
|
16
|
+
返回上一页
|
|
17
|
+
</MacSecondaryButton>
|
|
18
|
+
<MacPrimaryButton onClick={() => window.location.assign(home)}>
|
|
19
|
+
<Home size={17} />
|
|
20
|
+
返回入口
|
|
21
|
+
</MacPrimaryButton>
|
|
22
|
+
</>
|
|
23
|
+
}
|
|
24
|
+
description="请检查当前 React Router 路由、页面 code 或平台 route resolver 配置。"
|
|
25
|
+
fullScreen
|
|
26
|
+
icon={<Compass size={24} />}
|
|
27
|
+
status="404"
|
|
28
|
+
title="页面不存在"
|
|
29
|
+
/>
|
|
15
30
|
);
|
|
16
31
|
}
|