openxiangda 1.0.51 → 1.0.53

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.51",
3
+ "version": "1.0.53",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  import fs from "node:fs";
17
17
  import path from "node:path";
18
18
  import { pathToFileURL } from "node:url";
19
- import { buildSync } from "esbuild";
19
+ import { build } from "esbuild";
20
20
  import { loadConfig, getApiBaseUrl, rootDir } from "./utils/load-config.mjs";
21
21
  import {
22
22
  ensureSchemaFormUuid,
@@ -79,6 +79,36 @@ sync-schema - 将表单 Schema 同步到后端 API
79
79
  `);
80
80
  }
81
81
 
82
+ function resolveWorkspaceAlias(importPath) {
83
+ if (!importPath.startsWith("@/")) return null;
84
+ const basePath = path.join(rootDir, "src", importPath.slice(2));
85
+ const candidates = [
86
+ basePath,
87
+ `${basePath}.ts`,
88
+ `${basePath}.tsx`,
89
+ `${basePath}.js`,
90
+ `${basePath}.jsx`,
91
+ `${basePath}.mjs`,
92
+ path.join(basePath, "index.ts"),
93
+ path.join(basePath, "index.tsx"),
94
+ path.join(basePath, "index.js"),
95
+ path.join(basePath, "index.jsx"),
96
+ path.join(basePath, "index.mjs"),
97
+ ];
98
+ return candidates.find((candidate) => fs.existsSync(candidate)) || basePath;
99
+ }
100
+
101
+ function workspaceAliasPlugin() {
102
+ return {
103
+ name: "openxiangda-workspace-alias",
104
+ setup(build) {
105
+ build.onResolve({ filter: /^@\// }, (args) => ({
106
+ path: resolveWorkspaceAlias(args.path),
107
+ }));
108
+ },
109
+ };
110
+ }
111
+
82
112
  // ---------- 表单发现 ----------
83
113
 
84
114
  function discoverForms(filterName) {
@@ -111,12 +141,13 @@ async function loadSchema(schemaPath) {
111
141
  const tmpFile = schemaPath.replace(/\.ts$/, ".tmp.mjs");
112
142
  try {
113
143
  // 使用 esbuild 将 .ts 打包为单文件 .mjs(解析所有依赖),再动态 import
114
- const result = buildSync({
144
+ const result = await build({
115
145
  entryPoints: [schemaPath],
116
146
  bundle: true,
117
147
  format: "esm",
118
148
  platform: "node",
119
149
  target: "node18",
150
+ plugins: [workspaceAliasPlugin()],
120
151
  write: false,
121
152
  outfile: "out.mjs",
122
153
  });
@@ -18,6 +18,7 @@ pnpm install
18
18
  pnpm dev
19
19
  pnpm typecheck
20
20
  pnpm build
21
+ openxiangda workspace publish --profile <name> --form <formCode>
21
22
  openxiangda resource plan --profile <name>
22
23
  openxiangda resource publish --profile <name>
23
24
  openxiangda runtime deploy --profile <name>
@@ -28,10 +29,13 @@ openxiangda runtime deploy --profile <name>
28
29
  完整发布顺序:
29
30
 
30
31
  ```bash
32
+ openxiangda workspace publish --profile <name> --form <formCode>
31
33
  openxiangda resource publish --profile <name>
32
34
  openxiangda runtime deploy --profile <name>
33
35
  ```
34
36
 
37
+ 如果应用需要平台表单、流程表单、详情页或数据列表页,在 `src/forms/<formCode>/schema.ts` 定义表单 schema,并用 `openxiangda workspace publish --form <formCode>` 发布。React SPA 自身继续用 `runtime deploy` 发布。
38
+
35
39
  ## 权限资源
36
40
 
37
41
  React SPA 页面需要声明菜单 code、route code 和 path pattern:
@@ -0,0 +1,32 @@
1
+ import { defineAppWorkspaceConfig } from "openxiangda/build";
2
+
3
+ export default defineAppWorkspaceConfig({
4
+ appType: process.env.APP_TYPE || process.env.OPENXIANGDA_APP_TYPE || "APP_XXXXXXXXXXXXXXXX",
5
+ appName: process.env.APP_NAME || "OpenXiangda React SPA",
6
+ platformUrl:
7
+ process.env.APP_PLATFORM_URL ||
8
+ process.env.OPENXIANGDA_BASE_URL ||
9
+ "https://yida.wisejob.cn/service",
10
+ servicePrefix: process.env.APP_SERVICE_PREFIX || "/service",
11
+ appKey: process.env.APP_KEY || "",
12
+ appSecret: process.env.APP_SECRET || "",
13
+ userId: process.env.APP_USER_ID || "",
14
+ version: process.env.APP_VERSION || "0.1.0",
15
+ buildId: process.env.APP_BUILD_ID || "",
16
+ oss: {
17
+ region: process.env.APP_OSS_REGION || "oss-cn-hangzhou",
18
+ bucket: process.env.APP_OSS_BUCKET || "sy-app-workspace-dev",
19
+ accessKeyId: process.env.APP_OSS_ACCESS_KEY_ID || "",
20
+ accessKeySecret: process.env.APP_OSS_ACCESS_KEY_SECRET || "",
21
+ pathPrefix: process.env.APP_OSS_PATH_PREFIX || "app-workspace",
22
+ },
23
+ defaults: {
24
+ protocolVersion: process.env.APP_PAGE_PROTOCOL_VERSION || "1.0",
25
+ frameworkVersion: process.env.APP_FRAMEWORK_VERSION || "18.3.1",
26
+ cssIsolation: "none",
27
+ formMenuParentId: process.env.APP_FORM_MENU_PARENT_ID || "",
28
+ formMenuIcon: process.env.APP_FORM_MENU_ICON || "",
29
+ pageMenuParentId: process.env.APP_PAGE_MENU_PARENT_ID || "",
30
+ pageMenuIcon: process.env.APP_PAGE_MENU_ICON || "",
31
+ },
32
+ });
@@ -6,6 +6,10 @@
6
6
  "scripts": {
7
7
  "dev": "vite",
8
8
  "build": "vite build",
9
+ "build:forms": "lowcode-workspace build-forms",
10
+ "sync-schema": "lowcode-workspace sync-schema",
11
+ "publish:all": "lowcode-workspace publish-all",
12
+ "openxiangda:publish": "lowcode-workspace publish-all",
9
13
  "typecheck": "tsc -p tsconfig.app.json --noEmit",
10
14
  "check": "pnpm typecheck && pnpm build",
11
15
  "resources:plan": "openxiangda resource plan",
@@ -0,0 +1,135 @@
1
+ import { defineFormSchema } from "openxiangda";
2
+
3
+ export type FormComponentName =
4
+ | "TextField"
5
+ | "TextAreaField"
6
+ | "NumberField"
7
+ | "DateField"
8
+ | "CascadeDateField"
9
+ | "SelectField"
10
+ | "MultiSelectField"
11
+ | "RadioField"
12
+ | "CheckboxField"
13
+ | "UserSelectField"
14
+ | "DepartmentSelectField"
15
+ | "AttachmentField"
16
+ | "ImageField"
17
+ | "AddressField"
18
+ | "CascadeSelectField"
19
+ | "LocationField"
20
+ | "EditorField"
21
+ | "JSONField"
22
+ | "SubFormField";
23
+
24
+ export type FormOption = {
25
+ label: string;
26
+ value: string;
27
+ color?: string;
28
+ disabled?: boolean;
29
+ };
30
+
31
+ export type FieldDefinition = {
32
+ fieldId: string;
33
+ componentName: FormComponentName;
34
+ label: string;
35
+ required?: boolean;
36
+ behavior?: "NORMAL" | "READONLY" | "DISABLED" | "HIDDEN";
37
+ rules?: Array<{
38
+ required?: boolean;
39
+ message?: string;
40
+ min?: number;
41
+ max?: number;
42
+ minLength?: number;
43
+ maxLength?: number;
44
+ preset?: "phone" | "idCard" | "email" | "url" | "bankCard";
45
+ pattern?: RegExp | string;
46
+ }>;
47
+ placeholder?: string;
48
+ tips?: string;
49
+ options?: FormOption[];
50
+ defaultValue?: unknown;
51
+ columns?: FieldDefinition[];
52
+ [key: string]: unknown;
53
+ };
54
+
55
+ export type OpenXiangdaFormSchemaInput = {
56
+ formMeta: {
57
+ formUuid: string;
58
+ appType: string;
59
+ title: string;
60
+ formType?: "receipt" | "process";
61
+ };
62
+ fields: FieldDefinition[];
63
+ template?: Record<string, unknown>;
64
+ };
65
+
66
+ export function option(value: string, label = value): FormOption {
67
+ return { label, value };
68
+ }
69
+
70
+ export function options(values: string[]): FormOption[] {
71
+ return values.map((value) => option(value));
72
+ }
73
+
74
+ export function createFormSchema(input: OpenXiangdaFormSchemaInput) {
75
+ return defineFormSchema({
76
+ formMeta: input.formMeta,
77
+ template: {
78
+ ...(input.template || {}),
79
+ ...(input.formMeta.formType === "process" ? { formType: "process" } : {}),
80
+ },
81
+ fields: input.fields.map(normalizeField),
82
+ layout: input.fields.map((field) => ({
83
+ id: `layout_${field.fieldId}`,
84
+ type: "field" as const,
85
+ fieldId: field.fieldId,
86
+ })),
87
+ });
88
+ }
89
+
90
+ function normalizeField(field: FieldDefinition): FieldDefinition {
91
+ return {
92
+ ...field,
93
+ options:
94
+ needsOptionsArray(field.componentName) && !Array.isArray(field.options)
95
+ ? []
96
+ : field.options,
97
+ columns: field.columns?.map(normalizeField),
98
+ rules:
99
+ field.rules ||
100
+ (field.required
101
+ ? [
102
+ {
103
+ required: true,
104
+ message: `${needsSelectVerb(field.componentName) ? "请选择" : "请填写"}${field.label}`,
105
+ },
106
+ ]
107
+ : undefined),
108
+ };
109
+ }
110
+
111
+ function needsOptionsArray(componentName: FormComponentName): boolean {
112
+ return [
113
+ "SelectField",
114
+ "MultiSelectField",
115
+ "RadioField",
116
+ "CheckboxField",
117
+ "CascadeSelectField",
118
+ ].includes(componentName);
119
+ }
120
+
121
+ function needsSelectVerb(componentName: FormComponentName): boolean {
122
+ return [
123
+ "SelectField",
124
+ "MultiSelectField",
125
+ "RadioField",
126
+ "CheckboxField",
127
+ "UserSelectField",
128
+ "DepartmentSelectField",
129
+ "DateField",
130
+ "CascadeDateField",
131
+ "CascadeSelectField",
132
+ "ImageField",
133
+ "AttachmentField",
134
+ ].includes(componentName);
135
+ }