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,24 @@
|
|
|
1
|
+
import { Home, LogIn, Shield, Sparkles } from "lucide-react";
|
|
2
|
+
import { useEffect } from "react";
|
|
1
3
|
import { Link, Outlet, useParams } from "react-router-dom";
|
|
2
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
PermissionBoundary,
|
|
6
|
+
useCanAccessRoute,
|
|
7
|
+
useRuntimeAuth,
|
|
8
|
+
useRuntimeBootstrap,
|
|
9
|
+
type PermissionBoundaryFallbackState,
|
|
10
|
+
} from "openxiangda/runtime/react";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
MacPrimaryButton,
|
|
14
|
+
MacSecondaryButton,
|
|
15
|
+
MacStatePage,
|
|
16
|
+
MacStatusPill,
|
|
17
|
+
} from "@/shared/mac-admin";
|
|
3
18
|
|
|
4
19
|
export function UserShell() {
|
|
5
20
|
const { appType = "" } = useParams();
|
|
21
|
+
const bootstrap = useRuntimeBootstrap();
|
|
6
22
|
const adminAccess = useCanAccessRoute({
|
|
7
23
|
routeCode: "admin.dashboard",
|
|
8
24
|
menuCode: "runtime_workspace",
|
|
@@ -11,26 +27,41 @@ export function UserShell() {
|
|
|
11
27
|
|
|
12
28
|
return (
|
|
13
29
|
<PermissionBoundary
|
|
14
|
-
|
|
30
|
+
fallback={state => <UserAccessState appType={appType} state={state} />}
|
|
31
|
+
loadingFallback={<UserLoadingState />}
|
|
15
32
|
menuCode="user_portal"
|
|
16
33
|
path={`/view/${appType}/portal`}
|
|
17
|
-
|
|
18
|
-
fallback={<ShellState title="无权访问用户门户" description="请切换到有权限的账号,或联系管理员开通页面权限。" />}
|
|
34
|
+
routeCode="portal.home"
|
|
19
35
|
>
|
|
20
|
-
<div className="min-h-screen bg-[
|
|
21
|
-
<header className="border-b border-
|
|
22
|
-
<div className="mx-auto flex max-w-6xl items-center justify-between px-5 py-4">
|
|
23
|
-
<Link className="
|
|
24
|
-
|
|
36
|
+
<div className="min-h-screen overflow-x-hidden bg-[linear-gradient(180deg,#eef2f7_0%,#f8fafc_58%,#eef2f7_100%)]">
|
|
37
|
+
<header className="border-b border-white/70 bg-white/[0.78] backdrop-blur-xl">
|
|
38
|
+
<div className="mx-auto flex h-20 max-w-6xl items-center justify-between gap-3 px-5 py-4">
|
|
39
|
+
<Link className="flex min-w-0 items-center gap-3" to={`/view/${appType}/portal`}>
|
|
40
|
+
<span className="grid h-11 w-11 shrink-0 place-items-center rounded-2xl bg-slate-950 text-sm font-semibold text-white shadow-lg shadow-slate-300/70">
|
|
41
|
+
OX
|
|
42
|
+
</span>
|
|
43
|
+
<span className="min-w-0">
|
|
44
|
+
<span className="block truncate text-base font-semibold text-slate-950">
|
|
45
|
+
{String(bootstrap.data?.app?.name || "用户门户")}
|
|
46
|
+
</span>
|
|
47
|
+
<span className="block truncate text-xs text-slate-500">OpenXiangda Portal</span>
|
|
48
|
+
</span>
|
|
25
49
|
</Link>
|
|
26
|
-
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
50
|
+
<div className="flex shrink-0 items-center gap-2">
|
|
51
|
+
<MacStatusPill tone="emerald">已登录</MacStatusPill>
|
|
52
|
+
{adminAccess.canAccess ? (
|
|
53
|
+
<Link
|
|
54
|
+
className="inline-flex h-10 items-center gap-2 rounded-xl bg-white px-3 text-sm font-semibold text-slate-700 shadow-sm ring-1 ring-slate-200 transition hover:bg-slate-50"
|
|
55
|
+
to={`/view/${appType}/admin`}
|
|
56
|
+
>
|
|
57
|
+
<Home size={17} />
|
|
58
|
+
管理后台
|
|
59
|
+
</Link>
|
|
60
|
+
) : null}
|
|
61
|
+
</div>
|
|
31
62
|
</div>
|
|
32
63
|
</header>
|
|
33
|
-
<main className="mx-auto max-w-6xl px-5 py-6">
|
|
64
|
+
<main className="mx-auto min-w-0 max-w-6xl px-5 py-6">
|
|
34
65
|
<Outlet />
|
|
35
66
|
</main>
|
|
36
67
|
</div>
|
|
@@ -38,13 +69,61 @@ export function UserShell() {
|
|
|
38
69
|
);
|
|
39
70
|
}
|
|
40
71
|
|
|
41
|
-
function
|
|
72
|
+
function UserAccessState({
|
|
73
|
+
appType,
|
|
74
|
+
state,
|
|
75
|
+
}: {
|
|
76
|
+
appType: string;
|
|
77
|
+
state: PermissionBoundaryFallbackState;
|
|
78
|
+
}) {
|
|
79
|
+
const auth = useRuntimeAuth();
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (state.errorType === "unauthenticated") {
|
|
83
|
+
void auth.redirectToLogin({ replace: true });
|
|
84
|
+
}
|
|
85
|
+
}, [auth, state.errorType]);
|
|
86
|
+
|
|
87
|
+
if (state.errorType === "unauthenticated") {
|
|
88
|
+
return (
|
|
89
|
+
<MacStatePage
|
|
90
|
+
actions={<MacPrimaryButton onClick={() => void auth.redirectToLogin({ replace: true })}>去登录</MacPrimaryButton>}
|
|
91
|
+
description="当前没有有效登录态,正在跳转到登录页。"
|
|
92
|
+
fullScreen
|
|
93
|
+
icon={<LogIn size={24} />}
|
|
94
|
+
status="401"
|
|
95
|
+
title="需要登录"
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
42
100
|
return (
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
101
|
+
<MacStatePage
|
|
102
|
+
actions={
|
|
103
|
+
<>
|
|
104
|
+
<MacSecondaryButton onClick={() => window.history.back()}>返回上一页</MacSecondaryButton>
|
|
105
|
+
<MacPrimaryButton onClick={() => window.location.assign(`/view/${appType}/public`)}>
|
|
106
|
+
打开公开页
|
|
107
|
+
</MacPrimaryButton>
|
|
108
|
+
</>
|
|
109
|
+
}
|
|
110
|
+
description={state.message || "请联系管理员开通用户门户页面权限。"}
|
|
111
|
+
fullScreen
|
|
112
|
+
icon={<Shield size={24} />}
|
|
113
|
+
status="403"
|
|
114
|
+
title="无权访问用户门户"
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function UserLoadingState() {
|
|
120
|
+
return (
|
|
121
|
+
<MacStatePage
|
|
122
|
+
description="正在确认当前用户、菜单和页面权限。"
|
|
123
|
+
fullScreen
|
|
124
|
+
icon={<Sparkles size={24} />}
|
|
125
|
+
status="LOADING"
|
|
126
|
+
title="正在进入用户门户"
|
|
127
|
+
/>
|
|
49
128
|
);
|
|
50
129
|
}
|
|
@@ -1,57 +1,219 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Activity,
|
|
3
|
+
CheckCircle2,
|
|
4
|
+
Clock3,
|
|
5
|
+
Cookie,
|
|
6
|
+
Database,
|
|
7
|
+
FileText,
|
|
8
|
+
GitBranch,
|
|
9
|
+
Rocket,
|
|
10
|
+
ShieldCheck,
|
|
11
|
+
Sparkles,
|
|
12
|
+
Workflow,
|
|
13
|
+
} from "lucide-react";
|
|
14
|
+
import type { ReactNode } from "react";
|
|
2
15
|
import { useRuntimeBootstrap } from "openxiangda/runtime/react";
|
|
3
16
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
17
|
+
import {
|
|
18
|
+
MacDiagnosticPanel,
|
|
19
|
+
MacListItem,
|
|
20
|
+
MacMetricCard,
|
|
21
|
+
MacPageHeader,
|
|
22
|
+
MacPanel,
|
|
23
|
+
MacPrimaryButton,
|
|
24
|
+
MacSecondaryButton,
|
|
25
|
+
MacStatusPill,
|
|
26
|
+
MacTrendBars,
|
|
27
|
+
} from "@/shared/mac-admin";
|
|
28
|
+
|
|
29
|
+
const metrics = [
|
|
30
|
+
{
|
|
31
|
+
caption: "较昨日 +12.4%,来自用户门户与公开页。",
|
|
32
|
+
icon: Activity,
|
|
33
|
+
label: "页面访问",
|
|
34
|
+
tone: "blue" as const,
|
|
35
|
+
value: "24,680",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
caption: "流程节点、办理任务和待确认数据。",
|
|
39
|
+
icon: Clock3,
|
|
40
|
+
label: "待处理",
|
|
41
|
+
tone: "amber" as const,
|
|
42
|
+
value: "18",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
caption: "表单、流程和数据列表均由 SDK 默认页承载。",
|
|
46
|
+
icon: FileText,
|
|
47
|
+
label: "默认页",
|
|
48
|
+
tone: "emerald" as const,
|
|
49
|
+
value: "6",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
caption: "Playwright 可使用真实用户一次性登录验证。",
|
|
53
|
+
icon: Sparkles,
|
|
54
|
+
label: "AI 校验",
|
|
55
|
+
tone: "violet" as const,
|
|
56
|
+
value: "Ready",
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const activities = [
|
|
61
|
+
{ icon: <CheckCircle2 size={17} />, text: "加载应用菜单与当前用户页面权限。", tone: "emerald" as const },
|
|
62
|
+
{ icon: <GitBranch size={17} />, text: "React Router 接管 /view 下的应用路由。", tone: "blue" as const },
|
|
63
|
+
{ icon: <ShieldCheck size={17} />, text: "后端继续兜底表单、字段、数据和流程权限。", tone: "amber" as const },
|
|
64
|
+
{ icon: <Sparkles size={17} />, text: "AI 验证登录以真实用户身份进入页面。", tone: "violet" as const },
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const tasks = [
|
|
68
|
+
{ label: "运行时需求", meta: "普通表单 · 待提交", tone: "blue" as const },
|
|
69
|
+
{ label: "运行时审批", meta: "流程表单 · 待处理", tone: "amber" as const },
|
|
70
|
+
{ label: "用户门户", meta: "页面权限 · 已发布", tone: "emerald" as const },
|
|
71
|
+
{ label: "公开访问", meta: "ticket 链路 · 待验证", tone: "violet" as const },
|
|
9
72
|
];
|
|
10
73
|
|
|
11
74
|
export function RuntimeWorkspacePage() {
|
|
12
75
|
const runtime = useRuntimeBootstrap();
|
|
76
|
+
const appName = String(runtime.data?.app?.name || "OpenXiangda React SPA");
|
|
77
|
+
const appType = runtime.appType || String(runtime.data?.appType || "");
|
|
78
|
+
const buildId = String(runtime.data?.runtime?.activeBuildId || "local-dev");
|
|
79
|
+
const releaseId = String(runtime.data?.runtime?.activeReleaseId || "draft");
|
|
80
|
+
|
|
13
81
|
return (
|
|
14
|
-
<div className="space-y-5">
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
</
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
82
|
+
<div className="min-w-0 space-y-5">
|
|
83
|
+
<MacPageHeader
|
|
84
|
+
actions={
|
|
85
|
+
<>
|
|
86
|
+
<MacSecondaryButton>
|
|
87
|
+
<Database size={17} />
|
|
88
|
+
同步资源
|
|
89
|
+
</MacSecondaryButton>
|
|
90
|
+
<MacPrimaryButton>
|
|
91
|
+
<Rocket size={17} />
|
|
92
|
+
发布版本
|
|
93
|
+
</MacPrimaryButton>
|
|
94
|
+
</>
|
|
95
|
+
}
|
|
96
|
+
description="应用路由、布局、用户门户、公开访问和默认表单页均由当前 React SPA 接管;平台继续负责登录、权限、数据和流程接口。"
|
|
97
|
+
eyebrow="Runtime Workspace"
|
|
98
|
+
meta={
|
|
99
|
+
<>
|
|
100
|
+
<MacStatusPill tone="emerald">SDK 已连接</MacStatusPill>
|
|
101
|
+
<MacStatusPill tone="blue">Cookie 同域代理</MacStatusPill>
|
|
102
|
+
<MacStatusPill tone="amber">后端权限兜底</MacStatusPill>
|
|
103
|
+
<MacStatusPill tone="violet">React SPA</MacStatusPill>
|
|
104
|
+
</>
|
|
105
|
+
}
|
|
106
|
+
title={appName}
|
|
107
|
+
/>
|
|
108
|
+
|
|
109
|
+
<section className="grid min-w-0 gap-4 md:grid-cols-2 xl:grid-cols-4">
|
|
110
|
+
{metrics.map(metric => (
|
|
111
|
+
<MacMetricCard key={metric.label} {...metric} />
|
|
112
|
+
))}
|
|
27
113
|
</section>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
114
|
+
|
|
115
|
+
<section className="grid min-w-0 gap-4 xl:grid-cols-[minmax(0,1.35fr)_minmax(320px,0.65fr)]">
|
|
116
|
+
<MacPanel
|
|
117
|
+
description="用于检查新应用运行质量、AI 验证覆盖度和默认页接入情况。"
|
|
118
|
+
title="运行概览"
|
|
119
|
+
>
|
|
120
|
+
<div className="grid min-w-0 gap-4 lg:grid-cols-[minmax(0,1fr)_280px]">
|
|
121
|
+
<div className="min-w-0">
|
|
122
|
+
<MacTrendBars tone="blue" values={[36, 48, 42, 66, 58, 78, 86, 71, 92, 88, 104, 118]} />
|
|
123
|
+
<div className="mt-4 grid gap-3 sm:grid-cols-3">
|
|
124
|
+
<RuntimeChip icon={<Cookie size={17} />} label="Service Proxy" value="/service" />
|
|
125
|
+
<RuntimeChip icon={<ShieldCheck size={17} />} label="Route Guard" value="backend" />
|
|
126
|
+
<RuntimeChip icon={<Workflow size={17} />} label="Workflow" value="enabled" />
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
<div className="rounded-2xl border border-slate-200/80 bg-slate-950 p-5 text-white">
|
|
130
|
+
<div className="text-sm text-slate-300">AI 验证覆盖</div>
|
|
131
|
+
<div
|
|
132
|
+
className="mx-auto mt-6 grid h-36 w-36 place-items-center rounded-full"
|
|
133
|
+
style={{ background: "conic-gradient(#22c55e 0 72%, #38bdf8 72% 88%, #f59e0b 88% 100%)" }}
|
|
134
|
+
>
|
|
135
|
+
<div className="grid h-24 w-24 place-items-center rounded-full bg-slate-950 text-center">
|
|
136
|
+
<span className="text-3xl font-semibold">88%</span>
|
|
137
|
+
<span className="-mt-4 text-xs text-slate-400">verified</span>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
<div className="mt-5 grid gap-2 text-xs text-slate-300">
|
|
141
|
+
<span>真实用户登录链路</span>
|
|
142
|
+
<span>默认页 + override 双路径</span>
|
|
143
|
+
<span>401 / 403 状态分离</span>
|
|
144
|
+
</div>
|
|
33
145
|
</div>
|
|
34
|
-
<div className="text-sm text-slate-500">{card.label}</div>
|
|
35
|
-
<div className="mt-1 text-lg font-semibold text-slate-950">{card.value}</div>
|
|
36
146
|
</div>
|
|
37
|
-
|
|
147
|
+
</MacPanel>
|
|
148
|
+
|
|
149
|
+
<MacPanel title="待办与入口" description="这些入口可以替换成你的真实业务菜单。">
|
|
150
|
+
<div className="space-y-3">
|
|
151
|
+
{tasks.map(task => (
|
|
152
|
+
<MacListItem key={task.label} tone={task.tone}>
|
|
153
|
+
<div className="flex items-center justify-between gap-3">
|
|
154
|
+
<div className="min-w-0">
|
|
155
|
+
<div className="truncate text-sm font-semibold text-slate-950">{task.label}</div>
|
|
156
|
+
<div className="mt-1 truncate text-xs text-slate-500">{task.meta}</div>
|
|
157
|
+
</div>
|
|
158
|
+
<span className="rounded-full bg-slate-100 px-2.5 py-1 text-xs text-slate-500">open</span>
|
|
159
|
+
</div>
|
|
160
|
+
</MacListItem>
|
|
161
|
+
))}
|
|
162
|
+
</div>
|
|
163
|
+
</MacPanel>
|
|
38
164
|
</section>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
{
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
165
|
+
|
|
166
|
+
<section className="grid min-w-0 gap-4 xl:grid-cols-[minmax(0,1fr)_380px]">
|
|
167
|
+
<MacPanel title="最近活动">
|
|
168
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
169
|
+
{activities.map(activity => (
|
|
170
|
+
<MacListItem icon={activity.icon} key={activity.text} tone={activity.tone}>
|
|
171
|
+
<div className="text-sm leading-6 text-slate-600">{activity.text}</div>
|
|
172
|
+
</MacListItem>
|
|
173
|
+
))}
|
|
174
|
+
</div>
|
|
175
|
+
</MacPanel>
|
|
176
|
+
|
|
177
|
+
<MacPanel title="运行环境" description="诊断信息默认收起,避免首页出现横向滚动。">
|
|
178
|
+
<div className="space-y-3 text-sm">
|
|
179
|
+
<InfoRow label="appType" value={appType} />
|
|
180
|
+
<InfoRow label="buildId" value={buildId} />
|
|
181
|
+
<InfoRow label="releaseId" value={releaseId} />
|
|
182
|
+
<InfoRow label="mode" value={String(runtime.data?.runtime?.mode || "react-spa")} />
|
|
52
183
|
</div>
|
|
53
|
-
</
|
|
184
|
+
</MacPanel>
|
|
54
185
|
</section>
|
|
186
|
+
|
|
187
|
+
<MacDiagnosticPanel data={runtime.data} title="Runtime Bootstrap" />
|
|
188
|
+
</div>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function RuntimeChip({
|
|
193
|
+
icon,
|
|
194
|
+
label,
|
|
195
|
+
value,
|
|
196
|
+
}: {
|
|
197
|
+
icon: ReactNode;
|
|
198
|
+
label: string;
|
|
199
|
+
value: string;
|
|
200
|
+
}) {
|
|
201
|
+
return (
|
|
202
|
+
<div className="rounded-2xl border border-slate-200/80 bg-white/75 p-3">
|
|
203
|
+
<div className="flex items-center gap-2 text-xs text-slate-500">
|
|
204
|
+
<span className="text-blue-600">{icon}</span>
|
|
205
|
+
{label}
|
|
206
|
+
</div>
|
|
207
|
+
<div className="mt-2 truncate text-sm font-semibold text-slate-950">{value}</div>
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function InfoRow({ label, value }: { label: string; value: string }) {
|
|
213
|
+
return (
|
|
214
|
+
<div className="flex min-w-0 items-center justify-between gap-3 rounded-xl bg-slate-50 px-3 py-2">
|
|
215
|
+
<span className="shrink-0 text-xs font-medium text-slate-500">{label}</span>
|
|
216
|
+
<span className="min-w-0 truncate text-xs font-semibold text-slate-800">{value}</span>
|
|
55
217
|
</div>
|
|
56
218
|
);
|
|
57
219
|
}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
import { Database, FilePlus2, Filter, RefreshCw, Rows3 } from "lucide-react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
1
3
|
import { useParams } from "react-router-dom";
|
|
2
4
|
import { DataManagementList } from "openxiangda";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
MacDiagnosticPanel,
|
|
8
|
+
MacPageHeader,
|
|
9
|
+
MacPanel,
|
|
10
|
+
MacPrimaryButton,
|
|
11
|
+
MacSecondaryButton,
|
|
12
|
+
MacStatusPill,
|
|
13
|
+
} from "@/shared/mac-admin";
|
|
3
14
|
import {
|
|
4
15
|
defaultPageOverrides,
|
|
5
16
|
resolveDefaultPageOverride,
|
|
@@ -19,17 +30,75 @@ export function DataRoutePage() {
|
|
|
19
30
|
);
|
|
20
31
|
|
|
21
32
|
return (
|
|
22
|
-
<div className="
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
<div className="min-w-0 space-y-5">
|
|
34
|
+
<MacPageHeader
|
|
35
|
+
actions={
|
|
36
|
+
<>
|
|
37
|
+
<MacSecondaryButton>
|
|
38
|
+
<Filter size={17} />
|
|
39
|
+
筛选
|
|
40
|
+
</MacSecondaryButton>
|
|
41
|
+
<MacSecondaryButton onClick={() => window.location.reload()}>
|
|
42
|
+
<RefreshCw size={17} />
|
|
43
|
+
刷新
|
|
44
|
+
</MacSecondaryButton>
|
|
45
|
+
<MacPrimaryButton onClick={() => window.location.assign(`/view/${appType}/admin/forms/${formUuid}/new`)}>
|
|
46
|
+
<FilePlus2 size={17} />
|
|
47
|
+
新增数据
|
|
48
|
+
</MacPrimaryButton>
|
|
49
|
+
</>
|
|
50
|
+
}
|
|
51
|
+
description="使用 SDK 内置 DataManagementList 展示有权限的数据,新增和详情仍回到同一套 React SPA 默认页。"
|
|
52
|
+
eyebrow="Data Management"
|
|
53
|
+
meta={
|
|
54
|
+
<>
|
|
55
|
+
<MacStatusPill tone="blue">数据范围后端计算</MacStatusPill>
|
|
56
|
+
<MacStatusPill tone="emerald">字段权限生效</MacStatusPill>
|
|
57
|
+
<MacStatusPill tone="amber">详情页联动</MacStatusPill>
|
|
58
|
+
</>
|
|
59
|
+
}
|
|
60
|
+
title="数据管理"
|
|
61
|
+
/>
|
|
62
|
+
<section className="grid gap-4 md:grid-cols-3">
|
|
63
|
+
<DataSummary icon={<Database size={18} />} label="当前表单" value={formUuid || "--"} />
|
|
64
|
+
<DataSummary icon={<Rows3 size={18} />} label="列表模式" value="SDK 默认页" />
|
|
65
|
+
<DataSummary icon={<Filter size={18} />} label="权限过滤" value="后端兜底" />
|
|
66
|
+
</section>
|
|
67
|
+
<MacPanel className="min-w-0 overflow-hidden p-0">
|
|
68
|
+
<div className="min-w-0 overflow-hidden">
|
|
69
|
+
{Override ? (
|
|
70
|
+
<Override
|
|
71
|
+
appType={appType}
|
|
72
|
+
defaultNode={defaultNode}
|
|
73
|
+
formUuid={formUuid}
|
|
74
|
+
kind="data-manage-list"
|
|
75
|
+
/>
|
|
76
|
+
) : (
|
|
77
|
+
defaultNode
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
</MacPanel>
|
|
81
|
+
<MacDiagnosticPanel data={{ appType, formUuid, kind: "data-manage-list" }} title="数据页上下文" />
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function DataSummary({
|
|
87
|
+
icon,
|
|
88
|
+
label,
|
|
89
|
+
value,
|
|
90
|
+
}: {
|
|
91
|
+
icon: ReactNode;
|
|
92
|
+
label: string;
|
|
93
|
+
value: string;
|
|
94
|
+
}) {
|
|
95
|
+
return (
|
|
96
|
+
<div className="rounded-2xl border border-white/70 bg-white/[0.82] p-4 shadow-[0_16px_45px_rgba(15,23,42,0.05)]">
|
|
97
|
+
<div className="flex items-center gap-2 text-sm text-slate-500">
|
|
98
|
+
<span className="text-blue-600">{icon}</span>
|
|
99
|
+
{label}
|
|
100
|
+
</div>
|
|
101
|
+
<div className="mt-2 truncate text-base font-semibold text-slate-950">{value}</div>
|
|
33
102
|
</div>
|
|
34
103
|
);
|
|
35
104
|
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import { FileSearch, LockKeyhole, ShieldCheck, Ticket } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
MacDiagnosticPanel,
|
|
5
|
+
MacPageHeader,
|
|
6
|
+
MacPanel,
|
|
7
|
+
MacStatePage,
|
|
8
|
+
MacStatusPill,
|
|
9
|
+
} from "@/shared/mac-admin";
|
|
1
10
|
import {
|
|
2
11
|
defaultPageOverrides,
|
|
3
12
|
resolveDefaultPageOverride,
|
|
@@ -7,23 +16,67 @@ export function FilePreviewRoutePage() {
|
|
|
7
16
|
const ticket = new URLSearchParams(window.location.search).get("ticket") || "";
|
|
8
17
|
const appType = process.env.OPENXIANGDA_APP_TYPE || process.env.APP_TYPE || "";
|
|
9
18
|
const Override = resolveDefaultPageOverride(defaultPageOverrides, "file-preview");
|
|
10
|
-
const defaultNode = (
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
const defaultNode = ticket ? (
|
|
20
|
+
<div className="min-w-0 space-y-5">
|
|
21
|
+
<MacPageHeader
|
|
22
|
+
description="轻量 runtime 内置文件预览入口。后续平台文件元数据协议稳定后,可在这里直接接入预览器或通过 override 替换整页。"
|
|
23
|
+
eyebrow="File Preview"
|
|
24
|
+
meta={
|
|
25
|
+
<>
|
|
26
|
+
<MacStatusPill tone="blue">ticket</MacStatusPill>
|
|
27
|
+
<MacStatusPill tone="emerald">同域校验</MacStatusPill>
|
|
28
|
+
<MacStatusPill tone="amber">可 override</MacStatusPill>
|
|
29
|
+
</>
|
|
30
|
+
}
|
|
31
|
+
title="文件预览"
|
|
32
|
+
/>
|
|
33
|
+
<MacPanel title="预览状态" description="当前 ticket 已进入 runtime,但文件内容预览仍等待平台文件接口接入。">
|
|
34
|
+
<div className="grid gap-4 md:grid-cols-[220px_minmax(0,1fr)]">
|
|
35
|
+
<div className="grid min-h-44 place-items-center rounded-2xl border border-dashed border-slate-300 bg-slate-50 text-slate-500">
|
|
36
|
+
<FileSearch size={44} />
|
|
37
|
+
</div>
|
|
38
|
+
<div className="min-w-0 space-y-3">
|
|
39
|
+
<Info label="ticket" value={ticket} />
|
|
40
|
+
<Info label="appType" value={appType || "--"} />
|
|
41
|
+
<Info label="preview" value="waiting for file metadata API" />
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</MacPanel>
|
|
45
|
+
<MacDiagnosticPanel data={{ appType, ticket, kind: "file-preview" }} title="文件预览上下文" />
|
|
46
|
+
</div>
|
|
47
|
+
) : (
|
|
48
|
+
<MacStatePage
|
|
49
|
+
description="当前链接没有携带有效 ticket。请从平台文件字段或业务页面重新打开预览。"
|
|
50
|
+
fullScreen
|
|
51
|
+
icon={<LockKeyhole size={24} />}
|
|
52
|
+
status="TICKET"
|
|
53
|
+
title="文件预览 ticket 无效"
|
|
54
|
+
/>
|
|
18
55
|
);
|
|
19
56
|
|
|
20
57
|
return (
|
|
21
|
-
<main className="min-h-screen bg-[
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
58
|
+
<main className="min-h-screen overflow-x-hidden bg-[linear-gradient(180deg,#f8fafc_0%,#eef2f7_100%)] p-5">
|
|
59
|
+
<div className="mx-auto max-w-6xl">
|
|
60
|
+
{Override ? (
|
|
61
|
+
<Override appType={appType} defaultNode={defaultNode} kind="file-preview" />
|
|
62
|
+
) : (
|
|
63
|
+
defaultNode
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
27
66
|
</main>
|
|
28
67
|
);
|
|
29
68
|
}
|
|
69
|
+
|
|
70
|
+
function Info({ label, value }: { label: string; value: string }) {
|
|
71
|
+
return (
|
|
72
|
+
<div className="flex min-w-0 items-center gap-3 rounded-2xl bg-slate-50 px-4 py-3">
|
|
73
|
+
<span className="grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-blue-50 text-blue-700">
|
|
74
|
+
{label === "ticket" ? <Ticket size={17} /> : <ShieldCheck size={17} />}
|
|
75
|
+
</span>
|
|
76
|
+
<span className="min-w-0">
|
|
77
|
+
<span className="block text-xs font-medium text-slate-500">{label}</span>
|
|
78
|
+
<span className="block truncate text-sm font-semibold text-slate-950">{value}</span>
|
|
79
|
+
</span>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|