@steedos-labs/plugin-workflow 3.0.25 → 3.0.27

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,8 +1,20 @@
1
1
  {
2
2
  "name": "@steedos-labs/plugin-workflow",
3
- "version": "3.0.25",
3
+ "version": "3.0.27",
4
4
  "main": "package.service.js",
5
5
  "license": "MIT",
6
+ "files": [
7
+ "main",
8
+ "src",
9
+ "designer/dist",
10
+ "public/applications",
11
+ "public/office",
12
+ "public/workflow",
13
+ "package.service.js",
14
+ "package.service.yml",
15
+ "LICENSE.txt",
16
+ "README.md"
17
+ ],
6
18
  "scripts": {
7
19
  "build:watch": "tsc --watch",
8
20
  "release": "npm publish --registry https://registry.npmjs.org && npx cnpm sync @steedos-labs/plugin-workflow",
@@ -529,6 +529,13 @@ tbody .color-priority-muted *{
529
529
  box-shadow: none;
530
530
  }
531
531
 
532
+ .steedos-instance-style-table .antd-RadiosControl{
533
+ margin-left: 0.5rem !important;
534
+ }
535
+
536
+ .steedos-instance-style-table .antd-CheckboxesControl{
537
+ margin-left: 0.5rem !important;
538
+ }
532
539
 
533
540
  .instances-sidebar .steedos-listview-change-button > button > .fa-caret-down{
534
541
  display: none;
@@ -556,6 +563,12 @@ tbody .color-priority-muted *{
556
563
  height: calc(100vh - 101px);
557
564
  }
558
565
 
566
+ @media (max-width: 768px){
567
+ .steedos-instance-detail-wrapper{
568
+ height: calc(100vh - 64px);
569
+ }
570
+ }
571
+
559
572
  .steedos-amis-instance-view-body .antd-Panel-title{
560
573
  font-size: 14px;
561
574
  font-weight: 500;
@@ -627,6 +640,25 @@ tbody .color-priority-muted *{
627
640
  border: none !important;
628
641
  }
629
642
 
643
+ .steedos-instance-style-table .instance-form .td-childfield {
644
+ padding: 0 !important;
645
+ border: 1px solid black !important;
646
+ }
647
+ .steedos-instance-style-table .instance-form .td-childfield .antd-Table{
648
+ margin-bottom: 0px !important;
649
+ }
650
+
651
+ .steedos-instance-style-table .instance-form .td-childfield .antd-Table .antd-Table-operationCell {
652
+ border: none !important;
653
+ }
654
+
655
+ .steedos-instance-style-table .instance-form .td-childfield .antd-Table td:first-child {
656
+ border-right: none !important;
657
+ }
658
+ .steedos-instance-style-table .antd-Select-value{
659
+ white-space: unset !important;
660
+ }
661
+
630
662
 
631
663
  /* 1. 基础设置:合并边框并去掉表格容器的外边框 */
632
664
  .instance-form .antd-Table-table {
@@ -751,6 +783,14 @@ tbody .color-priority-muted *{
751
783
  }
752
784
  }
753
785
 
786
+ .antd-Combo-form .antd-Form-row .antd-Form-col{
787
+ margin-bottom: 0.75rem !important;
788
+ }
789
+ .antd-Combo-form .antd-Form-row .antd-Form-col .antd-Form-rowInner > div{
790
+ flex: 1;
791
+ }
792
+
793
+
754
794
  /* .steedos-instance-detail-wrapper{
755
795
  height: auto !important;
756
796
  }
@@ -28,6 +28,22 @@ function getFieldLabel(fieldDef, fieldName) {
28
28
  return fieldName;
29
29
  }
30
30
 
31
+ const getArgumentsList = (func)=>{
32
+ let funcString;
33
+ if (typeof func === 'function') {
34
+ funcString = func.toString();
35
+ } else {
36
+ funcString = func;
37
+ }
38
+ const regExp = /function\s*\w*\(([\s\S]*?)\)/;
39
+ if (regExp.test(funcString)) {
40
+ const argList = RegExp.$1.split(',');
41
+ return argList.map(arg => arg.replace(/\s/g, ''));
42
+ } else {
43
+ return [];
44
+ }
45
+ }
46
+
31
47
  function fieldToAmis(fieldName, fieldDef, schema, isColumn = false, isPrint = false) {
32
48
  // Handle section type special case
33
49
  if (fieldDef && fieldDef.type === 'section') {
@@ -85,6 +101,126 @@ function fieldToAmis(fieldName, fieldDef, schema, isColumn = false, isPrint = fa
85
101
  case 'checkbox': type = 'checkboxes'; break;
86
102
  case 'select': type = 'select'; break;
87
103
  case 'lookup': type = 'select'; break; // Simplified for now
104
+ case 'odata':
105
+ type = 'select';
106
+ const field = fieldDef;
107
+ const inTable = isColumn;
108
+ const argsName = getArgumentsList(field.filters);
109
+ let labelField = field.formula ? field.formula.substr(1, field.formula.length - 2) : 'name';
110
+ if(labelField.indexOf(".") > -1){
111
+ labelField = labelField.substr(labelField.indexOf(".") + 1);
112
+ }
113
+ config.multiple = field.is_multiselect;
114
+
115
+ // Compress script to single line to avoid JSON parsing issues with control characters
116
+ const adaptorScript = `
117
+ return (async () => {
118
+ let options = _.map(payload.value, (item)=>{
119
+ const value = item;
120
+ item["@label"] = item["${labelField}"];
121
+ delete item['@odata.editLink'];
122
+ delete item['@odata.etag'];
123
+ delete item['@odata.id'];
124
+ return {
125
+ label: item["@label"],
126
+ value: value
127
+ }
128
+ });
129
+
130
+ if(api.selectedIds && api.selectedIds.length > 0){
131
+ const loadedIds = options.map(function(opt){ return opt.value._id; });
132
+ const missingIds = api.selectedIds.filter(function(id){ return !loadedIds.includes(id); });
133
+ if(missingIds.length > 0){
134
+ const baseUrl = api.baseUrl;
135
+ const idFilter = missingIds.map(function(id){ return "_id eq '" + id + "'"; }).join(" or ");
136
+ const fetchUrl = baseUrl + (baseUrl.indexOf('?') > -1 ? '&' : '?') + "$filter=" + idFilter;
137
+ try {
138
+ const response = await fetch(fetchUrl, {});
139
+ const data = await response.json();
140
+ if (data && data.value) {
141
+ const missingOptions = _.map(data.value, (item) => {
142
+ const value = item;
143
+ item["@label"] = item["${labelField}"];
144
+ delete item['@odata.editLink'];
145
+ delete item['@odata.etag'];
146
+ delete item['@odata.id'];
147
+ return { label: item["@label"], value: value };
148
+ });
149
+ options.unshift(...missingOptions);
150
+ }
151
+ } catch (e) {
152
+ console.error("Failed to fetch missing values", e);
153
+ }
154
+ }
155
+ }
156
+ payload.data = { options: options };
157
+ return payload;
158
+ })();
159
+ `.replace(/\s+/g, ' ');
160
+
161
+ const filtersStr = field.filters ? _.replace(field.filters, /_.pluck/g, '_.map') : '';
162
+ const requestAdaptorScript = `
163
+ const filters = \`${filtersStr}\`;
164
+ let url = \`${field.url}\`;
165
+ api.baseUrl = url;
166
+ if(filters){
167
+ let joinKey = url.indexOf('?') > 0 ? '&' : '?';
168
+ let _filter = [];
169
+ if(filters.startsWith('function(') || filters.startsWith('function (')){
170
+ const argsName = ${JSON.stringify(argsName)};
171
+ const fun = eval('_fun='+filters);
172
+ const funArgs = [];
173
+ for(const item of argsName){
174
+ funArgs.push(context[item]);
175
+ }
176
+ _filter = fun.apply({}, funArgs);
177
+ }else{
178
+ _filter = filters;
179
+ }
180
+
181
+ const val = context.value || _.get(context, '${field.code}');
182
+ let ids = [];
183
+ if(val){
184
+ const values = Array.isArray(val) ? val : [val];
185
+ ids = values.map((v) => v && (v._id || v)).filter((v) => typeof v === 'string');
186
+ }
187
+
188
+ ids = _.uniq(ids);
189
+ api.selectedIds = ids;
190
+
191
+ if(context.term){
192
+ _filter = \`(\${_filter}) and contains(name, '\${context.term}')\`;
193
+ }
194
+ joinKey = url.indexOf('?') > 0 ? '&' : '?';
195
+ api.url = url + joinKey + "$filter=" + _filter;
196
+ }else{
197
+ api.url = url;
198
+ }
199
+ api.query = {};
200
+ return api;
201
+ `.replace(/\s+/g, ' ');
202
+
203
+ config.autoComplete = {
204
+ url: field.url.indexOf('?') < 0 ? `${field.url}?term=\${term}` : `${field.url}&term=\${term}`,
205
+ method: "get",
206
+ dataType: "json",
207
+ adaptor: adaptorScript,
208
+ requestAdaptor: requestAdaptorScript
209
+ };
210
+
211
+ // Only add trackExpression if there are dependent fields (argsName)
212
+ if (argsName && argsName.length > 0) {
213
+ // Use simpler tracking without |json to avoid potential newline issues in stringified objects
214
+ config.autoComplete.trackExpression = _.join(_.map(argsName, (item) => `\${${item}}`), '-');
215
+ }
216
+
217
+ config.source = _.cloneDeep(config.autoComplete);
218
+ // trackExpression is needed in source to react to dependency changes
219
+
220
+ if(!inTable){
221
+ config.searchable = true;
222
+ }
223
+ break;
88
224
  case 'table':
89
225
  type = 'steedos-input-table';
90
226
  config.columnsTogglable = false;
@@ -287,7 +423,13 @@ function convertTemplate(content, fields, isPrint) {
287
423
  });
288
424
 
289
425
  // --- Sub-phase 2c: Convert afFieldLabelText ---
290
- newContent = newContent.replace(/\{\{afFieldLabelText\s+name=["'](.+?)["']\}\}/g, '$1');
426
+ newContent = newContent.replace(/\{\{afFieldLabelText\s+name=["'](.+?)["']\}\}/g, (match, name) => {
427
+ const fieldDef = findFieldDef(fields, name);
428
+ if (fieldDef && fieldDef.is_required) {
429
+ return `${name}<span class="antd-Form-star" style="display: \${record.step.permissions['${name}'] == 'editable' ? 'inline' : 'none'}">*</span>`;
430
+ }
431
+ return name;
432
+ });
291
433
 
292
434
  // --- Sub-phase 2d: Convert instanceSignText ---
293
435
  newContent = newContent.replace(/\{\{>\s*instanceSignText\s+([\s\S]+?)\}\}/g, (match, args) => {
@@ -334,6 +476,14 @@ function convertTemplate(content, fields, isPrint) {
334
476
  return `{% amis %}\n${JSON.stringify(amisSchema, null, 2)}\n{% endamis %}`;
335
477
  });
336
478
 
479
+ // --- Sub-phase 2e: Add header for tr-child-table ---
480
+ // Extract dynamic title from td-childfield-xxx class if present
481
+ newContent = newContent.replace(/(<tr[^>]*class\s*=\s*["'][^"']*tr-child-table[^"']*["'][^>]*>[\s\S]*?class\s*=\s*["'][^"']*?td-childfield-)([^\s"']+)/gi,
482
+ (match, beforeName, name) => {
483
+ return `<tr><td colspan="10" style="background:#f1f1f1" class="font-bold">${name}</td></tr>${beforeName}${name}`;
484
+ }
485
+ );
486
+
337
487
  return newContent;
338
488
  }
339
489