@tuoyuan/gateway-api-select-config 1.2.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/LICENSE +21 -0
- package/README.md +605 -0
- package/dist/gateway-api-select-config.css +3 -0
- package/dist/gateway-api-select-config.es.js +1767 -0
- package/dist/gateway-api-select-config.umd.js +28 -0
- package/package.json +60 -0
- package/src/components/ApiConfig.vue +2264 -0
- package/src/components/SchemaFieldEditor.vue +572 -0
- package/src/index.js +70 -0
- package/src/utils/dataExtractor.js +359 -0
- package/src/utils/dataFilters.js +300 -0
- package/src/utils/reqMappingUtils.js +147 -0
- package/src/utils/schemaUtils.js +197 -0
- package/src/utils/treeUtils.js +134 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="schema-field-editor">
|
|
3
|
+
<a-empty v-if="fields.length === 0" description="未定义输出字段">
|
|
4
|
+
<template #image>
|
|
5
|
+
<icon-file />
|
|
6
|
+
</template>
|
|
7
|
+
<div class="empty-desc">
|
|
8
|
+
此 API 的 schema 定义不完整(缺少 items.properties)。<br>
|
|
9
|
+
保存后将自动返回完整的原始数据,无需配置字段映射。
|
|
10
|
+
</div>
|
|
11
|
+
</a-empty>
|
|
12
|
+
|
|
13
|
+
<div v-else class="field-list">
|
|
14
|
+
<div
|
|
15
|
+
v-for="field in fields"
|
|
16
|
+
:key="field.key"
|
|
17
|
+
class="field-item"
|
|
18
|
+
>
|
|
19
|
+
<!-- 两列布局:左侧标签,右侧值 -->
|
|
20
|
+
<a-row :gutter="8" class="field-row">
|
|
21
|
+
<!-- 左侧:字段信息 -->
|
|
22
|
+
<a-col :span="5" class="field-label-col">
|
|
23
|
+
<!-- 字段名和标签在同一行 -->
|
|
24
|
+
<div class="field-header">
|
|
25
|
+
<span class="field-name">{{ field.label || field.name }}</span>
|
|
26
|
+
<div class="field-tags">
|
|
27
|
+
<a-tag color="arcoblue" size="small" class="type-tag">
|
|
28
|
+
{{ field.type }}
|
|
29
|
+
</a-tag>
|
|
30
|
+
<a-tag v-if="field.required" color="red" size="mini" class="required-tag">
|
|
31
|
+
必填
|
|
32
|
+
</a-tag>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</a-col>
|
|
36
|
+
|
|
37
|
+
<!-- 右侧:输入和选择区域 - 统一容器 -->
|
|
38
|
+
<a-col :span="19" class="field-value-col">
|
|
39
|
+
<div class="value-container">
|
|
40
|
+
<!-- 路径输入 -->
|
|
41
|
+
<div class="input-wrapper">
|
|
42
|
+
<a-input
|
|
43
|
+
:model-value="fieldPaths[field.key] || ''"
|
|
44
|
+
@input="handleInput(field.key, $event)"
|
|
45
|
+
:placeholder="getPlaceholder(field)"
|
|
46
|
+
allow-clear
|
|
47
|
+
class="path-input"
|
|
48
|
+
>
|
|
49
|
+
<template #prefix>
|
|
50
|
+
<icon-link />
|
|
51
|
+
</template>
|
|
52
|
+
<template #suffix>
|
|
53
|
+
<a-button
|
|
54
|
+
v-if="getExamples(field).length > 0"
|
|
55
|
+
@click="showExamples(field)"
|
|
56
|
+
type="text"
|
|
57
|
+
size="small"
|
|
58
|
+
class="example-trigger-btn"
|
|
59
|
+
>
|
|
60
|
+
<template #icon>
|
|
61
|
+
<icon-bulb />
|
|
62
|
+
</template>
|
|
63
|
+
</a-button>
|
|
64
|
+
</template>
|
|
65
|
+
</a-input>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<!-- 格式化配置 -->
|
|
69
|
+
<a-row v-if="shouldShowFormat(field)" :gutter="12" align="center" class="format-row">
|
|
70
|
+
<a-col :span="4">
|
|
71
|
+
<div class="format-label">格式化</div>
|
|
72
|
+
</a-col>
|
|
73
|
+
<a-col :span="20">
|
|
74
|
+
<a-select
|
|
75
|
+
:model-value="fieldFormats[field.key] || ''"
|
|
76
|
+
@change="handleFormatChange(field.key, $event)"
|
|
77
|
+
placeholder="选择格式化方式"
|
|
78
|
+
allow-clear
|
|
79
|
+
size="small"
|
|
80
|
+
class="format-select"
|
|
81
|
+
>
|
|
82
|
+
<a-option value="">无</a-option>
|
|
83
|
+
<a-optgroup v-if="isDateField(field)" label="日期格式">
|
|
84
|
+
<a-option value="YYYY-MM-DD">YYYY-MM-DD</a-option>
|
|
85
|
+
<a-option value="YYYY-MM-DD HH:mm:ss">YYYY-MM-DD HH:mm:ss</a-option>
|
|
86
|
+
<a-option value="YYYY/MM/DD">YYYY/MM/DD</a-option>
|
|
87
|
+
<a-option value="timestamp">时间戳(毫秒)</a-option>
|
|
88
|
+
<a-option value="timestamp_s">时间戳(秒)</a-option>
|
|
89
|
+
</a-optgroup>
|
|
90
|
+
<a-optgroup v-if="isNumberField(field)" label="数字格式">
|
|
91
|
+
<a-option value="number">数字</a-option>
|
|
92
|
+
<a-option value="integer">整数</a-option>
|
|
93
|
+
<a-option value="currency">货币</a-option>
|
|
94
|
+
<a-option value="percent">百分比</a-option>
|
|
95
|
+
</a-optgroup>
|
|
96
|
+
<a-optgroup label="通用格式">
|
|
97
|
+
<a-option value="string">字符串</a-option>
|
|
98
|
+
<a-option value="boolean">布尔值</a-option>
|
|
99
|
+
<a-option value="json">JSON</a-option>
|
|
100
|
+
</a-optgroup>
|
|
101
|
+
</a-select>
|
|
102
|
+
</a-col>
|
|
103
|
+
</a-row>
|
|
104
|
+
</div>
|
|
105
|
+
</a-col>
|
|
106
|
+
</a-row>
|
|
107
|
+
|
|
108
|
+
<!-- 示例栏:单独一行,浅灰背景 -->
|
|
109
|
+
<div v-if="activeExampleField === field.key" class="examples-section">
|
|
110
|
+
<div class="examples-header">
|
|
111
|
+
<icon-bulb class="examples-icon" />
|
|
112
|
+
<span class="examples-title">常用路径示例</span>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="examples-list">
|
|
115
|
+
<div
|
|
116
|
+
v-for="(example, idx) in getExamples(field)"
|
|
117
|
+
:key="idx"
|
|
118
|
+
class="example-item"
|
|
119
|
+
@click="applyExample(field.key, example.path)"
|
|
120
|
+
>
|
|
121
|
+
<code class="example-code">{{ example.path }}</code>
|
|
122
|
+
<span class="example-desc">{{ example.desc }}</span>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="examples-footer">
|
|
126
|
+
点击示例可直接应用
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</template>
|
|
133
|
+
|
|
134
|
+
<script>
|
|
135
|
+
import { computed, ref } from 'vue';
|
|
136
|
+
import {
|
|
137
|
+
Empty as AEmpty,
|
|
138
|
+
Space as ASpace,
|
|
139
|
+
Tag as ATag,
|
|
140
|
+
Input as AInput,
|
|
141
|
+
Button as AButton,
|
|
142
|
+
Select as ASelect,
|
|
143
|
+
Option as AOption,
|
|
144
|
+
Optgroup as AOptgroup,
|
|
145
|
+
Row as ARow,
|
|
146
|
+
Col as ACol
|
|
147
|
+
} from '@arco-design/web-vue';
|
|
148
|
+
import {
|
|
149
|
+
IconFile,
|
|
150
|
+
IconLink,
|
|
151
|
+
IconBulb
|
|
152
|
+
} from '@arco-design/web-vue/es/icon';
|
|
153
|
+
import { extractSchemaFields } from '../utils/schemaUtils';
|
|
154
|
+
|
|
155
|
+
export default {
|
|
156
|
+
name: 'SchemaFieldEditor',
|
|
157
|
+
components: {
|
|
158
|
+
AEmpty,
|
|
159
|
+
ASpace,
|
|
160
|
+
ATag,
|
|
161
|
+
AInput,
|
|
162
|
+
AButton,
|
|
163
|
+
ASelect,
|
|
164
|
+
AOption,
|
|
165
|
+
AOptgroup,
|
|
166
|
+
ARow,
|
|
167
|
+
ACol,
|
|
168
|
+
IconFile,
|
|
169
|
+
IconLink,
|
|
170
|
+
IconBulb
|
|
171
|
+
},
|
|
172
|
+
props: {
|
|
173
|
+
schema: {
|
|
174
|
+
type: Object,
|
|
175
|
+
required: true
|
|
176
|
+
},
|
|
177
|
+
fieldPaths: {
|
|
178
|
+
type: Object,
|
|
179
|
+
default: () => ({})
|
|
180
|
+
},
|
|
181
|
+
fieldFormats: {
|
|
182
|
+
type: Object,
|
|
183
|
+
default: () => ({})
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
emits: ['update', 'updateFormat'],
|
|
187
|
+
setup(props, { emit }) {
|
|
188
|
+
const fields = computed(() => {
|
|
189
|
+
return extractSchemaFields(props.schema);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const activeExampleField = ref(null);
|
|
193
|
+
|
|
194
|
+
const handleInput = (key, value) => {
|
|
195
|
+
emit('update', { key, path: value });
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const handleFormatChange = (key, format) => {
|
|
199
|
+
emit('updateFormat', { key, format });
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const shouldShowFormat = (field) => {
|
|
203
|
+
return !['array', 'object'].includes(field.type);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const isDateField = (field) => {
|
|
207
|
+
const name = field.name.toLowerCase();
|
|
208
|
+
return name.includes('time') || name.includes('date') ||
|
|
209
|
+
name.includes('created') || name.includes('updated') ||
|
|
210
|
+
field.type === 'string' && (field.format === 'date' || field.format === 'date-time');
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const isNumberField = (field) => {
|
|
214
|
+
return field.type === 'number' || field.type === 'integer';
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const getPlaceholder = (field) => {
|
|
218
|
+
return `例如:@.${field.name}`;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const getExamples = (field) => {
|
|
222
|
+
const examples = [];
|
|
223
|
+
const fieldName = field.name;
|
|
224
|
+
|
|
225
|
+
if (field.type !== 'array' && field.type !== 'object') {
|
|
226
|
+
examples.push(
|
|
227
|
+
{ path: `@.${fieldName}`, desc: '直接取当前对象的字段' },
|
|
228
|
+
{ path: `@.data.${fieldName}`, desc: '取 data 对象中的字段' },
|
|
229
|
+
{ path: `@.result.${fieldName}`, desc: '取 result 对象中的字段' }
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
if (fieldName.toLowerCase().includes('id')) {
|
|
233
|
+
examples.push({ path: `@.${fieldName}`, desc: 'ID 字段(推荐)' });
|
|
234
|
+
}
|
|
235
|
+
if (fieldName.toLowerCase().includes('name')) {
|
|
236
|
+
examples.push({ path: `@.${fieldName}`, desc: '名称字段(推荐)' });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else if (field.type === 'array') {
|
|
240
|
+
examples.push(
|
|
241
|
+
{ path: `@.${fieldName}`, desc: '直接取当前对象的数组' },
|
|
242
|
+
{ path: `@.data.${fieldName}`, desc: '取 data 中的数组' },
|
|
243
|
+
{ path: '@.data.list', desc: '取 data.list 数组' }
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
else if (field.type === 'object') {
|
|
247
|
+
examples.push(
|
|
248
|
+
{ path: `@.${fieldName}`, desc: '直接取当前对象的子对象' },
|
|
249
|
+
{ path: `@.data.${fieldName}`, desc: '取 data 中的子对象' }
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return examples;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const showExamples = (field) => {
|
|
257
|
+
if (activeExampleField.value === field.key) {
|
|
258
|
+
activeExampleField.value = null;
|
|
259
|
+
} else {
|
|
260
|
+
activeExampleField.value = field.key;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const applyExample = (key, path) => {
|
|
265
|
+
emit('update', { key, path });
|
|
266
|
+
activeExampleField.value = null;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
fields,
|
|
271
|
+
activeExampleField,
|
|
272
|
+
handleInput,
|
|
273
|
+
handleFormatChange,
|
|
274
|
+
shouldShowFormat,
|
|
275
|
+
isDateField,
|
|
276
|
+
isNumberField,
|
|
277
|
+
getPlaceholder,
|
|
278
|
+
getExamples,
|
|
279
|
+
showExamples,
|
|
280
|
+
applyExample
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
</script>
|
|
285
|
+
|
|
286
|
+
<style scoped>
|
|
287
|
+
.schema-field-editor {
|
|
288
|
+
width: 100%;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.empty-desc {
|
|
292
|
+
margin-top: 12px;
|
|
293
|
+
font-size: 14px;
|
|
294
|
+
color: #86909c;
|
|
295
|
+
line-height: 1.6;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* 字段列表 */
|
|
299
|
+
.field-list {
|
|
300
|
+
display: flex;
|
|
301
|
+
flex-direction: column;
|
|
302
|
+
gap: 0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* 字段项 */
|
|
306
|
+
.field-item {
|
|
307
|
+
background: #ffffff;
|
|
308
|
+
border-bottom: 1px solid #e5e6eb;
|
|
309
|
+
transition: all 0.3s ease;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.field-item:last-child {
|
|
313
|
+
border-bottom: none;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.field-item:hover {
|
|
317
|
+
background: #f7f8fa;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* 字段行 */
|
|
321
|
+
.field-row {
|
|
322
|
+
padding: 20px 0;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* 左侧:字段信息列 */
|
|
326
|
+
.field-label-col {
|
|
327
|
+
display: flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/* 字段头部 - 字段名和标签在同一行 */
|
|
332
|
+
.field-header {
|
|
333
|
+
display: flex;
|
|
334
|
+
flex-direction: row;
|
|
335
|
+
align-items: center;
|
|
336
|
+
gap: 12px;
|
|
337
|
+
width: 100%;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* 字段名称 - 第一优先级 */
|
|
341
|
+
.field-name {
|
|
342
|
+
font-size: 15px;
|
|
343
|
+
font-weight: 600;
|
|
344
|
+
color: #1d2129;
|
|
345
|
+
line-height: 1.4;
|
|
346
|
+
letter-spacing: 0.2px;
|
|
347
|
+
white-space: nowrap;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* 字段标签组 */
|
|
351
|
+
.field-tags {
|
|
352
|
+
display: flex;
|
|
353
|
+
align-items: center;
|
|
354
|
+
gap: 6px;
|
|
355
|
+
flex-wrap: nowrap;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* 类型标签 - 浅蓝背景 */
|
|
359
|
+
.type-tag {
|
|
360
|
+
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
|
|
361
|
+
font-size: 12px;
|
|
362
|
+
font-weight: 500;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/* 必填标签 - 红色,小字号 */
|
|
366
|
+
.required-tag {
|
|
367
|
+
font-size: 11px;
|
|
368
|
+
font-weight: 500;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/* 右侧:值列 */
|
|
372
|
+
.field-value-col {
|
|
373
|
+
display: flex;
|
|
374
|
+
flex-direction: column;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* 统一容器 - 给输入和格式化区域统一背景 */
|
|
378
|
+
.value-container {
|
|
379
|
+
background: #f7f8fa;
|
|
380
|
+
border: 1px solid #e5e6eb;
|
|
381
|
+
border-radius: 6px;
|
|
382
|
+
padding: 14px;
|
|
383
|
+
display: flex;
|
|
384
|
+
flex-direction: column;
|
|
385
|
+
gap: 12px;
|
|
386
|
+
transition: all 0.3s ease;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.value-container:hover {
|
|
390
|
+
border-color: #c9cdd4;
|
|
391
|
+
background: #fafbfc;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/* 输入框包装 */
|
|
395
|
+
.input-wrapper {
|
|
396
|
+
width: 100%;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/* 路径输入框 - 增强交互 */
|
|
400
|
+
.path-input {
|
|
401
|
+
transition: all 0.3s ease;
|
|
402
|
+
background: #ffffff;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.path-input:hover {
|
|
406
|
+
border-color: #4080ff;
|
|
407
|
+
box-shadow: 0 0 0 2px rgba(64, 128, 255, 0.1);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.path-input:focus-within {
|
|
411
|
+
border-color: #4080ff;
|
|
412
|
+
box-shadow: 0 0 0 3px rgba(64, 128, 255, 0.15);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* 示例触发按钮 */
|
|
416
|
+
.example-trigger-btn {
|
|
417
|
+
color: #f7ba1e;
|
|
418
|
+
transition: all 0.2s ease;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.example-trigger-btn:hover {
|
|
422
|
+
color: #f7ba1e;
|
|
423
|
+
transform: scale(1.1);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* 格式化行 */
|
|
427
|
+
.format-row {
|
|
428
|
+
margin-top: 0;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/* 格式化标签 */
|
|
432
|
+
.format-label {
|
|
433
|
+
font-size: 13px;
|
|
434
|
+
color: #4e5969;
|
|
435
|
+
font-weight: 500;
|
|
436
|
+
line-height: 32px;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* 格式化选择器 - 增强交互 */
|
|
440
|
+
.format-select {
|
|
441
|
+
transition: all 0.3s ease;
|
|
442
|
+
background: #ffffff;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.format-select:hover {
|
|
446
|
+
border-color: #4080ff;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.format-select:focus-within {
|
|
450
|
+
border-color: #4080ff;
|
|
451
|
+
box-shadow: 0 0 0 2px rgba(64, 128, 255, 0.1);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/* 示例区域 - 浅灰背景,单独一行 */
|
|
455
|
+
.examples-section {
|
|
456
|
+
background: #f2f3f5;
|
|
457
|
+
border-top: 1px solid #e5e6eb;
|
|
458
|
+
padding: 20px 24px;
|
|
459
|
+
margin: 0;
|
|
460
|
+
animation: slideDown 0.3s ease;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
@keyframes slideDown {
|
|
464
|
+
from {
|
|
465
|
+
opacity: 0;
|
|
466
|
+
max-height: 0;
|
|
467
|
+
padding-top: 0;
|
|
468
|
+
padding-bottom: 0;
|
|
469
|
+
}
|
|
470
|
+
to {
|
|
471
|
+
opacity: 1;
|
|
472
|
+
max-height: 500px;
|
|
473
|
+
padding-top: 16px;
|
|
474
|
+
padding-bottom: 16px;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/* 示例头部 */
|
|
479
|
+
.examples-header {
|
|
480
|
+
display: flex;
|
|
481
|
+
align-items: center;
|
|
482
|
+
gap: 8px;
|
|
483
|
+
margin-bottom: 12px;
|
|
484
|
+
padding-bottom: 8px;
|
|
485
|
+
border-bottom: 1px solid #e5e6eb;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.examples-icon {
|
|
489
|
+
color: #f7ba1e;
|
|
490
|
+
font-size: 16px;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.examples-title {
|
|
494
|
+
font-size: 13px;
|
|
495
|
+
font-weight: 600;
|
|
496
|
+
color: #4e5969;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/* 示例列表 */
|
|
500
|
+
.examples-list {
|
|
501
|
+
display: flex;
|
|
502
|
+
flex-direction: column;
|
|
503
|
+
gap: 8px;
|
|
504
|
+
margin-bottom: 12px;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/* 示例项 */
|
|
508
|
+
.example-item {
|
|
509
|
+
display: flex;
|
|
510
|
+
align-items: center;
|
|
511
|
+
gap: 12px;
|
|
512
|
+
padding: 10px 12px;
|
|
513
|
+
background: #ffffff;
|
|
514
|
+
border: 1px solid #e5e6eb;
|
|
515
|
+
border-radius: 6px;
|
|
516
|
+
cursor: pointer;
|
|
517
|
+
transition: all 0.2s ease;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.example-item:hover {
|
|
521
|
+
border-color: #4080ff;
|
|
522
|
+
background: #f2f7ff;
|
|
523
|
+
transform: translateX(4px);
|
|
524
|
+
box-shadow: 0 2px 8px rgba(64, 128, 255, 0.1);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/* 示例代码 - 等宽字体,浅灰背景 */
|
|
528
|
+
.example-code {
|
|
529
|
+
font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace;
|
|
530
|
+
font-size: 13px;
|
|
531
|
+
font-weight: 500;
|
|
532
|
+
color: #165dff;
|
|
533
|
+
background: #e8f3ff;
|
|
534
|
+
padding: 4px 8px;
|
|
535
|
+
border-radius: 4px;
|
|
536
|
+
white-space: nowrap;
|
|
537
|
+
border: 1px solid #bedaff;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/* 示例描述 */
|
|
541
|
+
.example-desc {
|
|
542
|
+
font-size: 13px;
|
|
543
|
+
color: #86909c;
|
|
544
|
+
flex: 1;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/* 示例底部提示 */
|
|
548
|
+
.examples-footer {
|
|
549
|
+
font-size: 12px;
|
|
550
|
+
color: #86909c;
|
|
551
|
+
text-align: center;
|
|
552
|
+
padding-top: 8px;
|
|
553
|
+
border-top: 1px solid #e5e6eb;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/* 响应式 */
|
|
557
|
+
@media (max-width: 768px) {
|
|
558
|
+
.field-row {
|
|
559
|
+
flex-direction: column;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.field-label-col,
|
|
563
|
+
.field-value-col {
|
|
564
|
+
width: 100% !important;
|
|
565
|
+
max-width: 100% !important;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.field-label-col {
|
|
569
|
+
margin-bottom: 12px;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
</style>
|
package/src/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 网关接口配置组件
|
|
3
|
+
* Gateway API Select Config Component
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import GatewayApiSelectConfig from './components/ApiConfig.vue';
|
|
7
|
+
import { extractDataBySchema, executeReqMapping } from './utils/dataExtractor';
|
|
8
|
+
import { extractOutputFields, formatConfig } from './utils/schemaUtils';
|
|
9
|
+
import {
|
|
10
|
+
extractArray,
|
|
11
|
+
noticeFilter,
|
|
12
|
+
newsFilter,
|
|
13
|
+
menuFilter,
|
|
14
|
+
userFilter,
|
|
15
|
+
applyFilter,
|
|
16
|
+
applyFilterByName,
|
|
17
|
+
filters,
|
|
18
|
+
fetchGatewayData
|
|
19
|
+
} from './utils/dataFilters';
|
|
20
|
+
|
|
21
|
+
// 导出组件(保持旧名称作为别名以兼容)
|
|
22
|
+
export { GatewayApiSelectConfig };
|
|
23
|
+
export { GatewayApiSelectConfig as ApiConfig }; // 别名,向后兼容
|
|
24
|
+
|
|
25
|
+
// 导出工具函数 - Schema 方式(原有的复杂方式)
|
|
26
|
+
export { extractDataBySchema, executeReqMapping, extractOutputFields, formatConfig };
|
|
27
|
+
|
|
28
|
+
// 导出过滤器 - Filter 方式(新的简单方式)
|
|
29
|
+
export {
|
|
30
|
+
extractArray,
|
|
31
|
+
noticeFilter,
|
|
32
|
+
newsFilter,
|
|
33
|
+
menuFilter,
|
|
34
|
+
userFilter,
|
|
35
|
+
applyFilter,
|
|
36
|
+
applyFilterByName,
|
|
37
|
+
filters,
|
|
38
|
+
fetchGatewayData // 统一的数据获取函数
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// 默认导出
|
|
42
|
+
export default GatewayApiSelectConfig;
|
|
43
|
+
|
|
44
|
+
// Vue 插件安装方法
|
|
45
|
+
export const install = (app) => {
|
|
46
|
+
app.component('GatewayApiSelectConfig', GatewayApiSelectConfig);
|
|
47
|
+
app.component('ApiConfig', GatewayApiSelectConfig); // 别名,向后兼容
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// 如果是直接通过 script 标签引入
|
|
51
|
+
if (typeof window !== 'undefined' && window.Vue) {
|
|
52
|
+
window.GatewayApiSelectConfig = GatewayApiSelectConfig;
|
|
53
|
+
window.ApiConfig = GatewayApiSelectConfig; // 别名,向后兼容
|
|
54
|
+
window.GatewayApiSelectConfigUtils = {
|
|
55
|
+
extractDataBySchema,
|
|
56
|
+
executeReqMapping,
|
|
57
|
+
extractOutputFields,
|
|
58
|
+
formatConfig,
|
|
59
|
+
// 新增过滤器
|
|
60
|
+
extractArray,
|
|
61
|
+
noticeFilter,
|
|
62
|
+
newsFilter,
|
|
63
|
+
menuFilter,
|
|
64
|
+
userFilter,
|
|
65
|
+
applyFilter,
|
|
66
|
+
applyFilterByName,
|
|
67
|
+
filters,
|
|
68
|
+
fetchGatewayData
|
|
69
|
+
};
|
|
70
|
+
}
|