openxiangda 1.0.51 → 1.0.52

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.52",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -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) {
@@ -117,6 +147,7 @@ async function loadSchema(schemaPath) {
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
+ }