openxiangda 1.0.49 → 1.0.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/lib/cli.js +339 -9
  2. package/lib/workspace-init.js +20 -8
  3. package/openxiangda-skills/SKILL.md +4 -3
  4. package/openxiangda-skills/skills/openxiangda-app/SKILL.md +28 -0
  5. package/openxiangda-skills/skills/openxiangda-core/SKILL.md +45 -1
  6. package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +31 -0
  7. package/package.json +7 -1
  8. package/packages/sdk/dist/runtime/index.cjs +3614 -3377
  9. package/packages/sdk/dist/runtime/index.cjs.map +1 -1
  10. package/packages/sdk/dist/runtime/index.d.mts +1 -0
  11. package/packages/sdk/dist/runtime/index.d.ts +1 -0
  12. package/packages/sdk/dist/runtime/index.mjs +3103 -2860
  13. package/packages/sdk/dist/runtime/index.mjs.map +1 -1
  14. package/packages/sdk/dist/runtime/react.cjs +236 -0
  15. package/packages/sdk/dist/runtime/react.cjs.map +1 -0
  16. package/packages/sdk/dist/runtime/react.d.mts +109 -0
  17. package/packages/sdk/dist/runtime/react.d.ts +109 -0
  18. package/packages/sdk/dist/runtime/react.mjs +222 -0
  19. package/packages/sdk/dist/runtime/react.mjs.map +1 -0
  20. package/templates/openxiangda-react-spa/.env.example +4 -0
  21. package/templates/openxiangda-react-spa/AGENTS.md +65 -0
  22. package/templates/openxiangda-react-spa/index.html +12 -0
  23. package/templates/openxiangda-react-spa/package.json +35 -0
  24. package/templates/openxiangda-react-spa/postcss.config.cjs +6 -0
  25. package/templates/openxiangda-react-spa/src/app/router.tsx +97 -0
  26. package/templates/openxiangda-react-spa/src/layouts/AdminShell.tsx +102 -0
  27. package/templates/openxiangda-react-spa/src/layouts/PublicShell.tsx +11 -0
  28. package/templates/openxiangda-react-spa/src/layouts/UserShell.tsx +22 -0
  29. package/templates/openxiangda-react-spa/src/main.tsx +12 -0
  30. package/templates/openxiangda-react-spa/src/pages/admin/RuntimeWorkspacePage.tsx +57 -0
  31. package/templates/openxiangda-react-spa/src/pages/defaults/DataRoutePage.tsx +17 -0
  32. package/templates/openxiangda-react-spa/src/pages/defaults/FilePreviewRoutePage.tsx +14 -0
  33. package/templates/openxiangda-react-spa/src/pages/defaults/FormRoutePage.tsx +62 -0
  34. package/templates/openxiangda-react-spa/src/pages/portal/UserPortalPage.tsx +27 -0
  35. package/templates/openxiangda-react-spa/src/pages/public/PublicHomePage.tsx +10 -0
  36. package/templates/openxiangda-react-spa/src/pages/states/NotFoundPage.tsx +16 -0
  37. package/templates/openxiangda-react-spa/src/resources/menus/menus.json +31 -0
  38. package/templates/openxiangda-react-spa/src/resources/permissions/page-groups/app-admin.json +8 -0
  39. package/templates/openxiangda-react-spa/src/resources/permissions/page-groups/app-user.json +8 -0
  40. package/templates/openxiangda-react-spa/src/resources/roles/roles.json +14 -0
  41. package/templates/openxiangda-react-spa/src/styles/index.css +23 -0
  42. package/templates/openxiangda-react-spa/tailwind.config.cjs +29 -0
  43. package/templates/openxiangda-react-spa/tsconfig.app.json +36 -0
  44. package/templates/openxiangda-react-spa/tsconfig.json +7 -0
  45. package/templates/openxiangda-react-spa/tsconfig.node.json +10 -0
  46. package/templates/openxiangda-react-spa/vite.config.ts +73 -0
  47. package/templates/sy-lowcode-app-workspace/src/dev/App.tsx +33 -1
@@ -0,0 +1,8 @@
1
+ {
2
+ "code": "app_admin_spa_pages",
3
+ "name": "应用管理员 SPA 页面",
4
+ "roles": ["app_admin"],
5
+ "menuCodes": ["runtime_workspace", "user_portal", "public_home"],
6
+ "routeCodes": ["admin.dashboard", "portal.home", "public.home"],
7
+ "pathPatterns": ["/view/:appType/admin/*", "/view/:appType/portal/*", "/view/:appType/public/*"]
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "code": "app_user_spa_pages",
3
+ "name": "普通用户 SPA 页面",
4
+ "roles": ["app_user"],
5
+ "menuCodes": ["user_portal", "public_home"],
6
+ "routeCodes": ["portal.home", "public.home"],
7
+ "pathPatterns": ["/view/:appType/portal/*", "/view/:appType/public/*"]
8
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "roles": [
3
+ {
4
+ "code": "app_admin",
5
+ "name": "应用管理员",
6
+ "description": "可访问 mac-admin 后台与全部 React SPA 路由"
7
+ },
8
+ {
9
+ "code": "app_user",
10
+ "name": "普通用户",
11
+ "description": "可访问用户门户和被授权的业务页面"
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,23 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ html,
6
+ body,
7
+ #root {
8
+ min-height: 100%;
9
+ }
10
+
11
+ body {
12
+ margin: 0;
13
+ font-family:
14
+ Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
15
+ sans-serif;
16
+ background: #f6f7f9;
17
+ }
18
+
19
+ .ox-panel {
20
+ border: 1px solid rgb(226 232 240);
21
+ border-radius: 8px;
22
+ background: rgb(255 255 255 / 0.92);
23
+ }
@@ -0,0 +1,29 @@
1
+ const path = require("node:path");
2
+ const openxiangdaPresetModule = require("openxiangda/tailwind-preset");
3
+ const openxiangdaPreset =
4
+ openxiangdaPresetModule.default ?? openxiangdaPresetModule;
5
+
6
+ function resolveOpenXiangdaContent() {
7
+ try {
8
+ const packagePath = require.resolve("openxiangda");
9
+ const distDir = path.dirname(packagePath);
10
+ return [path.join(distDir, "..", "**/*.{js,mjs,cjs}")];
11
+ } catch {
12
+ return [];
13
+ }
14
+ }
15
+
16
+ /** @type {import('tailwindcss').Config} */
17
+ module.exports = {
18
+ content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}", ...resolveOpenXiangdaContent()],
19
+ blocklist: ["[-:T]", "[-:TZ.]"],
20
+ presets: [openxiangdaPreset],
21
+ theme: {
22
+ extend: {
23
+ colors: {
24
+ graphite: "#1f2937",
25
+ },
26
+ },
27
+ },
28
+ plugins: [],
29
+ };
@@ -0,0 +1,36 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "allowJs": false,
7
+ "skipLibCheck": true,
8
+ "esModuleInterop": true,
9
+ "allowSyntheticDefaultImports": true,
10
+ "strict": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "module": "ESNext",
13
+ "moduleResolution": "Bundler",
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "noEmit": true,
17
+ "jsx": "react-jsx",
18
+ "baseUrl": ".",
19
+ "paths": {
20
+ "@/*": ["src/*"],
21
+ "openxiangda": [
22
+ "../../packages/sdk/src/components/index.ts",
23
+ "node_modules/openxiangda"
24
+ ],
25
+ "openxiangda/runtime": [
26
+ "../../packages/sdk/src/runtime/index.ts",
27
+ "node_modules/openxiangda/runtime"
28
+ ],
29
+ "openxiangda/runtime/react": [
30
+ "../../packages/sdk/src/runtime/react/openxiangdaProvider.tsx",
31
+ "node_modules/openxiangda/runtime/react"
32
+ ]
33
+ }
34
+ },
35
+ "include": ["src", "vite.config.ts"]
36
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ]
7
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "allowSyntheticDefaultImports": true
8
+ },
9
+ "include": ["vite.config.ts"]
10
+ }
@@ -0,0 +1,73 @@
1
+ import react from "@vitejs/plugin-react";
2
+ import { fileURLToPath } from "node:url";
3
+ import { defineConfig, loadEnv } from "vite";
4
+
5
+ const resolveProxyTarget = (value?: string) => {
6
+ const normalized = String(value || "").trim();
7
+ if (!normalized) return "";
8
+ try {
9
+ return new URL(normalized).origin;
10
+ } catch {
11
+ return "";
12
+ }
13
+ };
14
+
15
+ const toPort = (value?: string) => {
16
+ const port = Number(value || 5174);
17
+ return Number.isFinite(port) && port > 0 ? port : 5174;
18
+ };
19
+
20
+ export default defineConfig(({ mode }) => {
21
+ const env = loadEnv(mode, process.cwd(), "");
22
+ const serviceTarget = resolveProxyTarget(
23
+ env.OPENXIANGDA_BASE_URL || env.APP_PLATFORM_URL,
24
+ );
25
+ const servicePrefix = env.APP_SERVICE_PREFIX || "/service";
26
+ const appType = env.OPENXIANGDA_APP_TYPE || env.APP_TYPE || "";
27
+ const buildId = env.OPENXIANGDA_BUILD_ID || "";
28
+ const runtimeAssetBase =
29
+ env.OPENXIANGDA_RUNTIME_ASSET_BASE ||
30
+ (appType && buildId
31
+ ? `${servicePrefix.replace(/\/+$/, "")}/openxiangda-api/v1/apps/${encodeURIComponent(appType)}/runtime/releases/by-build/${encodeURIComponent(buildId)}/files/`
32
+ : "/");
33
+
34
+ return {
35
+ base: runtimeAssetBase,
36
+ define: {
37
+ "process.env.OPENXIANGDA_APP_TYPE": JSON.stringify(appType),
38
+ "process.env.APP_TYPE": JSON.stringify(appType),
39
+ "process.env.OPENXIANGDA_BASE_URL": JSON.stringify(
40
+ env.OPENXIANGDA_BASE_URL || env.APP_PLATFORM_URL || "",
41
+ ),
42
+ "process.env.APP_SERVICE_PREFIX": JSON.stringify(servicePrefix),
43
+ },
44
+ plugins: [react()],
45
+ resolve: {
46
+ alias: [
47
+ {
48
+ find: "@",
49
+ replacement: fileURLToPath(new URL("./src", import.meta.url)),
50
+ },
51
+ ],
52
+ dedupe: ["react", "react-dom"],
53
+ },
54
+ server: {
55
+ host: env.OPENXIANGDA_DEV_HOST || "127.0.0.1",
56
+ port: toPort(env.OPENXIANGDA_DEV_PORT),
57
+ proxy: serviceTarget
58
+ ? {
59
+ [servicePrefix]: {
60
+ target: serviceTarget,
61
+ changeOrigin: true,
62
+ secure: false,
63
+ cookieDomainRewrite: "",
64
+ cookiePathRewrite: "/",
65
+ headers: {
66
+ "x-openxiangda-dev-proxy": "1",
67
+ },
68
+ },
69
+ }
70
+ : undefined,
71
+ },
72
+ };
73
+ });
@@ -412,6 +412,23 @@ const findPageByIdentity = (pages: PageOption[], identity: string) =>
412
412
  page.dirName === identity,
413
413
  );
414
414
 
415
+ const getRuntimePermission = (route?: BrowserRuntimeRouteResolution | null) => {
416
+ const permission = route?.runtime?.permission;
417
+ return permission && typeof permission === "object"
418
+ ? (permission as { canView?: unknown; message?: unknown })
419
+ : undefined;
420
+ };
421
+
422
+ const isRuntimePermissionDenied = (route?: BrowserRuntimeRouteResolution | null) =>
423
+ getRuntimePermission(route)?.canView === false;
424
+
425
+ const getRuntimePermissionMessage = (route: BrowserRuntimeRouteResolution) =>
426
+ String(
427
+ route.message ||
428
+ getRuntimePermission(route)?.message ||
429
+ "您没有权限访问当前页面",
430
+ );
431
+
415
432
  const RuntimeRouteNotice = ({
416
433
  route,
417
434
  routeError,
@@ -446,6 +463,17 @@ const RuntimeRouteNotice = ({
446
463
 
447
464
  if (!route) return null;
448
465
 
466
+ if (isRuntimePermissionDenied(route)) {
467
+ return (
468
+ <Alert
469
+ type="warning"
470
+ showIcon
471
+ message={getRuntimePermissionMessage(route)}
472
+ style={{ margin: 16 }}
473
+ />
474
+ );
475
+ }
476
+
449
477
  if (route.mode === "builtin-route") {
450
478
  return null;
451
479
  }
@@ -500,11 +528,15 @@ export default function App() {
500
528
  );
501
529
 
502
530
  const shouldBlockLocalMount =
531
+ isRuntimePermissionDenied(routeResolution) ||
503
532
  routeResolution?.mode === "builtin-route" ||
504
533
  routeResolution?.kind === "work-center" ||
505
534
  (routeResolution?.mode === "not-found" &&
506
535
  routeResolution.params?.routeAppType !== undefined);
507
536
  const shouldRenderBuiltinRoute = routeResolution?.mode === "builtin-route";
537
+ const localMountBlockedDescription = isRuntimePermissionDenied(routeResolution)
538
+ ? "当前用户没有权限访问该页面"
539
+ : "当前路由不挂载本地代码页";
508
540
 
509
541
  const handleSelectPage = (value: string) => {
510
542
  setSelectedCode(value);
@@ -774,7 +806,7 @@ export default function App() {
774
806
  <section style={{ minHeight: "calc(100vh - 73px)" }}>
775
807
  {shouldBlockLocalMount ? (
776
808
  <div style={{ padding: 16 }}>
777
- <Empty description="当前路由不挂载本地代码页" />
809
+ <Empty description={localMountBlockedDescription} />
778
810
  </div>
779
811
  ) : !selected && !selectedCode ? (
780
812
  <Spin style={{ margin: 32 }} />