@steedos/service-pages 2.7.7 → 2.7.8-beta.1

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.
@@ -9,5 +9,5 @@ filtersFunction: !!js/function |
9
9
  return ['name', '!=', 'cfs_instances_filerecord']
10
10
  }
11
11
  filterable: true
12
- required: "{{'record' == formData.type || 'list' == formData.type || 'form' == formData.type ? true: false}}"
13
- visible_on: "{{'record' == formData.type || 'list' == formData.type || 'form' == formData.type ? true: false}}"
12
+ required: "{{'record' == formData.type || 'list' == formData.type || 'form' == formData.type || 'field_layout' == formData.type ? true: false}}"
13
+ visible_on: "{{'record' == formData.type || 'list' == formData.type || 'form' == formData.type || 'field_layout' == formData.type ? true: false}}"
@@ -12,6 +12,8 @@ options:
12
12
  value: list
13
13
  - label: 表单
14
14
  value: form
15
+ - label: 字段布局页面
16
+ value: field_layout
15
17
  sort_no: 130
16
18
  visible_on: "{{'redash' != formData.render_engine ? true: false}}"
17
19
  filterable: true
@@ -9,9 +9,36 @@
9
9
  <head>
10
10
  <script src="/unpkg.com/@steedos-builder/fiddle@0.0.5/dist/builder-fiddle.umd.js"></script>
11
11
  <script src="/unpkg.com/axios@0.26.1/dist/axios.min.js"></script>
12
+ <script src="https://unpkg.steedos.cn/flowbite@2.3.0/dist/flowbite.min.js"></script>
13
+ <script src="https://cdn.tailwindcss.com"></script>
12
14
  </head>
13
15
 
14
16
  <body>
17
+ <!-- Main modal -->
18
+ <div id="fieldLayoutModal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
19
+ <div class="relative p-4 w-full max-w-2xl max-h-full">
20
+ <!-- Modal content -->
21
+ <div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
22
+ <!-- Modal header -->
23
+ <div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
24
+ <h3 class="text-xl font-semibold text-gray-900 dark:text-white">
25
+ 字段保存结果
26
+ </h3>
27
+ <button type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-hide="fieldLayoutModal">
28
+ <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
29
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
30
+ </svg>
31
+ <span class="sr-only">Close modal</span>
32
+ </button>
33
+ </div>
34
+ <!-- Modal body -->
35
+ <div class="p-4 md:p-5 space-y-4">
36
+ <ul class="space-y-4 text-gray-500 list-disc list-inside dark:text-gray-400" id="fieldLayoutList">
37
+ </ul>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </div>
15
42
  <builder-fiddle host="<%=builderHost%>"></builder-fiddle>
16
43
  <script>
17
44
  const useOpenAPI= <%=(useOpenAPI === "true" || useOpenAPI === true)%>;
@@ -28,6 +55,32 @@
28
55
 
29
56
  let comp = document.querySelector("builder-fiddle");
30
57
 
58
+ // set the modal menu element
59
+ const $targetEl = document.getElementById('fieldLayoutModal');
60
+ // options with default values
61
+ const options = {
62
+ placement: 'top-center',
63
+ backdrop: 'dynamic',
64
+ backdropClasses:
65
+ 'bg-gray-900/50 dark:bg-gray-900/80 fixed inset-0 z-40',
66
+ closable: true,
67
+ onHide: () => {
68
+ console.log('modal is hidden');
69
+ },
70
+ onShow: () => {
71
+ console.log('modal is shown');
72
+ },
73
+ onToggle: () => {
74
+ console.log('modal has been toggled');
75
+ },
76
+ };
77
+ // instance options object
78
+ const instanceOptions = {
79
+ id: 'fieldLayoutModal',
80
+ override: true
81
+ };
82
+ const modal = new Flowbite.default.Modal($targetEl, options, instanceOptions);
83
+
31
84
  const loadPage = async () => {
32
85
  const { assetUrls, rootUrl, userId, tenantId, authToken, pageId } = settings;
33
86
 
@@ -146,6 +199,53 @@
146
199
  });
147
200
  };
148
201
 
202
+ const getElement = (title,dataList) => {
203
+ const topLevelLi = document.createElement('li');
204
+ topLevelLi.textContent = title;
205
+
206
+ const nestedUl = document.createElement('ul');
207
+ nestedUl.classList.add('flex', 'flex-wrap', 'text-gray-900', 'dark:text-white');
208
+
209
+ for (let i = 0; i < dataList.length; i++) {
210
+ const nestedLi = document.createElement('li');
211
+ nestedLi.classList.add('pl-6');
212
+ nestedLi.textContent = dataList[i];
213
+ nestedUl.appendChild(nestedLi);
214
+ }
215
+
216
+ topLevelLi.appendChild(nestedUl);
217
+ return topLevelLi;
218
+ }
219
+
220
+ const convertLogToMessage = (fieldLayoutLog) => {
221
+ var list = document.getElementById('fieldLayoutList');
222
+ list.innerHTML = '';
223
+ if (fieldLayoutLog.insert.success.length > 0) {
224
+ const topLevelLi = getElement(`新增成功(${fieldLayoutLog.insert.success.length})`,fieldLayoutLog.insert.success);
225
+ list.appendChild(topLevelLi);
226
+ }
227
+ if (fieldLayoutLog.insert.error.length > 0) {
228
+ const topLevelLi = getElement(`新增失败(${fieldLayoutLog.insert.error.length})`,fieldLayoutLog.insert.error);
229
+ list.appendChild(topLevelLi);
230
+ }
231
+ if (fieldLayoutLog.update.success.length > 0) {
232
+ const topLevelLi = getElement(`修改成功(${fieldLayoutLog.update.success.length})`,fieldLayoutLog.update.success);
233
+ list.appendChild(topLevelLi);
234
+ }
235
+ if (fieldLayoutLog.update.error.length > 0) {
236
+ const topLevelLi = getElement(`修改失败(${fieldLayoutLog.update.error.length})`,fieldLayoutLog.update.error);
237
+ list.appendChild(topLevelLi);
238
+ }
239
+ if (fieldLayoutLog.delete.success.length > 0) {
240
+ const topLevelLi = getElement(`删除成功(${fieldLayoutLog.delete.success.length})`,fieldLayoutLog.delete.success);
241
+ list.appendChild(topLevelLi);
242
+ }
243
+ if (fieldLayoutLog.delete.error.length > 0) {
244
+ const topLevelLi = getElement(`删除失败(${fieldLayoutLog.delete.error.length})`,fieldLayoutLog.delete.error);
245
+ list.appendChild(topLevelLi);
246
+ }
247
+ };
248
+
149
249
  const deployPageVersion = async () => {
150
250
  const { rootUrl, tenantId, authToken, pageId } = settings;
151
251
 
@@ -175,6 +275,10 @@
175
275
  "*"
176
276
  );
177
277
  }
278
+ if(response.data.fieldLayoutLog){
279
+ convertLogToMessage(response.data.fieldLayoutLog);
280
+ modal.show();
281
+ }
178
282
  })
179
283
  .catch(function (error) {
180
284
  // handle error
@@ -1,8 +1,8 @@
1
1
  <!--
2
2
  * @Author: baozhoutao@steedos.com
3
3
  * @Date: 2023-08-01 17:47:18
4
- * @LastEditors: 殷亮辉 yinlianghui@hotoa.com
5
- * @LastEditTime: 2024-03-14 18:26:27
4
+ * @LastEditors: baozhoutao@steedos.com
5
+ * @LastEditTime: 2024-09-04 13:55:15
6
6
  * @Description:
7
7
  -->
8
8
  <!DOCTYPE html>
@@ -145,6 +145,9 @@
145
145
  window.moment = amisRequire('moment');
146
146
  window.React = amisRequire('react');
147
147
  window.ReactDOM = amisRequire('react-dom');
148
+ window.AmisCore = amisRequire("amis-core")
149
+ window.AmisUI = amisRequire("amis-ui");
150
+ window.Amis = amisRequire('amis');
148
151
 
149
152
  window.addEventListener('message', function (event) {
150
153
  const { data } = event;
@@ -43,8 +43,14 @@ module.exports = {
43
43
  const { pageId } = ctx.params;
44
44
  const userSession = ctx.meta.user;
45
45
  const lastVersion = await this.getLatestPageVersion(pageId);
46
+ const page = await objectql.getObject('pages').findOne(pageId);
47
+ const response = {};
46
48
  if(lastVersion){
47
- return await objectql.getObject('page_versions').update(lastVersion._id, { is_active: true }, userSession);
49
+ if(page && page.type == 'field_layout'){
50
+ response.fieldLayoutLog = await this.fieldLayoutSave(lastVersion.schema, page.object_name, userSession)
51
+ }
52
+ response.page_versions = await objectql.getObject('page_versions').update(lastVersion._id, { is_active: true }, userSession);
53
+ return response;
48
54
  }
49
55
  }
50
56
  },
@@ -421,6 +427,140 @@ module.exports = {
421
427
  }
422
428
  }
423
429
  },
430
+ fieldLayoutSave: {
431
+ async handler(schemaString, object_name, userSession) {
432
+ const submitProps = ["_name", "name", "type", "amis", "auto_fill_mapping", "autonumber_enable_modify", "column_name", "coordinatesType", "create", "data_type",
433
+ "defaultValue", "deleted_lookup_record_behavior", "depend_on", "description", "enable_enhanced_lookup", "enable_thousands", "filterable", "filters", "filtersFunction", "formula_blank_value", "formula",
434
+ "generated", "group", "hidden", "index", "inlineHelpText", "is_customize", "is_name", "is_system", "is_wide", "label", "language", "multiple", "object", "options", "precision", "primary", "readonly", "reference_to_field",
435
+ "reference_to", "required", "rows", "scale", "searchable", "show_as_qr", "sort_no", "sortable", "static", "summary_field", "summary_object", "summary_filters", "summary_type", "unique", "visible_on", "write_requires_master_read"
436
+ ];
437
+ const schema = JSON.parse(schemaString);
438
+ let steedosFields = [];
439
+ //提取schema中的steedos-field
440
+ function findSteedosFields(obj) {
441
+ if (Array.isArray(obj)) {
442
+ for (let i = 0; i < obj.length; i++) {
443
+ findSteedosFields(obj[i]);
444
+ }
445
+ } else if (typeof obj === 'object' && obj !== null) {
446
+ if (obj.type === 'steedos-field') {
447
+ steedosFields.push(obj);
448
+ } else {
449
+ for (let key in obj) {
450
+ findSteedosFields(obj[key]);
451
+ }
452
+ }
453
+ }
454
+ }
455
+ findSteedosFields(schema);
456
+
457
+ const fields = [];
458
+ //根据object_fields的字段,提取对应属性
459
+ _.forEach(steedosFields, item => {
460
+ item._name = item.name;
461
+ fields.push(_.pick(item.config, submitProps))
462
+ })
463
+ const object_fields = await objectql.getObject('object_fields');
464
+ const dbFields = await object_fields.directFind({filters: ['object','=', object_name]});
465
+ /*
466
+ 若fields中存在,dbFields中不存在,将该对象name值存入insertFields
467
+ 若fields中存在,dbFields中也存在,将该对象name值存入updateFields
468
+ 若fields中不存在,dbFields中存在,将该对象name值存入deleteFields
469
+ */
470
+ let insertFields = _.differenceBy(fields, dbFields, 'name').map(field => field.name);
471
+ let updateFields = _.intersectionBy(fields, dbFields, 'name').map(field => field.name);
472
+ let deleteFields = _.differenceBy(dbFields, fields, 'name').map(field => field.name);
473
+
474
+ // 用于记录成功和失败的字段
475
+ const log = {
476
+ insert: {
477
+ success: [],
478
+ error: []
479
+ },
480
+ update: {
481
+ success: [],
482
+ error: []
483
+ },
484
+ delete: {
485
+ success: [],
486
+ error: []
487
+ }
488
+ };
489
+
490
+ // 循环需要增加的字段
491
+ for (const fieldName of insertFields) {
492
+ try {
493
+ const newId = await object_fields._makeNewID();
494
+ const now = new Date();
495
+ const field = _.find(fields, { name: fieldName });
496
+ await object_fields.directInsert(Object.assign({}, field, {
497
+ _id: newId,
498
+ owner: userSession.userId,
499
+ space: userSession.spaceId,
500
+ object: object_name,
501
+ created: now,
502
+ modified: now,
503
+ created_by: userSession.userId,
504
+ modified_by: userSession.userId,
505
+ company_id: userSession.company_id,
506
+ company_ids: userSession.company_ids
507
+ }));
508
+ log.insert.success.push(fieldName);
509
+ } catch (e) {
510
+ log.insert.error.push(fieldName);
511
+ console.error(`新增字段 ${fieldName} 时出错:`, e);
512
+ }
513
+ }
514
+
515
+ // const fieldsToUpdate = [];
516
+ // _.forEach(updateFields, field => {
517
+ // const fieldInFields = _.find(fields, {name: field});
518
+ // const sameKeys = _.keys(fieldInFields);
519
+ // const fieldInDbFields = _.pick(_.find(dbFields, {name: field}), sameKeys);
520
+
521
+
522
+ // // 比较这两个对象,如果不相等,则添加到fieldsToUpdate数组中
523
+ // if (!_.isEqual(fieldInFields, fieldInDbFields)) {
524
+ // fieldsToUpdate.push(field);
525
+ // }
526
+ // });
527
+ // // 更新updateFields为fieldsToUpdate
528
+ // updateFields = fieldsToUpdate;
529
+ // 循环需要修改的字段
530
+ const now = new Date();
531
+ for (const fieldName of updateFields) {
532
+ try {
533
+ const field = _.find(fields, { name: fieldName });
534
+ const id = _.find(dbFields, { name: fieldName })._id;
535
+ const submitField = _.omit(field, ['name', '_name']);
536
+ await object_fields.directUpdate(id, Object.assign({}, submitField, {
537
+ modified: now,
538
+ modified_by: userSession.userId
539
+ }));
540
+ log.update.success.push(fieldName);
541
+ } catch (e) {
542
+ log.update.error.push(fieldName);
543
+ console.error(`更新字段 ${fieldName} 时出错:`, e);
544
+ }
545
+ }
546
+ // 循环需要删除的字段
547
+ for (const fieldName of deleteFields) {
548
+ try {
549
+ const id = _.find(dbFields, { name: fieldName })._id;
550
+ await object_fields.directDelete(id);
551
+ log.delete.success.push(fieldName);
552
+ } catch (e) {
553
+ log.delete.error.push(fieldName);
554
+ console.error(`删除字段 ${fieldName} 时出错:`, e);
555
+ }
556
+ }
557
+ //label和修改时间未实时生效
558
+ const object = await objectql.getObject('objects');
559
+ const current_object = await object.findOne({filters:[["name","=",object_name]]});
560
+ await object.update(current_object._id,{reload_time: new Date()})
561
+ return log;
562
+ }
563
+ }
424
564
  },
425
565
 
426
566
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steedos/service-pages",
3
- "version": "2.7.7",
3
+ "version": "2.7.8-beta.1",
4
4
  "main": "package.service.js",
5
5
  "scripts": {},
6
6
  "license": "MIT",
@@ -8,7 +8,7 @@
8
8
  "publishConfig": {
9
9
  "access": "public"
10
10
  },
11
- "gitHead": "82b87061344ebe4c6a9dbd258e8891fcc39bfdc6",
11
+ "gitHead": "8d913c6e5741fc022c36b86db513753f975294a4",
12
12
  "dependencies": {
13
13
  "clone": "^2.1.2",
14
14
  "ejs": "^3.1.8"