@pactor-app/app 1.18.0 → 1.20.0
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 +43 -0
- package/dist/index.cjs +614 -0
- package/dist/index.d.cts +262 -3
- package/dist/index.d.ts +262 -3
- package/dist/index.js +609 -0
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -878,14 +878,623 @@ function EmbeddedPage({
|
|
|
878
878
|
}
|
|
879
879
|
return /* @__PURE__ */ jsx5(I18nContextProvider2, { value: i18n, children: /* @__PURE__ */ jsx5(InteractionProvider2, { bridge: assembly.uiBridge, children: content }) });
|
|
880
880
|
}
|
|
881
|
+
|
|
882
|
+
// src/sitemap/actions.ts
|
|
883
|
+
var defaultSitemapActions = {
|
|
884
|
+
"app.reload": (action) => {
|
|
885
|
+
const to = action.params?.["to"];
|
|
886
|
+
if (typeof window !== "undefined") {
|
|
887
|
+
window.location.assign(typeof to === "string" && to !== "" ? to : "/");
|
|
888
|
+
}
|
|
889
|
+
return { ok: true };
|
|
890
|
+
},
|
|
891
|
+
"service.call": async (action, ctx) => {
|
|
892
|
+
const name = action.params?.["service"];
|
|
893
|
+
if (typeof name !== "string" || name === "") {
|
|
894
|
+
return {
|
|
895
|
+
ok: false,
|
|
896
|
+
error: new Error("[@pactor-app/app] service.call \u7F3A\u5C11 params.service")
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
const handler = ctx.context.services.get(name);
|
|
900
|
+
if (!handler) {
|
|
901
|
+
return {
|
|
902
|
+
ok: false,
|
|
903
|
+
error: new Error(`[@pactor-app/app] service \u672A\u6CE8\u518C: ${name}`)
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
try {
|
|
907
|
+
const callParams = action.params?.["params"];
|
|
908
|
+
const data = await handler(
|
|
909
|
+
typeof callParams === "object" && callParams !== null ? callParams : {}
|
|
910
|
+
);
|
|
911
|
+
return { ok: true, data };
|
|
912
|
+
} catch (cause) {
|
|
913
|
+
return {
|
|
914
|
+
ok: false,
|
|
915
|
+
error: cause instanceof Error ? cause : new Error(String(cause))
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
|
|
921
|
+
// src/sitemap/SitemapApp.tsx
|
|
922
|
+
import { MenuRegistry as MenuRegistry2 } from "@pactor-app/router";
|
|
923
|
+
import { Menu } from "@pactor-app/ui/components";
|
|
924
|
+
import {
|
|
925
|
+
createContext,
|
|
926
|
+
use,
|
|
927
|
+
useEffect as useEffect4,
|
|
928
|
+
useMemo as useMemo2,
|
|
929
|
+
useState as useState4
|
|
930
|
+
} from "react";
|
|
931
|
+
|
|
932
|
+
// src/sitemap/browser-route.ts
|
|
933
|
+
import { useCallback, useMemo, useSyncExternalStore } from "react";
|
|
934
|
+
var NAV_EVENT = "pactor:sitemap-navigate";
|
|
935
|
+
function readLocation() {
|
|
936
|
+
return `${window.location.pathname}${window.location.search}`;
|
|
937
|
+
}
|
|
938
|
+
function subscribe(callback) {
|
|
939
|
+
window.addEventListener("popstate", callback);
|
|
940
|
+
window.addEventListener(NAV_EVENT, callback);
|
|
941
|
+
return () => {
|
|
942
|
+
window.removeEventListener("popstate", callback);
|
|
943
|
+
window.removeEventListener(NAV_EVENT, callback);
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
function useBrowserRoute() {
|
|
947
|
+
const location = useSyncExternalStore(
|
|
948
|
+
subscribe,
|
|
949
|
+
readLocation,
|
|
950
|
+
// SSR 快照:服务端无 window,宿主应注入 pathname / navigate
|
|
951
|
+
() => "/"
|
|
952
|
+
);
|
|
953
|
+
const navigate = useCallback((to) => {
|
|
954
|
+
window.history.pushState(null, "", to);
|
|
955
|
+
window.dispatchEvent(new Event(NAV_EVENT));
|
|
956
|
+
}, []);
|
|
957
|
+
return useMemo(() => {
|
|
958
|
+
const qIndex = location.indexOf("?");
|
|
959
|
+
return {
|
|
960
|
+
pathname: qIndex >= 0 ? location.slice(0, qIndex) : location,
|
|
961
|
+
query: Object.fromEntries(
|
|
962
|
+
new URLSearchParams(qIndex >= 0 ? location.slice(qIndex + 1) : "")
|
|
963
|
+
),
|
|
964
|
+
navigate
|
|
965
|
+
};
|
|
966
|
+
}, [location, navigate]);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// src/sitemap/http.ts
|
|
970
|
+
function createHttpHooks(options = {}) {
|
|
971
|
+
const { proxyPrefix } = options;
|
|
972
|
+
const envelope = options.envelope ?? true;
|
|
973
|
+
return {
|
|
974
|
+
request(config) {
|
|
975
|
+
let url = config.url;
|
|
976
|
+
if (proxyPrefix !== void 0 && /^https?:\/\//.test(url) && typeof window !== "undefined" && !url.startsWith(window.location.origin)) {
|
|
977
|
+
const target = new URL(url);
|
|
978
|
+
url = `${proxyPrefix}${target.pathname}${target.search}`;
|
|
979
|
+
}
|
|
980
|
+
const extra = typeof options.headers === "function" ? options.headers() : options.headers;
|
|
981
|
+
return {
|
|
982
|
+
...config,
|
|
983
|
+
url,
|
|
984
|
+
headers: { ...config.headers, ...extra }
|
|
985
|
+
};
|
|
986
|
+
},
|
|
987
|
+
response(data) {
|
|
988
|
+
if (!envelope) {
|
|
989
|
+
return data;
|
|
990
|
+
}
|
|
991
|
+
if (typeof data === "object" && data !== null && "code" in data && "data" in data) {
|
|
992
|
+
const wrapped = data;
|
|
993
|
+
if (wrapped.code !== 0) {
|
|
994
|
+
throw new Error(wrapped.message ?? `\u4E1A\u52A1\u9519\u8BEF\u7801 ${wrapped.code}`);
|
|
995
|
+
}
|
|
996
|
+
return wrapped.data;
|
|
997
|
+
}
|
|
998
|
+
return data;
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
function withHttpHooks(fetcher, hooks) {
|
|
1003
|
+
if (hooks === void 0) {
|
|
1004
|
+
return fetcher;
|
|
1005
|
+
}
|
|
1006
|
+
return (async (input, init) => {
|
|
1007
|
+
let url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
1008
|
+
let requestInit = init;
|
|
1009
|
+
if (hooks.request !== void 0) {
|
|
1010
|
+
const config = await hooks.request({
|
|
1011
|
+
method: init?.method ?? "GET",
|
|
1012
|
+
url,
|
|
1013
|
+
headers: headersToRecord(init?.headers),
|
|
1014
|
+
body: init?.body ?? void 0
|
|
1015
|
+
});
|
|
1016
|
+
url = config.url;
|
|
1017
|
+
requestInit = {
|
|
1018
|
+
...init,
|
|
1019
|
+
method: config.method ?? init?.method ?? "GET",
|
|
1020
|
+
headers: config.headers,
|
|
1021
|
+
body: config.body === void 0 ? init?.body ?? null : config.body
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
const res = await fetcher(url, requestInit);
|
|
1025
|
+
if (hooks.response === void 0 || !res.ok) {
|
|
1026
|
+
return res;
|
|
1027
|
+
}
|
|
1028
|
+
const data = await hooks.response(await res.json());
|
|
1029
|
+
return new Response(JSON.stringify(data), {
|
|
1030
|
+
status: res.status,
|
|
1031
|
+
statusText: res.statusText,
|
|
1032
|
+
headers: res.headers
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
function headersToRecord(headers) {
|
|
1037
|
+
if (headers === void 0) {
|
|
1038
|
+
return void 0;
|
|
1039
|
+
}
|
|
1040
|
+
if (headers instanceof Headers) {
|
|
1041
|
+
return Object.fromEntries(headers.entries());
|
|
1042
|
+
}
|
|
1043
|
+
if (Array.isArray(headers)) {
|
|
1044
|
+
return Object.fromEntries(headers);
|
|
1045
|
+
}
|
|
1046
|
+
return { ...headers };
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// src/sitemap/url.ts
|
|
1050
|
+
function resolveUrl(url, baseUrl) {
|
|
1051
|
+
if (baseUrl === void 0 || baseUrl === "" || /^(https?:)?\/\//i.test(url)) {
|
|
1052
|
+
return url;
|
|
1053
|
+
}
|
|
1054
|
+
return `${baseUrl.replace(/\/+$/, "")}/${url.replace(/^\/+/, "")}`;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// src/sitemap/fetch.ts
|
|
1058
|
+
async function fetchSitemap(sources, options = {}) {
|
|
1059
|
+
const fetcher = withHttpHooks(options.fetcher ?? fetch, options.httpHooks);
|
|
1060
|
+
const responses = await Promise.all(
|
|
1061
|
+
sources.map(async (url) => {
|
|
1062
|
+
const res = await fetcher(resolveUrl(url, options.baseUrl), {
|
|
1063
|
+
cache: "no-store",
|
|
1064
|
+
// 跨源 cookie 会话(服务端开通 CORS + Allow-Credentials 时生效;
|
|
1065
|
+
// 同源行为不变)
|
|
1066
|
+
credentials: "include"
|
|
1067
|
+
});
|
|
1068
|
+
if (!res.ok) {
|
|
1069
|
+
throw new Error(`[@pactor-app/app] sitemap \u6E90 ${url} \u8BF7\u6C42\u5931\u8D25\uFF1AHTTP ${res.status}`);
|
|
1070
|
+
}
|
|
1071
|
+
return await res.json();
|
|
1072
|
+
})
|
|
1073
|
+
);
|
|
1074
|
+
if (responses.length === 0) {
|
|
1075
|
+
throw new Error("[@pactor-app/app] sources \u4E3A\u7A7A\uFF1A\u8BF7\u914D\u7F6E\u81F3\u5C11\u4E00\u4E2A sitemap \u63A5\u53E3 URL");
|
|
1076
|
+
}
|
|
1077
|
+
const [first, ...rest] = responses;
|
|
1078
|
+
const merged = {
|
|
1079
|
+
...first,
|
|
1080
|
+
layouts: { ...first.layouts },
|
|
1081
|
+
pages: [...first.pages],
|
|
1082
|
+
menus: [...first.menus]
|
|
1083
|
+
};
|
|
1084
|
+
for (const source of rest) {
|
|
1085
|
+
mergeById(merged.pages, source.pages, "pages");
|
|
1086
|
+
mergeById(merged.menus, source.menus, "menus");
|
|
1087
|
+
for (const [name, layout] of Object.entries(source.layouts ?? {})) {
|
|
1088
|
+
if (merged.layouts[name] !== void 0) {
|
|
1089
|
+
console.warn(`[@pactor-app/app] sitemap layouts \u952E\u51B2\u7A81\uFF1A${name}\uFF0C\u540E\u6E90\u8986\u76D6\u524D\u6E90`);
|
|
1090
|
+
}
|
|
1091
|
+
merged.layouts[name] = layout;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
return merged;
|
|
1095
|
+
}
|
|
1096
|
+
function mergeById(target, incoming, label) {
|
|
1097
|
+
for (const item of incoming) {
|
|
1098
|
+
const index = target.findIndex(
|
|
1099
|
+
(t) => t.id === item.id || item.path !== void 0 && t.path === item.path
|
|
1100
|
+
);
|
|
1101
|
+
if (index >= 0) {
|
|
1102
|
+
console.warn(`[@pactor-app/app] sitemap ${label} \u51B2\u7A81\uFF1A${item.id}\uFF0C\u540E\u6E90\u8986\u76D6\u524D\u6E90`);
|
|
1103
|
+
target[index] = item;
|
|
1104
|
+
} else {
|
|
1105
|
+
target.push(item);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// src/sitemap/loader.ts
|
|
1111
|
+
var defaultPageEndpoint = (pageId) => `/api/pages/${pageId}`;
|
|
1112
|
+
function createSitemapLoader(sitemap, options = {}) {
|
|
1113
|
+
const pageEndpoint = options.pageEndpoint ?? defaultPageEndpoint;
|
|
1114
|
+
const sessionService = options.sessionService ?? "session.context";
|
|
1115
|
+
const fetcher = withHttpHooks(options.fetcher ?? fetch, options.httpHooks);
|
|
1116
|
+
const pageById = new Map(sitemap.pages.map((p) => [p.id, p]));
|
|
1117
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1118
|
+
return {
|
|
1119
|
+
async loadPage(pageId) {
|
|
1120
|
+
const entry = pageById.get(pageId);
|
|
1121
|
+
const version = entry?.version ?? 1;
|
|
1122
|
+
const cacheKey = `${pageId}@${version}`;
|
|
1123
|
+
const hit = cache.get(cacheKey);
|
|
1124
|
+
if (hit) {
|
|
1125
|
+
return hit;
|
|
1126
|
+
}
|
|
1127
|
+
let resource;
|
|
1128
|
+
if (entry?.pageDsl !== void 0) {
|
|
1129
|
+
resource = {
|
|
1130
|
+
id: pageId,
|
|
1131
|
+
resourceType: "page",
|
|
1132
|
+
version,
|
|
1133
|
+
dslVersion: entry.pageDsl.dsl ?? "1.0",
|
|
1134
|
+
schema: entry.pageDsl
|
|
1135
|
+
};
|
|
1136
|
+
} else {
|
|
1137
|
+
const res = await fetcher(resolveUrl(pageEndpoint(pageId), options.baseUrl), {
|
|
1138
|
+
cache: "no-store",
|
|
1139
|
+
credentials: "include"
|
|
1140
|
+
});
|
|
1141
|
+
if (!res.ok) {
|
|
1142
|
+
throw new Error(
|
|
1143
|
+
`[@pactor-app/app] \u9875\u9762 ${pageId} \u52A0\u8F7D\u5931\u8D25\uFF1AHTTP ${res.status}`
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
resource = await res.json();
|
|
1147
|
+
}
|
|
1148
|
+
if (typeof resource.schema === "object" && resource.schema !== null) {
|
|
1149
|
+
const schema = resource.schema;
|
|
1150
|
+
schema.dataSources = {
|
|
1151
|
+
session: { type: "service", service: sessionService, autoLoad: true },
|
|
1152
|
+
...schema.dataSources
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
cache.set(cacheKey, resource);
|
|
1156
|
+
return resource;
|
|
1157
|
+
},
|
|
1158
|
+
async loadFragment(fragmentId) {
|
|
1159
|
+
throw new Error(`[@pactor-app/app] \u672A\u5B9E\u73B0\u7684 fragment \u52A0\u8F7D\uFF1A${fragmentId}`);
|
|
1160
|
+
},
|
|
1161
|
+
async loadBootstrap() {
|
|
1162
|
+
return sitemap;
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/sitemap/merge.ts
|
|
1168
|
+
function mergeSitemapMenus(sitemap) {
|
|
1169
|
+
const pageById = new Map(sitemap.pages.map((p) => [p.id, p]));
|
|
1170
|
+
const walk = (items) => items.map((item) => {
|
|
1171
|
+
const page = item.pageId !== void 0 ? pageById.get(item.pageId) : void 0;
|
|
1172
|
+
const merged = {
|
|
1173
|
+
...item,
|
|
1174
|
+
path: item.path ?? page?.path,
|
|
1175
|
+
title: item.title ?? page?.title ?? item.id
|
|
1176
|
+
};
|
|
1177
|
+
if (item.children !== void 0) {
|
|
1178
|
+
merged.children = walk(item.children);
|
|
1179
|
+
}
|
|
1180
|
+
return merged;
|
|
1181
|
+
});
|
|
1182
|
+
return walk(sitemap.menus ?? []);
|
|
1183
|
+
}
|
|
1184
|
+
function toSitemapNavItems(menuTree) {
|
|
1185
|
+
return menuTree.map((item) => ({
|
|
1186
|
+
key: item.path ?? item.id,
|
|
1187
|
+
label: item.title,
|
|
1188
|
+
icon: item.icon,
|
|
1189
|
+
disabled: item.disabled,
|
|
1190
|
+
children: item.children !== void 0 ? toSitemapNavItems(item.children) : void 0
|
|
1191
|
+
}));
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
// src/sitemap/route.ts
|
|
1195
|
+
function resolveSitemapRoute(sitemap, registry, pathname) {
|
|
1196
|
+
const match = registry.findByPath(pathname);
|
|
1197
|
+
const menu = match?.menu ?? null;
|
|
1198
|
+
if (match === null || menu === null || menu.pageId === void 0) {
|
|
1199
|
+
return { menu, route: null };
|
|
1200
|
+
}
|
|
1201
|
+
const layoutName = menu.layout ?? sitemap.layout ?? Object.keys(sitemap.layouts)[0] ?? "";
|
|
1202
|
+
const highlightId = menu.activeMenu ?? menu.id;
|
|
1203
|
+
const highlightItem = registry.findById(highlightId);
|
|
1204
|
+
return {
|
|
1205
|
+
menu,
|
|
1206
|
+
route: {
|
|
1207
|
+
pageId: menu.pageId,
|
|
1208
|
+
layoutName,
|
|
1209
|
+
highlightKey: highlightItem?.path ?? highlightId,
|
|
1210
|
+
params: match.params
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
var defaultSitemapGuard = ({
|
|
1215
|
+
sitemap,
|
|
1216
|
+
pathname,
|
|
1217
|
+
menu
|
|
1218
|
+
}) => {
|
|
1219
|
+
const loginPath = sitemap.auth?.loginPath ?? "/login";
|
|
1220
|
+
if (sitemap.user === void 0 && pathname !== loginPath && (menu === null || menu.public !== true)) {
|
|
1221
|
+
return { allow: false, redirectTo: loginPath };
|
|
1222
|
+
}
|
|
1223
|
+
return { allow: true };
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
// src/sitemap/session.ts
|
|
1227
|
+
function createSitemapSessionServices(session, serviceName = "session.context") {
|
|
1228
|
+
return {
|
|
1229
|
+
[serviceName]: () => Promise.resolve(session)
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// src/sitemap/SitemapApp.tsx
|
|
1234
|
+
import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1235
|
+
var OutletContext = createContext(null);
|
|
1236
|
+
function PageOutlet() {
|
|
1237
|
+
return use(OutletContext);
|
|
1238
|
+
}
|
|
1239
|
+
var NavHighlightContext = createContext("");
|
|
1240
|
+
function SitemapMenu({
|
|
1241
|
+
items,
|
|
1242
|
+
collapsible,
|
|
1243
|
+
onSelect
|
|
1244
|
+
}) {
|
|
1245
|
+
const highlight = use(NavHighlightContext);
|
|
1246
|
+
return /* @__PURE__ */ jsx6(
|
|
1247
|
+
Menu,
|
|
1248
|
+
{
|
|
1249
|
+
items,
|
|
1250
|
+
collapsible,
|
|
1251
|
+
selectedKey: highlight,
|
|
1252
|
+
onSelect
|
|
1253
|
+
}
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
function SitemapApp(props) {
|
|
1257
|
+
const { sources, fetcher, baseUrl } = props;
|
|
1258
|
+
const browserRoute = useBrowserRoute();
|
|
1259
|
+
const pathname = props.pathname ?? browserRoute.pathname;
|
|
1260
|
+
const query = props.query ?? browserRoute.query;
|
|
1261
|
+
const navigate = props.navigate ?? browserRoute.navigate;
|
|
1262
|
+
const [state, setState] = useState4({ status: "loading" });
|
|
1263
|
+
useEffect4(() => {
|
|
1264
|
+
let cancelled = false;
|
|
1265
|
+
setState({ status: "loading" });
|
|
1266
|
+
fetchSitemap(sources, {
|
|
1267
|
+
fetcher,
|
|
1268
|
+
baseUrl,
|
|
1269
|
+
httpHooks: props.httpAdapter
|
|
1270
|
+
}).then((sitemap2) => {
|
|
1271
|
+
if (!cancelled) {
|
|
1272
|
+
setState({ status: "ready", sitemap: sitemap2 });
|
|
1273
|
+
}
|
|
1274
|
+
}).catch((error) => {
|
|
1275
|
+
if (!cancelled) {
|
|
1276
|
+
setState({
|
|
1277
|
+
status: "error",
|
|
1278
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
return () => {
|
|
1283
|
+
cancelled = true;
|
|
1284
|
+
};
|
|
1285
|
+
}, [fetcher, baseUrl, props.httpAdapter, JSON.stringify(sources)]);
|
|
1286
|
+
const registry = useMemo2(
|
|
1287
|
+
() => state.status === "ready" ? new MenuRegistry2(mergeSitemapMenus(state.sitemap)) : null,
|
|
1288
|
+
[state]
|
|
1289
|
+
);
|
|
1290
|
+
const resolution = useMemo2(
|
|
1291
|
+
() => state.status === "ready" && registry !== null ? resolveSitemapRoute(state.sitemap, registry, pathname) : null,
|
|
1292
|
+
[state, registry, pathname]
|
|
1293
|
+
);
|
|
1294
|
+
const sitemap = state.status === "ready" ? state.sitemap : null;
|
|
1295
|
+
const guard = props.guard ?? defaultSitemapGuard;
|
|
1296
|
+
const decision = useMemo2(
|
|
1297
|
+
() => sitemap !== null && resolution !== null ? guard({ sitemap, pathname, menu: resolution.menu }) : { allow: true },
|
|
1298
|
+
[sitemap, resolution, guard, pathname]
|
|
1299
|
+
);
|
|
1300
|
+
useEffect4(() => {
|
|
1301
|
+
if (!decision.allow) {
|
|
1302
|
+
navigate(decision.redirectTo);
|
|
1303
|
+
}
|
|
1304
|
+
}, [decision, navigate]);
|
|
1305
|
+
if (state.status === "loading") {
|
|
1306
|
+
return /* @__PURE__ */ jsx6(Centered, { children: props.loading ?? "\u52A0\u8F7D\u4E2D\u2026" });
|
|
1307
|
+
}
|
|
1308
|
+
if (state.status === "error") {
|
|
1309
|
+
return props.renderError !== void 0 ? /* @__PURE__ */ jsx6(Fragment, { children: props.renderError(state.error) }) : /* @__PURE__ */ jsxs3(Centered, { children: [
|
|
1310
|
+
"sitemap \u52A0\u8F7D\u5931\u8D25\uFF1A",
|
|
1311
|
+
state.error.message
|
|
1312
|
+
] });
|
|
1313
|
+
}
|
|
1314
|
+
if (sitemap === null || registry === null || resolution === null) {
|
|
1315
|
+
return null;
|
|
1316
|
+
}
|
|
1317
|
+
if (!decision.allow) {
|
|
1318
|
+
return /* @__PURE__ */ jsx6(Centered, { children: "\u91CD\u5B9A\u5411\u2026" });
|
|
1319
|
+
}
|
|
1320
|
+
if (resolution.route === null) {
|
|
1321
|
+
return props.renderNotFound !== void 0 ? /* @__PURE__ */ jsx6(Fragment, { children: props.renderNotFound(pathname) }) : /* @__PURE__ */ jsxs3(Centered, { children: [
|
|
1322
|
+
"\u9875\u9762\u4E0D\u5B58\u5728\uFF08",
|
|
1323
|
+
pathname,
|
|
1324
|
+
"\uFF09"
|
|
1325
|
+
] });
|
|
1326
|
+
}
|
|
1327
|
+
const { pageId, layoutName, highlightKey, params } = resolution.route;
|
|
1328
|
+
return (
|
|
1329
|
+
// key 只随布局名变化:切换导航(同布局)时布局常驻,
|
|
1330
|
+
// 仅 PageOutlet 内的页面随路由重建
|
|
1331
|
+
/* @__PURE__ */ jsx6(NavHighlightContext.Provider, { value: highlightKey, children: /* @__PURE__ */ jsx6(
|
|
1332
|
+
SitemapLayout,
|
|
1333
|
+
{
|
|
1334
|
+
sitemap,
|
|
1335
|
+
registry,
|
|
1336
|
+
pageId,
|
|
1337
|
+
layoutName,
|
|
1338
|
+
pathname,
|
|
1339
|
+
query,
|
|
1340
|
+
navigate,
|
|
1341
|
+
routeParams: params,
|
|
1342
|
+
props
|
|
1343
|
+
},
|
|
1344
|
+
layoutName
|
|
1345
|
+
) })
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
function SitemapLayout({
|
|
1349
|
+
sitemap,
|
|
1350
|
+
registry,
|
|
1351
|
+
pageId,
|
|
1352
|
+
layoutName,
|
|
1353
|
+
pathname,
|
|
1354
|
+
query,
|
|
1355
|
+
navigate: hostNavigate,
|
|
1356
|
+
routeParams,
|
|
1357
|
+
props
|
|
1358
|
+
}) {
|
|
1359
|
+
const sessionService = props.sessionService ?? "session.context";
|
|
1360
|
+
const session = useMemo2(
|
|
1361
|
+
() => ({
|
|
1362
|
+
user: sitemap.user,
|
|
1363
|
+
app: { name: "Pactor App", ...sitemap.app },
|
|
1364
|
+
menus: toSitemapNavItems(registry.tree())
|
|
1365
|
+
}),
|
|
1366
|
+
[sitemap, registry]
|
|
1367
|
+
);
|
|
1368
|
+
const navigate = useMemo2(
|
|
1369
|
+
() => (to) => {
|
|
1370
|
+
if (/^https?:\/\//.test(to)) {
|
|
1371
|
+
window.open(to, "_blank", "noopener");
|
|
1372
|
+
} else {
|
|
1373
|
+
hostNavigate(to);
|
|
1374
|
+
}
|
|
1375
|
+
},
|
|
1376
|
+
[hostNavigate]
|
|
1377
|
+
);
|
|
1378
|
+
const loader = useMemo2(
|
|
1379
|
+
() => createSitemapLoader(sitemap, {
|
|
1380
|
+
pageEndpoint: props.pageEndpoint,
|
|
1381
|
+
fetcher: props.fetcher,
|
|
1382
|
+
sessionService,
|
|
1383
|
+
baseUrl: props.baseUrl,
|
|
1384
|
+
httpHooks: props.httpAdapter
|
|
1385
|
+
}),
|
|
1386
|
+
[
|
|
1387
|
+
sitemap,
|
|
1388
|
+
props.pageEndpoint,
|
|
1389
|
+
props.fetcher,
|
|
1390
|
+
props.baseUrl,
|
|
1391
|
+
props.httpAdapter,
|
|
1392
|
+
sessionService
|
|
1393
|
+
]
|
|
1394
|
+
);
|
|
1395
|
+
const services = useMemo2(
|
|
1396
|
+
() => ({
|
|
1397
|
+
...createSitemapSessionServices(session, sessionService),
|
|
1398
|
+
...props.services
|
|
1399
|
+
}),
|
|
1400
|
+
[session, sessionService, props.services]
|
|
1401
|
+
);
|
|
1402
|
+
const actions = useMemo2(
|
|
1403
|
+
() => ({ ...defaultSitemapActions, ...props.actions }),
|
|
1404
|
+
[props.actions]
|
|
1405
|
+
);
|
|
1406
|
+
const layoutDsl = sitemap.layouts[layoutName];
|
|
1407
|
+
const layoutPage = useMemo2(() => {
|
|
1408
|
+
if (layoutDsl === void 0) {
|
|
1409
|
+
return null;
|
|
1410
|
+
}
|
|
1411
|
+
return {
|
|
1412
|
+
dsl: layoutDsl.dsl,
|
|
1413
|
+
page: { id: `layout:${layoutName}`, title: layoutName },
|
|
1414
|
+
dataSources: {
|
|
1415
|
+
session: { type: "service", service: sessionService, autoLoad: true }
|
|
1416
|
+
},
|
|
1417
|
+
body: layoutDsl.body
|
|
1418
|
+
};
|
|
1419
|
+
}, [layoutDsl, layoutName, sessionService]);
|
|
1420
|
+
if (layoutDsl === void 0 || layoutPage === null) {
|
|
1421
|
+
return /* @__PURE__ */ jsxs3(Centered, { children: [
|
|
1422
|
+
"\u5E03\u5C40\u672A\u5B9A\u4E49\uFF1A",
|
|
1423
|
+
layoutName
|
|
1424
|
+
] });
|
|
1425
|
+
}
|
|
1426
|
+
return /* @__PURE__ */ jsx6(
|
|
1427
|
+
OutletContext.Provider,
|
|
1428
|
+
{
|
|
1429
|
+
value: (
|
|
1430
|
+
// 页面实例:key 随 pageId + pathname 变化(route 参数变化需重建,
|
|
1431
|
+
// PactorPage 的 route prop 不参与运行时重建)——布局保持挂载
|
|
1432
|
+
/* @__PURE__ */ jsx6(
|
|
1433
|
+
PactorPage,
|
|
1434
|
+
{
|
|
1435
|
+
pageId,
|
|
1436
|
+
route: { params: routeParams, query },
|
|
1437
|
+
loader,
|
|
1438
|
+
services,
|
|
1439
|
+
actions,
|
|
1440
|
+
permissions: sitemap.permissions ?? [],
|
|
1441
|
+
navigate,
|
|
1442
|
+
httpAdapter: props.httpAdapter,
|
|
1443
|
+
components: props.components
|
|
1444
|
+
},
|
|
1445
|
+
`${pageId}@${pathname}`
|
|
1446
|
+
)
|
|
1447
|
+
),
|
|
1448
|
+
children: /* @__PURE__ */ jsx6(
|
|
1449
|
+
PactorPage,
|
|
1450
|
+
{
|
|
1451
|
+
pageDsl: layoutPage,
|
|
1452
|
+
services,
|
|
1453
|
+
actions,
|
|
1454
|
+
navigate,
|
|
1455
|
+
httpAdapter: props.httpAdapter,
|
|
1456
|
+
components: { PageOutlet, SitemapMenu, ...props.components }
|
|
1457
|
+
}
|
|
1458
|
+
)
|
|
1459
|
+
}
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
function Centered({ children }) {
|
|
1463
|
+
return /* @__PURE__ */ jsx6(
|
|
1464
|
+
"div",
|
|
1465
|
+
{
|
|
1466
|
+
style: {
|
|
1467
|
+
minHeight: "60vh",
|
|
1468
|
+
display: "flex",
|
|
1469
|
+
flexDirection: "column",
|
|
1470
|
+
alignItems: "center",
|
|
1471
|
+
justifyContent: "center",
|
|
1472
|
+
gap: 8,
|
|
1473
|
+
color: "var(--muted-foreground, #6b7280)"
|
|
1474
|
+
},
|
|
1475
|
+
children
|
|
1476
|
+
}
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
881
1479
|
export {
|
|
882
1480
|
Application,
|
|
883
1481
|
DslPageLoader,
|
|
884
1482
|
PactorPage,
|
|
885
1483
|
PageErrorBoundary,
|
|
886
1484
|
ReactRouterBinding,
|
|
1485
|
+
SitemapApp,
|
|
1486
|
+
createHttpHooks,
|
|
887
1487
|
createPactorApplication,
|
|
888
1488
|
createPactorPage,
|
|
889
1489
|
createRuntimeHttpAdapter,
|
|
1490
|
+
createSitemapLoader,
|
|
1491
|
+
createSitemapSessionServices,
|
|
1492
|
+
defaultSitemapActions,
|
|
1493
|
+
defaultSitemapGuard,
|
|
1494
|
+
fetchSitemap,
|
|
1495
|
+
mergeSitemapMenus,
|
|
1496
|
+
resolveSitemapRoute,
|
|
1497
|
+
toSitemapNavItems,
|
|
1498
|
+
withHttpHooks,
|
|
890
1499
|
wrapHttpAdapter
|
|
891
1500
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pactor-app/app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "Pactor application: one-stop composition layer (createPactorApplication) wiring loader, bootstrap, registries, router, layout, interaction and page runtimes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pactor",
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@pactor-app/core": "1.
|
|
50
|
-
"@pactor-app/
|
|
51
|
-
"@pactor-app/
|
|
52
|
-
"@pactor-app/
|
|
53
|
-
"@pactor-app/
|
|
54
|
-
"@pactor-app/
|
|
55
|
-
"@pactor-app/
|
|
56
|
-
"@pactor-app/
|
|
49
|
+
"@pactor-app/core": "1.20.0",
|
|
50
|
+
"@pactor-app/permission": "1.20.0",
|
|
51
|
+
"@pactor-app/resource": "1.20.0",
|
|
52
|
+
"@pactor-app/schema": "1.20.0",
|
|
53
|
+
"@pactor-app/runtime": "1.20.0",
|
|
54
|
+
"@pactor-app/ui": "1.20.0",
|
|
55
|
+
"@pactor-app/expression": "1.20.0",
|
|
56
|
+
"@pactor-app/router": "1.20.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"react": ">=18",
|