@steedos/service-pages 2.7.6 → 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.
- package/main/default/objects/pages/fields/object_name.field.yml +2 -2
- package/main/default/objects/pages/fields/type.field.yml +2 -0
- package/main/default/routes/page_design.ejs +104 -0
- package/main/default/routes/page_view.ejs +5 -2
- package/main/default/services/page.service.js +141 -1
- package/package.json +2 -2
|
@@ -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}}"
|
|
@@ -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:
|
|
5
|
-
* @LastEditTime: 2024-
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
11
|
+
"gitHead": "8d913c6e5741fc022c36b86db513753f975294a4",
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"clone": "^2.1.2",
|
|
14
14
|
"ejs": "^3.1.8"
|