@houaoran/designer 1.0.0
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/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/components.es.js +11424 -0
- package/dist/components.umd.js +878 -0
- package/dist/index.es.js +39113 -0
- package/dist/index.umd.js +1187 -0
- package/package.json +96 -0
- package/src/components/DragBox.vue +49 -0
- package/src/components/DragTool.vue +235 -0
- package/src/components/EventConfig.vue +557 -0
- package/src/components/FcDesigner.vue +2569 -0
- package/src/components/FcTitle.vue +69 -0
- package/src/components/FetchConfig.vue +415 -0
- package/src/components/FieldInput.vue +371 -0
- package/src/components/FnConfig.vue +315 -0
- package/src/components/FnEditor.vue +327 -0
- package/src/components/FnInput.vue +103 -0
- package/src/components/FormLabel.vue +47 -0
- package/src/components/HtmlEditor.vue +125 -0
- package/src/components/JsonPreview.vue +146 -0
- package/src/components/OptionsTextInput.vue +151 -0
- package/src/components/PropsInput.vue +72 -0
- package/src/components/Required.vue +75 -0
- package/src/components/Row.vue +26 -0
- package/src/components/SignaturePad.vue +176 -0
- package/src/components/Struct.vue +153 -0
- package/src/components/StructEditor.vue +121 -0
- package/src/components/StructTree.vue +209 -0
- package/src/components/TableOptions.vue +164 -0
- package/src/components/TreeOptions.vue +167 -0
- package/src/components/TypeSelect.vue +144 -0
- package/src/components/Validate.vue +302 -0
- package/src/components/ValueInput.vue +89 -0
- package/src/components/Warning.vue +46 -0
- package/src/components/ai/AiPanel.vue +1122 -0
- package/src/components/ai/MarkdownRenderer.vue +548 -0
- package/src/components/language/LanguageConfig.vue +174 -0
- package/src/components/language/LanguageInput.vue +191 -0
- package/src/components/style/BackgroundInput.vue +315 -0
- package/src/components/style/BorderInput.vue +242 -0
- package/src/components/style/BoxSizeInput.vue +166 -0
- package/src/components/style/BoxSpaceInput.vue +269 -0
- package/src/components/style/ColorInput.vue +90 -0
- package/src/components/style/ConfigItem.vue +118 -0
- package/src/components/style/FontInput.vue +197 -0
- package/src/components/style/PositionInput.vue +146 -0
- package/src/components/style/RadiusInput.vue +164 -0
- package/src/components/style/ShadowContent.vue +335 -0
- package/src/components/style/ShadowInput.vue +91 -0
- package/src/components/style/SizeInput.vue +118 -0
- package/src/components/style/StyleConfig.vue +307 -0
- package/src/components/table/Table.vue +252 -0
- package/src/components/table/TableView.vue +1058 -0
- package/src/components/tableForm/TableForm.vue +471 -0
- package/src/components/tableForm/TableFormColumnView.vue +103 -0
- package/src/components/tableForm/TableFormView.vue +46 -0
- package/src/components/tree/FcTree.vue +713 -0
- package/src/components/tree/FcTreeNode.vue +216 -0
- package/src/config/base/field.js +43 -0
- package/src/config/base/form.js +132 -0
- package/src/config/base/style.js +26 -0
- package/src/config/base/validate.js +15 -0
- package/src/config/index.js +70 -0
- package/src/config/menu.js +24 -0
- package/src/config/rule/alert.js +45 -0
- package/src/config/rule/button.js +49 -0
- package/src/config/rule/card.js +40 -0
- package/src/config/rule/cascader.js +121 -0
- package/src/config/rule/checkbox.js +68 -0
- package/src/config/rule/col.js +86 -0
- package/src/config/rule/collapse.js +30 -0
- package/src/config/rule/collapseItem.js +36 -0
- package/src/config/rule/color.js +53 -0
- package/src/config/rule/date.js +66 -0
- package/src/config/rule/dateRange.js +60 -0
- package/src/config/rule/divider.js +31 -0
- package/src/config/rule/editor.js +31 -0
- package/src/config/rule/group.js +86 -0
- package/src/config/rule/html.js +43 -0
- package/src/config/rule/image.js +32 -0
- package/src/config/rule/input.js +62 -0
- package/src/config/rule/number.js +49 -0
- package/src/config/rule/password.js +52 -0
- package/src/config/rule/radio.js +43 -0
- package/src/config/rule/rate.js +44 -0
- package/src/config/rule/row.js +46 -0
- package/src/config/rule/select.js +70 -0
- package/src/config/rule/signaturePad.js +59 -0
- package/src/config/rule/slider.js +53 -0
- package/src/config/rule/space.js +44 -0
- package/src/config/rule/subForm.js +47 -0
- package/src/config/rule/switch.js +46 -0
- package/src/config/rule/tabPane.js +29 -0
- package/src/config/rule/table.js +37 -0
- package/src/config/rule/tableForm.js +115 -0
- package/src/config/rule/tableFormColumn.js +55 -0
- package/src/config/rule/tabs.js +38 -0
- package/src/config/rule/tag.js +69 -0
- package/src/config/rule/text.js +41 -0
- package/src/config/rule/textarea.js +63 -0
- package/src/config/rule/time.js +58 -0
- package/src/config/rule/timeRange.js +49 -0
- package/src/config/rule/title.js +37 -0
- package/src/config/rule/transfer.js +59 -0
- package/src/config/rule/tree.js +70 -0
- package/src/config/rule/treeSelect.js +77 -0
- package/src/config/rule/upload.js +107 -0
- package/src/form/index.js +19 -0
- package/src/index.js +173 -0
- package/src/locale/en.js +981 -0
- package/src/locale/zh-cn.js +983 -0
- package/src/style/fonts/fc-icons.woff +0 -0
- package/src/style/icon.css +1052 -0
- package/src/style/index.css +836 -0
- package/src/utils/form.js +9 -0
- package/src/utils/highlight/highlight.min.js +307 -0
- package/src/utils/highlight/javascript.min.js +80 -0
- package/src/utils/highlight/style.css +1 -0
- package/src/utils/highlight/xml.min.js +29 -0
- package/src/utils/hintStubs.js +120 -0
- package/src/utils/index.js +544 -0
- package/src/utils/jsonDiff.js +173 -0
- package/src/utils/locale.js +23 -0
- package/src/utils/message.js +19 -0
- package/src/utils/template.js +105 -0
- package/types/index.d.ts +575 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
import is, {hasProperty} from '@form-create/utils/lib/type';
|
|
2
|
+
import {parseFn} from '@form-create/utils/lib/json';
|
|
3
|
+
import toCase from '@form-create/utils/lib/tocase';
|
|
4
|
+
import {computed, isRef, ref, unref} from 'vue';
|
|
5
|
+
import ZhCn from '../locale/zh-cn';
|
|
6
|
+
import {message} from './message';
|
|
7
|
+
import {copy} from '@form-create/utils/lib/extend';
|
|
8
|
+
|
|
9
|
+
export {formTemplate, formTemplateV3, htmlTemplate} from './template';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export function makeRequiredRule() {
|
|
13
|
+
return {
|
|
14
|
+
type: 'Required', field: 'formCreate$required', title: '是否必填'
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function addAutoKeyMap(cm) {
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function makeTreeOptions(pre, config, level, data = []) {
|
|
22
|
+
if (!config.id) {
|
|
23
|
+
config.id = 1;
|
|
24
|
+
}
|
|
25
|
+
level && level--;
|
|
26
|
+
for (let i = 0; i < 3; i++) {
|
|
27
|
+
const item = {
|
|
28
|
+
[config.label]: pre + level * 10 + (i + 1),
|
|
29
|
+
[config.value]: '' + config.id++,
|
|
30
|
+
};
|
|
31
|
+
if (level) {
|
|
32
|
+
makeTreeOptions(pre, config, level, item.children = []);
|
|
33
|
+
}
|
|
34
|
+
data.push(item);
|
|
35
|
+
}
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function makeOptionsRule(t, to, label, value, children) {
|
|
40
|
+
const options = [
|
|
41
|
+
{'label': t('fetch.optionsType.struct'), 'value': 2},
|
|
42
|
+
{'label': t('fetch.optionsType.text'), 'value': 4},
|
|
43
|
+
{'label': t('fetch.optionsType.json'), 'value': 5},
|
|
44
|
+
{'label': t('fetch.optionsType.fetch'), 'value': 1},
|
|
45
|
+
];
|
|
46
|
+
const field = 'formCreate' + upper(to).replace('.', '>');
|
|
47
|
+
|
|
48
|
+
const control = [
|
|
49
|
+
{
|
|
50
|
+
value: 1,
|
|
51
|
+
rule: [
|
|
52
|
+
{
|
|
53
|
+
type: 'FetchConfig',
|
|
54
|
+
field: 'formCreateEffect>fetch',
|
|
55
|
+
props: {
|
|
56
|
+
to
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
value: 2,
|
|
63
|
+
rule: [
|
|
64
|
+
{
|
|
65
|
+
type: 'TableOptions',
|
|
66
|
+
field: field,
|
|
67
|
+
props: {
|
|
68
|
+
column: [{label: t('props.key'), key: 'label'}, {value: true, label: t('props.value'), key: 'value'}],
|
|
69
|
+
keyValue: 'label'
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
}
|
|
74
|
+
,
|
|
75
|
+
{
|
|
76
|
+
value: 4,
|
|
77
|
+
rule: [
|
|
78
|
+
{
|
|
79
|
+
type: 'OptionTextInput',
|
|
80
|
+
field: field,
|
|
81
|
+
props: {
|
|
82
|
+
placeholder: `选项1\n选项2\n选项3`,
|
|
83
|
+
field: {
|
|
84
|
+
label: label || 'label',
|
|
85
|
+
value: value || 'value',
|
|
86
|
+
children: children || 'children',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
value: 5,
|
|
94
|
+
rule: [
|
|
95
|
+
{
|
|
96
|
+
type: 'Struct',
|
|
97
|
+
field: field,
|
|
98
|
+
props: {
|
|
99
|
+
validate(val) {
|
|
100
|
+
return Array.isArray(val);
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
type: 'select',
|
|
110
|
+
title: t('props.options'),
|
|
111
|
+
field: '_optionType',
|
|
112
|
+
value: 2,
|
|
113
|
+
options,
|
|
114
|
+
wrap: {labelPosition: 'left'},
|
|
115
|
+
props: {
|
|
116
|
+
clearable: false
|
|
117
|
+
},
|
|
118
|
+
control
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function makeTreeOptionsRule(t, to, label, value, children) {
|
|
123
|
+
const options = [
|
|
124
|
+
{'label': t('fetch.optionsType.struct'), 'value': 2},
|
|
125
|
+
{'label': t('fetch.optionsType.text'), 'value': 4},
|
|
126
|
+
{'label': t('fetch.optionsType.json'), 'value': 5},
|
|
127
|
+
{'label': t('fetch.optionsType.fetch'), 'value': 1},
|
|
128
|
+
];
|
|
129
|
+
const field = 'formCreate' + upper(to).replace('.', '>');
|
|
130
|
+
|
|
131
|
+
const control = [
|
|
132
|
+
{
|
|
133
|
+
value: 1,
|
|
134
|
+
rule: [
|
|
135
|
+
{
|
|
136
|
+
type: 'FetchConfig',
|
|
137
|
+
field: 'formCreateEffect>fetch',
|
|
138
|
+
props: {
|
|
139
|
+
to
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
value: 2,
|
|
146
|
+
rule: [
|
|
147
|
+
{
|
|
148
|
+
type: 'TreeOptions',
|
|
149
|
+
field: field,
|
|
150
|
+
props: {
|
|
151
|
+
columns: {
|
|
152
|
+
label,
|
|
153
|
+
value
|
|
154
|
+
},
|
|
155
|
+
keyValue: label,
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
}
|
|
160
|
+
,
|
|
161
|
+
{
|
|
162
|
+
value: 4,
|
|
163
|
+
rule: [
|
|
164
|
+
{
|
|
165
|
+
type: 'OptionTextInput',
|
|
166
|
+
field: field,
|
|
167
|
+
props: {
|
|
168
|
+
placeholder: `选项1\n 选项1-1\n 选项1-1-1\n选项2\n 选项2-1\n 选项2-2`,
|
|
169
|
+
parseSpace: true,
|
|
170
|
+
field: {
|
|
171
|
+
label: label || 'label',
|
|
172
|
+
value: value || 'value',
|
|
173
|
+
children: children || 'children',
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
value: 5,
|
|
181
|
+
rule: [
|
|
182
|
+
{
|
|
183
|
+
type: 'Struct',
|
|
184
|
+
field: field,
|
|
185
|
+
props: {
|
|
186
|
+
validate(val) {
|
|
187
|
+
return Array.isArray(val);
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
type: 'select',
|
|
197
|
+
title: t('props.options'),
|
|
198
|
+
field: '_optionType',
|
|
199
|
+
value: 2,
|
|
200
|
+
options,
|
|
201
|
+
wrap: {labelPosition: 'left'},
|
|
202
|
+
props: {
|
|
203
|
+
clearable: false
|
|
204
|
+
},
|
|
205
|
+
control
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function upper(str) {
|
|
210
|
+
return str.replace(str[0], str[0].toLocaleUpperCase());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export const toJSON = function (val) {
|
|
214
|
+
const type = /object ([a-zA-Z]*)/.exec(Object.prototype.toString.call(val));
|
|
215
|
+
if (type && _toJSON[type[1].toLowerCase()]) {
|
|
216
|
+
return _toJSON[type[1].toLowerCase()](val);
|
|
217
|
+
} else {
|
|
218
|
+
return val;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const _toJSON = {
|
|
223
|
+
object: function (val) {
|
|
224
|
+
var json = [];
|
|
225
|
+
for (var i in val) {
|
|
226
|
+
if (!hasProperty(val, i)) continue;
|
|
227
|
+
json.push(
|
|
228
|
+
toJSON(i) + ': ' +
|
|
229
|
+
((val[i] != null) ? toJSON(val[i]) : 'null')
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
return '{\n ' + json.join(',\n ') + '\n}';
|
|
233
|
+
},
|
|
234
|
+
function: function (val) {
|
|
235
|
+
val = '' + val;
|
|
236
|
+
var exec = (/^ *([\w]+) *\(/).exec(val);
|
|
237
|
+
if (exec && exec[1] !== 'function') {
|
|
238
|
+
return 'function ' + val;
|
|
239
|
+
}
|
|
240
|
+
return val;
|
|
241
|
+
},
|
|
242
|
+
array: function (val) {
|
|
243
|
+
for (var i = 0, json = []; i < val.length; i++)
|
|
244
|
+
json[i] = (val[i] != null) ? toJSON(val[i]) : 'null';
|
|
245
|
+
return '[' + json.join(', ') + ']';
|
|
246
|
+
},
|
|
247
|
+
string: function (val) {
|
|
248
|
+
var tmp = val.split('');
|
|
249
|
+
for (var i = 0; i < tmp.length; i++) {
|
|
250
|
+
var c = tmp[i];
|
|
251
|
+
(c >= ' ') ?
|
|
252
|
+
(c === '\\') ? (tmp[i] = '\\\\') :
|
|
253
|
+
(c === '"') ? (tmp[i] = '\\"') : 0 :
|
|
254
|
+
(tmp[i] =
|
|
255
|
+
(c === '\n') ? '\\n' :
|
|
256
|
+
(c === '\r') ? '\\r' :
|
|
257
|
+
(c === '\t') ? '\\t' :
|
|
258
|
+
(c === '\b') ? '\\b' :
|
|
259
|
+
(c === '\f') ? '\\f' :
|
|
260
|
+
(c = c.charCodeAt(), ('\\u00' + ((c > 15) ? 1 : 0) + (c % 16)))
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
return '"' + tmp.join('') + '"';
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
export const deepParseFn = function (target) {
|
|
268
|
+
if (target && typeof target === 'object') {
|
|
269
|
+
for (let key in target) {
|
|
270
|
+
if (Object.prototype.hasOwnProperty.call(target, key)) {
|
|
271
|
+
let data = target[key];
|
|
272
|
+
if (Array.isArray(data) || is.Object(data)) {
|
|
273
|
+
deepParseFn(data);
|
|
274
|
+
}
|
|
275
|
+
if (is.String(data)) {
|
|
276
|
+
target[key] = parseFn(data);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return target;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
export function deepGet(object, path, defaultValue) {
|
|
286
|
+
path = (path || '').split('.');
|
|
287
|
+
|
|
288
|
+
let index = 0,
|
|
289
|
+
length = path.length;
|
|
290
|
+
|
|
291
|
+
while (object != null && index < length) {
|
|
292
|
+
object = object[path[index++]];
|
|
293
|
+
}
|
|
294
|
+
return (index && index === length) ? (object !== undefined ? object : defaultValue) : defaultValue;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export const buildTranslator = (locale) => (path, option) => translate(path, option, unref(locale));
|
|
298
|
+
|
|
299
|
+
export const translate = (path, option, locale) => {
|
|
300
|
+
const val = deepGet(locale, path, '');
|
|
301
|
+
if (typeof val === 'object') {
|
|
302
|
+
return val;
|
|
303
|
+
}
|
|
304
|
+
return val.replace(/\{(\w+)\}/g, (_, key) => `${option?.[key] ?? `{${key}}`}`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export const buildLocaleContext = (locale) => {
|
|
308
|
+
const lang = computed(() => unref(locale).name)
|
|
309
|
+
const name = computed(() => upper(toCase(lang.value || '')))
|
|
310
|
+
const localeRef = isRef(locale) ? locale : ref(locale)
|
|
311
|
+
return {
|
|
312
|
+
lang,
|
|
313
|
+
name,
|
|
314
|
+
locale: localeRef,
|
|
315
|
+
t: buildTranslator(locale),
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export const useLocale = (locale) => {
|
|
320
|
+
return buildLocaleContext(computed(() => locale.value || ZhCn))
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export const localeOptions = (t, options, prefix) => {
|
|
324
|
+
return options.map(opt => {
|
|
325
|
+
opt.label = t((prefix || 'props') + '.' + opt.label || opt.value) || opt.label;
|
|
326
|
+
return opt;
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export const localeProps = (t, prefix, rules) => {
|
|
331
|
+
return rules.map(rule => {
|
|
332
|
+
if (rule.field === 'formCreate$required') {
|
|
333
|
+
rule.title = t('validate.required') || rule.title;
|
|
334
|
+
} else if (rule.field && rule.field !== '_optionType') {
|
|
335
|
+
rule.title = t('com.' + prefix + '.' + rule.field) || rule.title;
|
|
336
|
+
}
|
|
337
|
+
if (rule.type === 'template' && is.trueArray(rule.children)) {
|
|
338
|
+
rule.children = localeProps(t, prefix, rule.children);
|
|
339
|
+
}
|
|
340
|
+
return rule;
|
|
341
|
+
})
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export const getRuleTree = (children) => {
|
|
345
|
+
const tree = [];
|
|
346
|
+
children && children.forEach(rule => {
|
|
347
|
+
if (rule._fc_drag_tag) {
|
|
348
|
+
const item = {
|
|
349
|
+
id: rule.__fc__.id,
|
|
350
|
+
rule,
|
|
351
|
+
children: getRuleTree(rule.children),
|
|
352
|
+
};
|
|
353
|
+
if (!item.children.length) {
|
|
354
|
+
delete item.children;
|
|
355
|
+
}
|
|
356
|
+
tree.push(item);
|
|
357
|
+
} else {
|
|
358
|
+
tree.push(...getRuleTree(rule.children));
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
return tree;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
export const getFormRuleDescription = (tree) => {
|
|
366
|
+
const getTree = (children) => {
|
|
367
|
+
const tree = [];
|
|
368
|
+
children && children.forEach(rule => {
|
|
369
|
+
if (rule.field) {
|
|
370
|
+
rule.children = getTree(rule.children || []);
|
|
371
|
+
if (!rule.children.length) {
|
|
372
|
+
delete rule.children;
|
|
373
|
+
}
|
|
374
|
+
tree.push(rule);
|
|
375
|
+
} else {
|
|
376
|
+
tree.push(...getTree(rule.children || []));
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
return tree;
|
|
380
|
+
}
|
|
381
|
+
return getTree(tree);
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
export const getRuleDescription = (children) => {
|
|
385
|
+
const getTree = (children) => {
|
|
386
|
+
const tree = [];
|
|
387
|
+
children && children.forEach(rule => {
|
|
388
|
+
if (typeof rule !== 'object') {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (rule._fc_drag_tag) {
|
|
392
|
+
const item = {
|
|
393
|
+
_fc_id: rule._fc_id,
|
|
394
|
+
type: rule.type,
|
|
395
|
+
field: rule.field,
|
|
396
|
+
title: rule.title,
|
|
397
|
+
name: rule.name,
|
|
398
|
+
slot: rule.slot,
|
|
399
|
+
props: {...rule.props || {}},
|
|
400
|
+
options: copy(rule.options),
|
|
401
|
+
children: getTree(rule.children || [])
|
|
402
|
+
};
|
|
403
|
+
if (rule.children && typeof rule.children[0] === 'string') {
|
|
404
|
+
item.content = rule.children[0];
|
|
405
|
+
}
|
|
406
|
+
if (!item.children.length) {
|
|
407
|
+
delete item.children;
|
|
408
|
+
}
|
|
409
|
+
tree.push(item);
|
|
410
|
+
} else {
|
|
411
|
+
tree.push(...getTree(rule.children));
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
return tree;
|
|
415
|
+
}
|
|
416
|
+
return getTree(children);
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
export function getInjectArg(t) {
|
|
420
|
+
return {
|
|
421
|
+
name: '$inject',
|
|
422
|
+
info: `$inject <a href="https://view.form-create.com/inject" target="_blank">${t('form.document')}</a>`,
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export function isElementInside(x, y, element) {
|
|
427
|
+
const rect = element.getBoundingClientRect();
|
|
428
|
+
return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function isNull(v) {
|
|
432
|
+
return ['', null, undefined].indexOf(v) !== -1;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export function escapeRegExp(str) {
|
|
436
|
+
return str.replace(/[\ .*+?^${}()|[\]\\]/g, '\\$&');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export function compareVersion(v1, v2) {
|
|
440
|
+
const a1 = v1.split('.');
|
|
441
|
+
const a2 = v2.split('.');
|
|
442
|
+
const minLength = Math.min(a1.length, a2.length);
|
|
443
|
+
|
|
444
|
+
for (var i = 0; i < minLength; i++) {
|
|
445
|
+
var diff = parseInt(a1[i], 10) - parseInt(a2[i], 10);
|
|
446
|
+
if (diff > 0) {
|
|
447
|
+
return 1;
|
|
448
|
+
} else if (diff < 0) {
|
|
449
|
+
return -1;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return a1.length === a2.length ? 0 : (a1.length < a2.length ? -1 : 1);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
export function copyTextToClipboard(text) {
|
|
457
|
+
const textArea = document.createElement('textarea');
|
|
458
|
+
|
|
459
|
+
textArea.style.position = 'fixed';
|
|
460
|
+
textArea.style.top = 0;
|
|
461
|
+
textArea.style.left = '-9999px';
|
|
462
|
+
|
|
463
|
+
textArea.value = text;
|
|
464
|
+
|
|
465
|
+
document.body.appendChild(textArea);
|
|
466
|
+
|
|
467
|
+
textArea.focus();
|
|
468
|
+
textArea.select();
|
|
469
|
+
|
|
470
|
+
try {
|
|
471
|
+
document.execCommand('copy');
|
|
472
|
+
} catch (err) {
|
|
473
|
+
console.log('Oops, unable to copy');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
message('已复制!', 'success');
|
|
477
|
+
|
|
478
|
+
document.body.removeChild(textArea);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export function uniqueArray(arr) {
|
|
482
|
+
return arr.filter((item, index) => arr.indexOf(item) === index);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* 节流函数 - 在指定时间内只执行一次函数
|
|
487
|
+
* @param {Function} func 要节流的函数
|
|
488
|
+
* @param {number} delay 延迟时间(毫秒)
|
|
489
|
+
* @param {Object} options 选项
|
|
490
|
+
* @param {boolean} options.leading 是否在开始时执行
|
|
491
|
+
* @param {boolean} options.trailing 是否在结束时执行
|
|
492
|
+
* @returns {Function} 节流后的函数
|
|
493
|
+
*/
|
|
494
|
+
export function throttle(func, delay, options = {}) {
|
|
495
|
+
const {leading = false, trailing = true} = options;
|
|
496
|
+
let timeout;
|
|
497
|
+
let previous = 0;
|
|
498
|
+
let result;
|
|
499
|
+
let lastArgs;
|
|
500
|
+
let lastContext;
|
|
501
|
+
|
|
502
|
+
const throttled = function (...args) {
|
|
503
|
+
const now = Date.now();
|
|
504
|
+
const context = this;
|
|
505
|
+
|
|
506
|
+
// 保存最后一次调用的参数和上下文
|
|
507
|
+
lastArgs = args;
|
|
508
|
+
lastContext = context;
|
|
509
|
+
|
|
510
|
+
// 如果是第一次调用且不需要在开始时执行
|
|
511
|
+
if (!previous && !leading) {
|
|
512
|
+
previous = now;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const remaining = delay - (now - previous);
|
|
516
|
+
|
|
517
|
+
if (remaining <= 0 || remaining > delay) {
|
|
518
|
+
if (timeout) {
|
|
519
|
+
clearTimeout(timeout);
|
|
520
|
+
timeout = null;
|
|
521
|
+
}
|
|
522
|
+
previous = now;
|
|
523
|
+
result = func.apply(lastContext, lastArgs);
|
|
524
|
+
} else if (!timeout && trailing) {
|
|
525
|
+
timeout = setTimeout(() => {
|
|
526
|
+
previous = leading ? 0 : Date.now();
|
|
527
|
+
timeout = null;
|
|
528
|
+
result = func.apply(lastContext, lastArgs);
|
|
529
|
+
}, remaining);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return result;
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
throttled.cancel = function () {
|
|
536
|
+
clearTimeout(timeout);
|
|
537
|
+
previous = 0;
|
|
538
|
+
timeout = null;
|
|
539
|
+
lastArgs = null;
|
|
540
|
+
lastContext = null;
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
return throttled;
|
|
544
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON 差异比较工具
|
|
3
|
+
* 用于比较两个 JSON 对象并生成差异信息
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 计算最长公共子序列(LCS)
|
|
8
|
+
* @param {Array} oldLines - 旧的行数组
|
|
9
|
+
* @param {Array} newLines - 新的行数组
|
|
10
|
+
* @returns {Array} LCS 矩阵
|
|
11
|
+
*/
|
|
12
|
+
function computeLCS(oldLines, newLines) {
|
|
13
|
+
const m = oldLines.length;
|
|
14
|
+
const n = newLines.length;
|
|
15
|
+
const lcs = Array(m + 1)
|
|
16
|
+
.fill(null)
|
|
17
|
+
.map(() => Array(n + 1).fill(0));
|
|
18
|
+
|
|
19
|
+
for (let i = 1; i <= m; i++) {
|
|
20
|
+
for (let j = 1; j <= n; j++) {
|
|
21
|
+
if (oldLines[i - 1] === newLines[j - 1]) {
|
|
22
|
+
lcs[i][j] = lcs[i - 1][j - 1] + 1;
|
|
23
|
+
} else {
|
|
24
|
+
lcs[i][j] = Math.max(lcs[i - 1][j], lcs[i][j - 1]);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return lcs;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 根据 LCS 矩阵生成差异操作序列
|
|
34
|
+
* @param {Array} lcs - LCS 矩阵
|
|
35
|
+
* @param {Array} oldLines - 旧的行数组
|
|
36
|
+
* @param {Array} newLines - 新的行数组
|
|
37
|
+
* @param {number} i - 当前旧行索引
|
|
38
|
+
* @param {number} j - 当前新行索引
|
|
39
|
+
* @returns {Array} 差异操作数组
|
|
40
|
+
*/
|
|
41
|
+
function generateDiffOps(lcs, oldLines, newLines, i = oldLines.length, j = newLines.length) {
|
|
42
|
+
if (i === 0 && j === 0) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
|
|
47
|
+
const ops = generateDiffOps(lcs, oldLines, newLines, i - 1, j - 1);
|
|
48
|
+
ops.push({
|
|
49
|
+
type: 'unchanged',
|
|
50
|
+
oldLine: oldLines[i - 1],
|
|
51
|
+
newLine: newLines[j - 1],
|
|
52
|
+
oldLineIndex: i - 1,
|
|
53
|
+
newLineIndex: j - 1,
|
|
54
|
+
});
|
|
55
|
+
return ops;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (j > 0 && (i === 0 || lcs[i][j - 1] >= lcs[i - 1][j])) {
|
|
59
|
+
const ops = generateDiffOps(lcs, oldLines, newLines, i, j - 1);
|
|
60
|
+
ops.push({
|
|
61
|
+
type: 'added',
|
|
62
|
+
oldLine: '',
|
|
63
|
+
newLine: newLines[j - 1],
|
|
64
|
+
oldLineIndex: -1,
|
|
65
|
+
newLineIndex: j - 1,
|
|
66
|
+
});
|
|
67
|
+
return ops;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (i > 0 && (j === 0 || lcs[i][j - 1] < lcs[i - 1][j])) {
|
|
71
|
+
const ops = generateDiffOps(lcs, oldLines, newLines, i - 1, j);
|
|
72
|
+
ops.push({
|
|
73
|
+
type: 'removed',
|
|
74
|
+
oldLine: oldLines[i - 1],
|
|
75
|
+
newLine: '',
|
|
76
|
+
oldLineIndex: i - 1,
|
|
77
|
+
newLineIndex: -1,
|
|
78
|
+
});
|
|
79
|
+
return ops;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 生成行级别的差异显示
|
|
87
|
+
* @param {Object} oldJson - 旧的 JSON 对象
|
|
88
|
+
* @param {Object} newJson - 新的 JSON 对象
|
|
89
|
+
* @param {number} contextLines - 上下文行数,默认5行
|
|
90
|
+
* @returns {Array} 行级别的差异数组
|
|
91
|
+
*/
|
|
92
|
+
export function generateLineDiff(oldJson, newJson, contextLines = 5) {
|
|
93
|
+
const oldStr = JSON.stringify(oldJson, null, 2);
|
|
94
|
+
const newStr = JSON.stringify(newJson, null, 2);
|
|
95
|
+
|
|
96
|
+
const oldLines = oldStr.split('\n');
|
|
97
|
+
const newLines = newStr.split('\n');
|
|
98
|
+
|
|
99
|
+
// 使用 LCS 算法计算差异
|
|
100
|
+
const lcs = computeLCS(oldLines, newLines);
|
|
101
|
+
const diffOps = generateDiffOps(lcs, oldLines, newLines);
|
|
102
|
+
|
|
103
|
+
// 找出所有有差异的行的索引
|
|
104
|
+
const changedIndices = new Set();
|
|
105
|
+
diffOps.forEach((op, index) => {
|
|
106
|
+
if (op.type !== 'unchanged') {
|
|
107
|
+
changedIndices.add(index);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// 如果没有差异,返回空数组
|
|
112
|
+
if (changedIndices.size === 0) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 找出需要显示的行(差异行 + 上下文)
|
|
117
|
+
const showIndices = new Set();
|
|
118
|
+
|
|
119
|
+
// 将连续的差异行合并为块
|
|
120
|
+
const changeBlocks = [];
|
|
121
|
+
const sortedChangedIndices = Array.from(changedIndices).sort((a, b) => a - b);
|
|
122
|
+
|
|
123
|
+
let currentBlock = { start: sortedChangedIndices[0], end: sortedChangedIndices[0] };
|
|
124
|
+
|
|
125
|
+
for (let i = 1; i < sortedChangedIndices.length; i++) {
|
|
126
|
+
if (sortedChangedIndices[i] === currentBlock.end + 1) {
|
|
127
|
+
currentBlock.end = sortedChangedIndices[i];
|
|
128
|
+
} else {
|
|
129
|
+
changeBlocks.push(currentBlock);
|
|
130
|
+
currentBlock = { start: sortedChangedIndices[i], end: sortedChangedIndices[i] };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
changeBlocks.push(currentBlock);
|
|
134
|
+
|
|
135
|
+
// 为每个差异块添加上下文
|
|
136
|
+
changeBlocks.forEach(block => {
|
|
137
|
+
// 添加差异行本身
|
|
138
|
+
for (let i = block.start; i <= block.end; i++) {
|
|
139
|
+
showIndices.add(i);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 添加上下文行
|
|
143
|
+
for (let i = 1; i <= contextLines; i++) {
|
|
144
|
+
// 前面的上下文
|
|
145
|
+
if (block.start - i >= 0) {
|
|
146
|
+
showIndices.add(block.start - i);
|
|
147
|
+
}
|
|
148
|
+
// 后面的上下文
|
|
149
|
+
if (block.end + i < diffOps.length) {
|
|
150
|
+
showIndices.add(block.end + i);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// 生成最终结果,只包含需要显示的行
|
|
156
|
+
const result = [];
|
|
157
|
+
const sortedIndices = Array.from(showIndices).sort((a, b) => a - b);
|
|
158
|
+
|
|
159
|
+
sortedIndices.forEach(index => {
|
|
160
|
+
const op = diffOps[index];
|
|
161
|
+
result.push({
|
|
162
|
+
type: op.type,
|
|
163
|
+
oldLine: op.oldLine,
|
|
164
|
+
newLine: op.newLine,
|
|
165
|
+
lineNumber: index + 1,
|
|
166
|
+
originalIndex: index,
|
|
167
|
+
isSame: op.type === 'unchanged',
|
|
168
|
+
isContext: op.type === 'unchanged' && !changedIndices.has(index),
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return result;
|
|
173
|
+
}
|