w-web-api 1.0.52 → 1.0.55
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/README.md +1 -1
- package/dist/css/app.cad4a1ca.css +1 -0
- package/dist/index.tmp +4 -2
- package/dist/js/app-legacy.7687b23b.js +1 -0
- package/dist/js/app.7687b23b.js +1 -0
- package/dist/js/chunk-vendors-legacy.4c27cf51.js +15 -0
- package/dist/js/chunk-vendors.4c27cf51.js +15 -0
- package/dist/w-web-api.umd.js +2 -2
- package/dist/w-web-api.umd.js.map +1 -1
- package/docs/WWebApi.html +2 -2
- package/docs/index.html +1 -1
- package/docs/{WWebApi.mjs.html → server_WWebApi.mjs.html} +113 -66
- package/docs/src_components_Common_CheckYesNo.vue.html +187 -0
- package/g.getSettings.mjs +3 -0
- package/logs/2026-06-19T16.log +6 -0
- package/logs/2026-06-20T11.log +4 -0
- package/logs/2026-06-20T14.log +2 -0
- package/logs/2026-06-21T20.log +1 -0
- package/logs/2026-06-21T21.log +4 -0
- package/logs/2026-06-22T07.log +12 -0
- package/logs/2026-06-22T08.log +808 -0
- package/logs/2026-06-22T09.log +104 -0
- package/logs/2026-06-22T10.log +26 -0
- package/logs/2026-06-22T11.log +860 -0
- package/logs/2026-06-22T12.log +430 -0
- package/logs/2026-06-22T23.log +1547 -0
- package/logs/2026-06-23T00.log +473 -0
- package/logs/2026-06-23T08.log +231 -0
- package/logs/2026-06-23T09.log +179 -0
- package/logs/2026-07-01T11.log +488 -0
- package/logs/2026-07-09T22.log +370 -0
- package/logs/2026-07-09T23.log +370 -0
- package/logs/2026-07-10T12.log +284 -0
- package/logs/2026-07-10T13.log +777 -0
- package/logs/2026-07-11T17.log +2 -0
- package/package.json +20 -15
- package/public/index.html +9 -2
- package/server/WWebApi.mjs +110 -63
- package/server/procApis.mjs +62 -0
- package/server/procLang.mjs +372 -66
- package/server/procProxy.mjs +120 -0
- package/server/srLog.mjs +30 -0
- package/server/staLogs/filterVpfsByWindow.mjs +27 -0
- package/server/staLogs/staEvent.mjs +131 -0
- package/server/staLogs/test_staEvent.mjs +198 -0
- package/settings.json +18 -0
- package/src/App.vue +11 -7
- package/src/assets/scalar-ui.css +113 -0
- package/src/components/Common/CheckYes.vue +102 -0
- package/src/components/Common/CheckYesNo.vue +115 -0
- package/src/components/Common/LoadingWinBar.vue +114 -0
- package/src/components/Layout.vue +8 -26
- package/src/components/LayoutContent.vue +299 -350
- package/src/components/LayoutContentEdit.vue +577 -0
- package/src/components/LayoutContentStats.vue +485 -0
- package/src/components/LayoutContentTest.vue +781 -0
- package/src/components/MdPanel.vue +80 -58
- package/src/main.js +11 -8
- package/src/plugins/mUI.mjs +19 -39
- package/src/schema/tables/apis.mjs +30 -0
- package/src/store/mutations.mjs +0 -14
- package/src/store/types.mjs +0 -3
- package/srv.mjs +50 -20
- package/tableTags.json +1 -1
- package/z/344/277/256/346/255/243/350/246/217/345/212/203/346/233/270.md +246 -0
- package/.github/workflows/ci-test.yml +0 -24
- package/.vscode/launch.json +0 -18
- package/db/wapis/apis/data.mdb +0 -0
- package/db/wapis/apis/lock.mdb +0 -0
- package/dist/css/app.b5cf2ef5.css +0 -1
- package/dist/js/app-legacy.927b5679.js +0 -1
- package/dist/js/app.927b5679.js +0 -1
- package/dist/js/chunk-vendors-legacy.ffb0c6c5.js +0 -13
- package/dist/js/chunk-vendors.ffb0c6c5.js +0 -13
- package/src/plugins/mDataSelectorSchema.mjs +0 -30
- package/src/plugins/mShare.mjs +0 -44
- package/tableTags-web-api.json +0 -1
- package/test/all.test.mjs +0 -10
|
@@ -5,8 +5,28 @@
|
|
|
5
5
|
@domresize="resize"
|
|
6
6
|
:changeApis="changeApis"
|
|
7
7
|
>
|
|
8
|
+
<div style="display:flex; height:100%; width:100%;">
|
|
9
|
+
|
|
10
|
+
<!-- 左側垂直主選單(仿 w-web-sso):切換 API 工作區 / 統計資訊 -->
|
|
11
|
+
<div :style="`width:${widthMainMenu}px; flex-shrink:0; height:100%; box-sizing:border-box; background:var(--bg-2); border-right:1px solid var(--border); display:flex; flex-direction:column; padding:8px 0; gap:4px;`">
|
|
12
|
+
<button
|
|
13
|
+
v-for="m in mainMenus"
|
|
14
|
+
:key="m.id"
|
|
15
|
+
class="w-mm-btn"
|
|
16
|
+
:class="{on: section===m.id}"
|
|
17
|
+
@click="onClickMainMenu(m)"
|
|
18
|
+
>
|
|
19
|
+
<svg viewBox="0 0 24 24" width="22" height="22" style="display:block; margin:0 auto;"><path :d="m.icon" fill="currentColor"></path></svg>
|
|
20
|
+
<div class="w-mm-label">{{$t(m.textKey)}}</div>
|
|
21
|
+
</button>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<!-- 工作區 -->
|
|
25
|
+
<div style="flex:1; min-width:0; height:100%; position:relative;">
|
|
26
|
+
|
|
27
|
+
<template v-if="section==='api'">
|
|
8
28
|
|
|
9
|
-
|
|
29
|
+
<template v-if="syncState">
|
|
10
30
|
|
|
11
31
|
<WDrawer
|
|
12
32
|
:style="`height:${panelHeight}px; width:100%;`"
|
|
@@ -23,25 +43,44 @@
|
|
|
23
43
|
>
|
|
24
44
|
|
|
25
45
|
<template v-slot:drawer="props">
|
|
26
|
-
<div :style="`height:${props.height}px; background
|
|
46
|
+
<div :style="`height:${props.height}px; background:var(--bg-2); border-right:1px solid var(--border); position:relative;`">
|
|
47
|
+
|
|
48
|
+
<!-- 搜尋 + 新增API -->
|
|
49
|
+
<div :style="`height:${heightDrawerHeader}px; box-sizing:border-box; padding:8px 40px 8px 8px; border-bottom:1px solid var(--border); display:flex; gap:6px; align-items:center;`">
|
|
50
|
+
<input
|
|
51
|
+
v-model="search"
|
|
52
|
+
type="text"
|
|
53
|
+
:placeholder="$t('searchApiPlaceholder')"
|
|
54
|
+
style="flex:1; min-width:0; padding:5px 8px; border:1px solid var(--border); border-radius:var(--radius); font-size:0.85rem; outline:none; box-sizing:border-box; transition:border-color .15s, box-shadow .15s;"
|
|
55
|
+
@focus="$event.target.style.borderColor='var(--accent)'; $event.target.style.boxShadow='var(--focus)'"
|
|
56
|
+
@blur="$event.target.style.borderColor='var(--border)'; $event.target.style.boxShadow='none'"
|
|
57
|
+
/>
|
|
58
|
+
<button
|
|
59
|
+
@click="onClickAddApi"
|
|
60
|
+
class="w-btn-primary"
|
|
61
|
+
style="flex-shrink:0; white-space:nowrap;"
|
|
62
|
+
>+ {{$t('addApi')}}</button>
|
|
63
|
+
</div>
|
|
27
64
|
|
|
28
65
|
<template v-if="apisTree.length>0">
|
|
29
66
|
<WTree
|
|
30
|
-
:style="`height:${props.height}px;`"
|
|
31
|
-
:viewHeightMax="props.height"
|
|
67
|
+
:style="`height:${props.height-heightDrawerHeader}px;`"
|
|
68
|
+
:viewHeightMax="props.height-heightDrawerHeader"
|
|
32
69
|
:data="apisTree"
|
|
33
70
|
:iconHeight="lineHeightTree"
|
|
34
71
|
:defItemHeight="lineHeightTree"
|
|
35
72
|
:activable="true"
|
|
36
73
|
:itemActive="apiActive"
|
|
37
74
|
:funActive="funActive"
|
|
38
|
-
:itemTextColorActive="'
|
|
39
|
-
:itemBackgroundColorActive ="'rgba(
|
|
75
|
+
:itemTextColorActive="'var(--c-1)'"
|
|
76
|
+
:itemBackgroundColorActive ="'linear-gradient(to right, #0099ff 3px, rgba(0,153,255,0.08) 3px)'"
|
|
77
|
+
:indent="0.25"
|
|
78
|
+
:iconSize="12"
|
|
40
79
|
>
|
|
41
80
|
<template v-slot:item="props">
|
|
42
81
|
|
|
43
82
|
<div
|
|
44
|
-
:style="`display:flex; align-items:center; min-height:${lineHeightTree}px;`"
|
|
83
|
+
:style="`display:flex; align-items:center; min-height:${lineHeightTree}px; color:var(--c-3); font-size:11px; font-weight:600; letter-spacing:.05em; text-transform:uppercase;`"
|
|
45
84
|
v-if="props.data.type!=='node'"
|
|
46
85
|
>
|
|
47
86
|
{{props.data.key}}
|
|
@@ -49,12 +88,12 @@
|
|
|
49
88
|
|
|
50
89
|
<div
|
|
51
90
|
:style="`display:flex; align-items:center; min-height:${lineHeightTree}px; cursor:pointer;`"
|
|
52
|
-
@click="ckItem(getItemByKey(props.data.text))"
|
|
91
|
+
@click="ckItem(getItemByKey(props.data.text), props.data)"
|
|
53
92
|
v-else
|
|
54
93
|
>
|
|
55
94
|
|
|
56
95
|
<div style="padding-right:5px;">
|
|
57
|
-
<div
|
|
96
|
+
<div class="w-mb" :class="getMethodByKey(props.data.text).toLowerCase()">
|
|
58
97
|
{{getMethodByKey(props.data.text)}}
|
|
59
98
|
</div>
|
|
60
99
|
</div>
|
|
@@ -81,7 +120,7 @@
|
|
|
81
120
|
>
|
|
82
121
|
<WButtonCircle
|
|
83
122
|
:paddingStyle="{v:3,h:3}"
|
|
84
|
-
:icon="
|
|
123
|
+
:icon="mdiArrowLeft"
|
|
85
124
|
:iconSize="16"
|
|
86
125
|
:backgroundColor="'#fff'"
|
|
87
126
|
:backgroundColorHover="'#eee'"
|
|
@@ -101,283 +140,100 @@
|
|
|
101
140
|
<template v-slot:content="props">
|
|
102
141
|
<div :style="`height:${props.height}px; width:${props.width}px; position:relative;`">
|
|
103
142
|
|
|
104
|
-
<!--
|
|
105
|
-
<template>
|
|
143
|
+
<!-- 模式切換(文件/編輯/測試) + 內容 -->
|
|
144
|
+
<template v-if="apiSelect || newMode">
|
|
145
|
+
|
|
146
|
+
<div :style="`height:${heightModeBar}px; box-sizing:border-box; padding:8px 12px; border-bottom:1px solid var(--border); background:var(--bg-1); display:flex; align-items:center;`">
|
|
147
|
+
<div class="w-seg">
|
|
148
|
+
<button class="w-seg-btn" :class="{on: mode==='docs'}" @click="onChangeMode({id:'docs'})">{{$t('tabDocs')}}</button>
|
|
149
|
+
<button class="w-seg-btn" :class="{on: mode==='edit'}" @click="onChangeMode({id:'edit'})">{{$t('tabEdit')}}</button>
|
|
150
|
+
<button class="w-seg-btn" :class="{on: mode==='test'}" @click="onChangeMode({id:'test'})">{{$t('tabTest')}}</button>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
106
153
|
|
|
107
154
|
<div
|
|
108
|
-
:style="`height:${props.height}px; overflow-y:auto;`"
|
|
109
|
-
v-if="apiSelect"
|
|
155
|
+
:style="`height:${props.height-heightModeBar}px; overflow-y:auto; background:var(--bg-1);`"
|
|
156
|
+
v-if="mode==='docs' && apiSelect"
|
|
110
157
|
>
|
|
111
|
-
<div style="padding:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<div style="font-size:0.8rem;">
|
|
128
|
-
{{$ui.gv(apiSelect,'url')}}
|
|
129
|
-
</div>
|
|
130
|
-
|
|
131
|
-
</div>
|
|
132
|
-
|
|
158
|
+
<div style="padding:30px 36px;">
|
|
159
|
+
|
|
160
|
+
<!-- op header -->
|
|
161
|
+
<div><span class="w-mb" :class="getMethod(apiSelect).toLowerCase()">{{getMethod(apiSelect)}}</span></div>
|
|
162
|
+
<div class="op-title">{{$ui.gv(apiSelect,'name')}}</div>
|
|
163
|
+
<div class="op-path"><span class="w-mb" :class="getMethod(apiSelect).toLowerCase()">{{getMethod(apiSelect)}}</span><span class="w-mono">{{$ui.gv(apiSelect,'url')}}</span></div>
|
|
164
|
+
<div class="op-desc">{{$ui.gv(apiSelect,'description')}}</div>
|
|
165
|
+
|
|
166
|
+
<!-- metadata pills -->
|
|
167
|
+
<div class="pills">
|
|
168
|
+
<span class="pill" v-if="$ui.gv(apiSelect,'version')"><b>{{$ui.gv(apiSelect,'version')}}</b></span>
|
|
169
|
+
<span class="pill" v-if="$ui.gv(apiSelect,'levels')">{{$t('levels')}} <b>{{$ui.gv(apiSelect,'levels')}}</b></span>
|
|
170
|
+
<span class="pill" v-if="keywordsList.length">{{$t('keywords')}}<span class="kw-chip" v-for="kw in keywordsList" :key="'kw-'+kw">{{kw}}</span></span>
|
|
171
|
+
<span class="pill ok" v-if="$ui.gv(apiSelect,'state')">{{$ui.gv(apiSelect,'state')}}</span>
|
|
172
|
+
<span class="pill" v-if="$ui.gv(apiSelect,'creator')">{{$t('creator')}} <b>{{$ui.gv(apiSelect,'creator')}}</b></span>
|
|
173
|
+
<span class="pill" v-if="$ui.gv(apiSelect,'dataSource')">{{$t('dataSource')}} <b>{{$ui.gv(apiSelect,'dataSource')}}</b></span>
|
|
133
174
|
</div>
|
|
134
175
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
{{$ui.gv(apiSelect,'description')}}
|
|
143
|
-
</div>
|
|
144
|
-
|
|
145
|
-
</div>
|
|
146
|
-
|
|
147
|
-
<div style="padding:10px;">
|
|
148
|
-
|
|
149
|
-
<div class="bk" style="">
|
|
150
|
-
|
|
151
|
-
<div class="bk-title" style="">
|
|
152
|
-
{{$t('tokens')}}
|
|
153
|
-
</div>
|
|
154
|
-
|
|
155
|
-
<div class="bk-item" style="">
|
|
156
|
-
{{$ui.gv(apiSelect,'tokens')}}
|
|
157
|
-
</div>
|
|
158
|
-
|
|
159
|
-
</div>
|
|
160
|
-
|
|
161
|
-
<div class="bk" style="">
|
|
162
|
-
|
|
163
|
-
<div class="bk-title" style="">
|
|
164
|
-
{{$t('version')}}
|
|
165
|
-
</div>
|
|
166
|
-
|
|
167
|
-
<div class="bk-item" style="">
|
|
168
|
-
{{$ui.gv(apiSelect,'version')}}
|
|
169
|
-
</div>
|
|
170
|
-
|
|
171
|
-
</div>
|
|
172
|
-
|
|
173
|
-
<div class="bk" style="">
|
|
174
|
-
|
|
175
|
-
<div class="bk-title" style="">
|
|
176
|
-
{{$t('levels')}}
|
|
177
|
-
</div>
|
|
178
|
-
|
|
179
|
-
<div class="bk-item" style="">
|
|
180
|
-
{{$ui.gv(apiSelect,'')}}
|
|
181
|
-
</div>
|
|
182
|
-
|
|
183
|
-
</div>
|
|
184
|
-
|
|
185
|
-
<div class="bk" style="">
|
|
186
|
-
|
|
187
|
-
<div class="bk-title" style="">
|
|
188
|
-
{{$t('keywords')}}
|
|
189
|
-
</div>
|
|
190
|
-
|
|
191
|
-
<div class="bk-item" style="">
|
|
192
|
-
{{$ui.gv(apiSelect,'')}}
|
|
193
|
-
</div>
|
|
194
|
-
|
|
195
|
-
</div>
|
|
196
|
-
|
|
197
|
-
<div class="bk" style="">
|
|
198
|
-
|
|
199
|
-
<div class="bk-title" style="">
|
|
200
|
-
{{$t('state')}}
|
|
201
|
-
</div>
|
|
202
|
-
|
|
203
|
-
<div class="bk-item" style="">
|
|
204
|
-
{{$ui.gv(apiSelect,'')}}
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
</div>
|
|
208
|
-
|
|
209
|
-
<div class="bk" style="">
|
|
210
|
-
|
|
211
|
-
<div class="bk-title" style="">
|
|
212
|
-
{{$t('timeCreate')}}
|
|
213
|
-
</div>
|
|
214
|
-
|
|
215
|
-
<div class="bk-item" style="">
|
|
216
|
-
{{$ui.gv(apiSelect,'timeCreate',getDay)}}
|
|
217
|
-
</div>
|
|
218
|
-
|
|
219
|
-
</div>
|
|
220
|
-
|
|
221
|
-
<div class="bk" style="">
|
|
222
|
-
|
|
223
|
-
<div class="bk-title" style="">
|
|
224
|
-
{{$t('timeUpdate')}}
|
|
225
|
-
</div>
|
|
226
|
-
|
|
227
|
-
<div class="bk-item" style="">
|
|
228
|
-
{{$ui.gv(apiSelect,'timeUpdate',getDay)}}
|
|
229
|
-
</div>
|
|
230
|
-
|
|
231
|
-
</div>
|
|
232
|
-
|
|
233
|
-
<div class="bk" style="">
|
|
234
|
-
|
|
235
|
-
<div class="bk-title" style="">
|
|
236
|
-
{{$t('creator')}}
|
|
237
|
-
</div>
|
|
238
|
-
|
|
239
|
-
<div class="bk-item" style="">
|
|
240
|
-
{{$ui.gv(apiSelect,'creator')}}
|
|
241
|
-
</div>
|
|
242
|
-
|
|
243
|
-
</div>
|
|
244
|
-
|
|
245
|
-
<div class="bk" style="">
|
|
246
|
-
|
|
247
|
-
<div class="bk-title" style="">
|
|
248
|
-
{{$t('dataSource')}}
|
|
249
|
-
</div>
|
|
250
|
-
|
|
251
|
-
<div class="bk-item" style="">
|
|
252
|
-
{{$ui.gv(apiSelect,'dataSource')}}
|
|
253
|
-
</div>
|
|
254
|
-
|
|
255
|
-
</div>
|
|
256
|
-
|
|
257
|
-
<div class="bk" style="">
|
|
258
|
-
|
|
259
|
-
<div class="bk-title" style="">
|
|
260
|
-
{{$t('isActive')}}
|
|
176
|
+
<!-- two-column split -->
|
|
177
|
+
<div class="split">
|
|
178
|
+
<!-- 左欄:參數說明 -->
|
|
179
|
+
<div>
|
|
180
|
+
<div class="dsec">
|
|
181
|
+
<div class="dsec-h">{{$t('docInput')}}</div>
|
|
182
|
+
<div class="doc-md"><MdPanel :md="$ui.gv(apiSelect,'mdInputParams')"></MdPanel></div>
|
|
261
183
|
</div>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
184
|
+
<div class="dsec">
|
|
185
|
+
<div class="dsec-h">{{$t('docOutputFields')}}</div>
|
|
186
|
+
<div class="doc-md"><MdPanel :md="$ui.gv(apiSelect,'mdOutputParams')"></MdPanel></div>
|
|
265
187
|
</div>
|
|
266
|
-
|
|
267
|
-
</div>
|
|
268
|
-
|
|
269
|
-
</div>
|
|
270
|
-
|
|
271
|
-
<div style="padding:10px;">
|
|
272
|
-
|
|
273
|
-
<div class="bk-title" style="">
|
|
274
|
-
{{$t('mdInputParams')}}
|
|
275
|
-
</div>
|
|
276
|
-
|
|
277
|
-
<div class="bk-item" style="padding:10px;">
|
|
278
|
-
<MdPanel
|
|
279
|
-
:md="$ui.gv(apiSelect,'mdInputParams')"
|
|
280
|
-
></MdPanel>
|
|
281
|
-
</div>
|
|
282
|
-
|
|
283
|
-
</div>
|
|
284
|
-
|
|
285
|
-
<div style="padding:10px;">
|
|
286
|
-
|
|
287
|
-
<div class="bk-title" style="">
|
|
288
|
-
{{$t('inputExample')}}
|
|
289
|
-
</div>
|
|
290
|
-
|
|
291
|
-
<div class="bk-item" style="padding:10px;">
|
|
292
|
-
{{$ui.gv(apiSelect,'inputExample')}}
|
|
293
|
-
</div>
|
|
294
|
-
|
|
295
|
-
</div>
|
|
296
|
-
|
|
297
|
-
<div style="padding:10px;">
|
|
298
|
-
|
|
299
|
-
<div class="bk-title" style="">
|
|
300
|
-
{{$t('mdOutputParams')}}
|
|
301
|
-
</div>
|
|
302
|
-
|
|
303
|
-
<div class="bk-item" style="padding:10px;">
|
|
304
|
-
<MdPanel
|
|
305
|
-
:md="$ui.gv(apiSelect,'mdOutputParams')"
|
|
306
|
-
></MdPanel>
|
|
307
188
|
</div>
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
<div class="bk-title" style="">
|
|
314
|
-
{{$t('outputExample')}}
|
|
315
|
-
</div>
|
|
316
|
-
|
|
317
|
-
<div style="padding-top:3px;">
|
|
318
|
-
|
|
319
|
-
<div style="padding-left:10px; margin-bottom:-1px;">
|
|
320
|
-
<!-- 因上下皆有border, 故需負margin-bottom來吃掉重複border -->
|
|
321
|
-
<WGroupChipRadio
|
|
322
|
-
:items="optputMenuItems"
|
|
323
|
-
:value="optputMenuItemSelect"
|
|
324
|
-
@input="changeOptputMenuItemSelect"
|
|
325
|
-
:group="true"
|
|
326
|
-
:group-border-radius-style="{top:true}"
|
|
327
|
-
:group-shift="7"
|
|
328
|
-
:border-radius="20"
|
|
329
|
-
:border-color="'#ddd'"
|
|
330
|
-
:border-color-hover="'#dadada'"
|
|
331
|
-
:border-color-active="'orange lighten-2'"
|
|
332
|
-
:margin-style="{}"
|
|
333
|
-
></WGroupChipRadio>
|
|
189
|
+
<!-- 右欄:code rail -->
|
|
190
|
+
<div class="rail">
|
|
191
|
+
<div class="card">
|
|
192
|
+
<div class="card-h"><span class="lab">{{$t('docRequest')}}</span><span class="w-muted" style="margin-left:auto; font-size:11.5px;">cURL</span></div>
|
|
193
|
+
<pre class="code">{{curlOf(apiSelect)}}</pre>
|
|
334
194
|
</div>
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
<div
|
|
339
|
-
style="padding:10px 15px;"
|
|
340
|
-
v-if="optputMenuItemSelectId==='tree'"
|
|
341
|
-
>
|
|
342
|
-
<WJsonView
|
|
343
|
-
style="width:100%;"
|
|
344
|
-
:viewHeightMax="null"
|
|
345
|
-
:data="getOutputJsonObj(apiSelect)"
|
|
346
|
-
></WJsonView>
|
|
347
|
-
</div>
|
|
348
|
-
|
|
349
|
-
<div
|
|
350
|
-
style="padding:15px; color:#555; font-size:0.85rem;"
|
|
351
|
-
v-if="optputMenuItemSelectId==='raw'
|
|
352
|
-
">
|
|
353
|
-
<pre style="margin:0px; padding:0px;">{{getOutputJson(apiSelect)}}</pre>
|
|
354
|
-
</div>
|
|
355
|
-
|
|
195
|
+
<div class="card">
|
|
196
|
+
<div class="card-h"><span class="lab">{{$t('resTitle')}}</span><span class="st">200 OK</span></div>
|
|
197
|
+
<pre class="code" v-html="jsonHighlight($ui.gv(apiSelect,'outputExample'))"></pre>
|
|
356
198
|
</div>
|
|
357
|
-
|
|
358
199
|
</div>
|
|
359
|
-
|
|
360
200
|
</div>
|
|
361
201
|
|
|
362
202
|
</div>
|
|
363
203
|
</div>
|
|
364
204
|
|
|
365
|
-
<
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
205
|
+
<LayoutContentEdit
|
|
206
|
+
v-else-if="mode==='edit'"
|
|
207
|
+
:item="editItem"
|
|
208
|
+
:height="props.height-heightModeBar"
|
|
209
|
+
:isNew="newMode"
|
|
210
|
+
@saved="onEditSaved"
|
|
211
|
+
@deleted="onEditDeleted"
|
|
212
|
+
@cancel="onEditCancel"
|
|
213
|
+
></LayoutContentEdit>
|
|
214
|
+
|
|
215
|
+
<LayoutContentTest
|
|
216
|
+
v-else-if="mode==='test' && apiSelect"
|
|
217
|
+
:item="apiSelect"
|
|
218
|
+
:height="props.height-heightModeBar"
|
|
219
|
+
></LayoutContentTest>
|
|
371
220
|
|
|
372
221
|
</template>
|
|
373
222
|
|
|
223
|
+
<div
|
|
224
|
+
style="padding:20px; font-size:0.9rem;"
|
|
225
|
+
v-else
|
|
226
|
+
>
|
|
227
|
+
{{$t('noSelectApi')}}
|
|
228
|
+
</div>
|
|
229
|
+
|
|
374
230
|
<div
|
|
375
231
|
:style="`position:absolute; top:1px; left:4px;`"
|
|
376
232
|
v-if="!drawer"
|
|
377
233
|
>
|
|
378
234
|
<WButtonCircle
|
|
379
235
|
:paddingStyle="{v:3,h:3}"
|
|
380
|
-
:icon="
|
|
236
|
+
:icon="mdiArrowRight"
|
|
381
237
|
:iconSize="16"
|
|
382
238
|
:backgroundColor="'#fff'"
|
|
383
239
|
:backgroundColorHover="'#eee'"
|
|
@@ -406,6 +262,16 @@
|
|
|
406
262
|
</div>
|
|
407
263
|
</template>
|
|
408
264
|
|
|
265
|
+
</template>
|
|
266
|
+
|
|
267
|
+
<LayoutContentStats
|
|
268
|
+
v-else-if="section==='stats'"
|
|
269
|
+
:height="panelHeight"
|
|
270
|
+
></LayoutContentStats>
|
|
271
|
+
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
</div>
|
|
409
275
|
</div>
|
|
410
276
|
</template>
|
|
411
277
|
|
|
@@ -416,28 +282,30 @@ import each from 'lodash-es/each.js'
|
|
|
416
282
|
import size from 'lodash-es/size.js'
|
|
417
283
|
import trim from 'lodash-es/trim.js'
|
|
418
284
|
import find from 'lodash-es/find.js'
|
|
285
|
+
import filter from 'lodash-es/filter.js'
|
|
419
286
|
import cloneDeep from 'lodash-es/cloneDeep.js'
|
|
420
287
|
import isobj from 'wsemi/src/isobj.mjs'
|
|
421
288
|
import iseobj from 'wsemi/src/iseobj.mjs'
|
|
422
|
-
import j2o from 'wsemi/src/j2o.mjs'
|
|
423
|
-
import timemsTZ2day from 'wsemi/src/timemsTZ2day.mjs'
|
|
424
289
|
import convertToTree from 'wsemi/src/convertToTree.mjs'
|
|
290
|
+
import { mdiArrowLeft, mdiArrowRight, mdiCloudBraces, mdiChartBar } from '@mdi/js'
|
|
425
291
|
import WButtonCircle from 'w-component-vue/src/components/WButtonCircle.vue'
|
|
426
|
-
import WGroupChipRadio from 'w-component-vue/src/components/WGroupChipRadio.vue'
|
|
427
292
|
import WDrawer from 'w-component-vue/src/components/WDrawer.vue'
|
|
428
293
|
import WTree from 'w-component-vue/src/components/WTree.vue'
|
|
429
|
-
import WJsonView from 'w-component-vue/src/components/WJsonView.vue'
|
|
430
294
|
import MdPanel from './MdPanel.vue'
|
|
295
|
+
import LayoutContentEdit from './LayoutContentEdit.vue'
|
|
296
|
+
import LayoutContentTest from './LayoutContentTest.vue'
|
|
297
|
+
import LayoutContentStats from './LayoutContentStats.vue'
|
|
431
298
|
|
|
432
299
|
|
|
433
300
|
export default {
|
|
434
301
|
components: {
|
|
435
302
|
WButtonCircle,
|
|
436
|
-
WGroupChipRadio,
|
|
437
303
|
WDrawer,
|
|
438
304
|
WTree,
|
|
439
|
-
WJsonView,
|
|
440
305
|
MdPanel,
|
|
306
|
+
LayoutContentEdit,
|
|
307
|
+
LayoutContentTest,
|
|
308
|
+
LayoutContentStats,
|
|
441
309
|
},
|
|
442
310
|
props: {
|
|
443
311
|
},
|
|
@@ -447,6 +315,14 @@ export default {
|
|
|
447
315
|
panelWidth: 0,
|
|
448
316
|
panelHeight: 0,
|
|
449
317
|
|
|
318
|
+
//左側垂直主選單
|
|
319
|
+
section: 'api', //'api' | 'stats'
|
|
320
|
+
widthMainMenu: 76,
|
|
321
|
+
mainMenus: [
|
|
322
|
+
{ id: 'api', textKey: 'mmApi', icon: mdiCloudBraces },
|
|
323
|
+
{ id: 'stats', textKey: 'mmStaInfor', icon: mdiChartBar },
|
|
324
|
+
],
|
|
325
|
+
|
|
450
326
|
drawer: true,
|
|
451
327
|
drawerWidth: 320,
|
|
452
328
|
drawerWidthMin: 200,
|
|
@@ -457,16 +333,17 @@ export default {
|
|
|
457
333
|
apisTree: [],
|
|
458
334
|
kpApisTree: {},
|
|
459
335
|
apiActive: null,
|
|
336
|
+
mdiArrowLeft,
|
|
337
|
+
mdiArrowRight,
|
|
460
338
|
apiSelect: null,
|
|
461
339
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
optputMenuItemSelectId: 'tree',
|
|
340
|
+
//API 管理:模式切換 / 新增 / 搜尋
|
|
341
|
+
mode: 'docs', //'docs' | 'edit' | 'test'
|
|
342
|
+
newMode: false,
|
|
343
|
+
blankApi: {},
|
|
344
|
+
search: '',
|
|
345
|
+
heightModeBar: 46,
|
|
346
|
+
heightDrawerHeader: 48,
|
|
470
347
|
|
|
471
348
|
}
|
|
472
349
|
},
|
|
@@ -484,32 +361,25 @@ export default {
|
|
|
484
361
|
let vo = this
|
|
485
362
|
//trigger
|
|
486
363
|
let apis = vo.apis
|
|
364
|
+
let search = vo.search //讓搜尋變更也觸發tree重建
|
|
365
|
+
void search
|
|
487
366
|
vo.genTree(apis)
|
|
488
367
|
return ''
|
|
489
368
|
},
|
|
490
369
|
|
|
491
|
-
|
|
370
|
+
editItem: function() {
|
|
492
371
|
let vo = this
|
|
493
|
-
|
|
494
|
-
{
|
|
495
|
-
id: 'tree',
|
|
496
|
-
text: vo.$t('outputMenuTree'),
|
|
497
|
-
icon: 'mdi-vanity-light',
|
|
498
|
-
},
|
|
499
|
-
{
|
|
500
|
-
id: 'raw',
|
|
501
|
-
text: vo.$t('outputMenuRaw'),
|
|
502
|
-
icon: 'mdi-lan',
|
|
503
|
-
},
|
|
504
|
-
]
|
|
505
|
-
return ms
|
|
372
|
+
return vo.newMode ? vo.blankApi : vo.apiSelect
|
|
506
373
|
},
|
|
507
374
|
|
|
508
|
-
|
|
375
|
+
//關鍵字以分號切開成陣列(trim + 去空),供 pills 區逐一以 chip 呈現
|
|
376
|
+
keywordsList: function() {
|
|
509
377
|
let vo = this
|
|
510
|
-
let
|
|
511
|
-
|
|
512
|
-
|
|
378
|
+
let s = get(vo.apiSelect, 'keywords', '')
|
|
379
|
+
if (!s) {
|
|
380
|
+
return []
|
|
381
|
+
}
|
|
382
|
+
return String(s).split(';').map((x) => x.trim()).filter((x) => x !== '')
|
|
513
383
|
},
|
|
514
384
|
|
|
515
385
|
},
|
|
@@ -530,14 +400,17 @@ export default {
|
|
|
530
400
|
genTree: function () {
|
|
531
401
|
let vo = this
|
|
532
402
|
|
|
403
|
+
//保留目前選取的API id, 重建後盡量維持選取(避免儲存/同步後選取跳回第一筆)
|
|
404
|
+
let prevId = get(vo.apiSelect, 'id', '')
|
|
405
|
+
|
|
533
406
|
//default
|
|
534
407
|
vo.apiActive = null
|
|
535
|
-
vo.apiSelect = null
|
|
536
408
|
vo.apisTree = []
|
|
537
409
|
vo.kpApisTree = {}
|
|
538
410
|
|
|
539
411
|
//check
|
|
540
412
|
if (size(vo.apis) === 0) {
|
|
413
|
+
vo.apiSelect = null
|
|
541
414
|
return
|
|
542
415
|
}
|
|
543
416
|
|
|
@@ -545,6 +418,15 @@ export default {
|
|
|
545
418
|
let apis = cloneDeep(vo.apis)
|
|
546
419
|
// console.log('apis', cloneDeep(apis))
|
|
547
420
|
|
|
421
|
+
//搜尋過濾(name/keywords/url/levels/method)
|
|
422
|
+
let sterm = trim(vo.search).toLowerCase()
|
|
423
|
+
if (sterm !== '') {
|
|
424
|
+
apis = filter(apis, (v) => {
|
|
425
|
+
let hay = `${get(v, 'name', '')} ${get(v, 'keywords', '')} ${get(v, 'url', '')} ${get(v, 'levels', '')} ${get(v, 'method', '')}`.toLowerCase()
|
|
426
|
+
return hay.indexOf(sterm) >= 0
|
|
427
|
+
})
|
|
428
|
+
}
|
|
429
|
+
|
|
548
430
|
//tr
|
|
549
431
|
let tr = {}
|
|
550
432
|
let kpTree = {}
|
|
@@ -570,8 +452,14 @@ export default {
|
|
|
570
452
|
let tree = convertToTree(tr, { bindRoot: '全部' })
|
|
571
453
|
// console.log('tree', cloneDeep(tree))
|
|
572
454
|
|
|
573
|
-
//apiSelect,
|
|
574
|
-
let apiSelect =
|
|
455
|
+
//apiSelect: 優先保留原選取(仍存在時), 否則取第一筆
|
|
456
|
+
let apiSelect = null
|
|
457
|
+
if (prevId !== '') {
|
|
458
|
+
apiSelect = find(apis, { id: prevId }) || null
|
|
459
|
+
}
|
|
460
|
+
if (!iseobj(apiSelect)) {
|
|
461
|
+
apiSelect = get(apis, 0, null)
|
|
462
|
+
}
|
|
575
463
|
// console.log('apiSelect', cloneDeep(apiSelect))
|
|
576
464
|
|
|
577
465
|
//save
|
|
@@ -596,6 +484,16 @@ export default {
|
|
|
596
484
|
return method
|
|
597
485
|
},
|
|
598
486
|
|
|
487
|
+
curlOf: function(item) {
|
|
488
|
+
let vo = this
|
|
489
|
+
let method = vo.getMethod(item) || 'GET'
|
|
490
|
+
let url = get(item, 'inputExample', '')
|
|
491
|
+
if (!url) {
|
|
492
|
+
url = get(item, 'url', '')
|
|
493
|
+
}
|
|
494
|
+
return 'curl -X ' + method + ' "' + url + '"'
|
|
495
|
+
},
|
|
496
|
+
|
|
599
497
|
getMethodByKey: function(key) {
|
|
600
498
|
let vo = this
|
|
601
499
|
let item = vo.getItemByKey(key)
|
|
@@ -603,62 +501,102 @@ export default {
|
|
|
603
501
|
return method
|
|
604
502
|
},
|
|
605
503
|
|
|
606
|
-
|
|
607
|
-
let
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
504
|
+
jsonHighlight: function(str) {
|
|
505
|
+
let s = (str === null || str === undefined) ? '' : String(str)
|
|
506
|
+
// escape HTML
|
|
507
|
+
s = s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
508
|
+
// key: "xxx":
|
|
509
|
+
s = s.replace(/("(?:[^"\\]|\\.)*")(\s*:)/g, '<span class="k">$1</span>$2')
|
|
510
|
+
// string value
|
|
511
|
+
s = s.replace(/:(\s*)("(?:[^"\\]|\\.)*")/g, ':$1<span class="s">$2</span>')
|
|
512
|
+
// number
|
|
513
|
+
s = s.replace(/([:\[,\s])(-?\d+\.?\d*)/g, '$1<span class="n">$2</span>')
|
|
514
|
+
// bool/null
|
|
515
|
+
s = s.replace(/\b(true|false|null)\b/g, '<span class="b">$1</span>')
|
|
516
|
+
return s
|
|
611
517
|
},
|
|
612
518
|
|
|
613
|
-
|
|
519
|
+
funActive: function(msg) {
|
|
614
520
|
let vo = this
|
|
615
|
-
let
|
|
616
|
-
|
|
617
|
-
|
|
521
|
+
let b = !Array.isArray(msg.item.children) //children非陣列代表沒有所屬節點(葉節點)
|
|
522
|
+
if (b) {
|
|
523
|
+
//渲染時用穩定的 api id(=節點 text) 對應目前選取項, 把 apiActive 刷新成該葉節點「當前」的 id;
|
|
524
|
+
//避免存檔後 tree 重建、convertToTree 重新產生節點 id, 使 apiActive 變舊而高亮(含左側 accent bar)失效
|
|
525
|
+
let itemText = get(msg, 'item.text', '')
|
|
526
|
+
let itemId = get(msg, 'item.id', '')
|
|
527
|
+
if (iseobj(vo.apiSelect) && itemText === vo.apiSelect.id) {
|
|
528
|
+
if (get(vo.apiActive, 'id') !== itemId) {
|
|
529
|
+
vo.apiActive = { id: itemId }
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
else if (!iseobj(vo.apiActive)) { //無選取時初始化第一個葉節點為 active
|
|
533
|
+
vo.apiActive = { id: itemId }
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return b
|
|
618
537
|
},
|
|
619
538
|
|
|
620
|
-
|
|
621
|
-
|
|
539
|
+
ckItem: function(item, node) {
|
|
540
|
+
// console.log('ckItem', item)
|
|
541
|
+
let vo = this
|
|
542
|
+
vo.apiSelect = cloneDeep(item)
|
|
543
|
+
if (iseobj(node)) { //點擊即更新 active 節點 id, 使高亮(含左側 bar)立即移到所點項目
|
|
544
|
+
vo.apiActive = { id: node.id }
|
|
545
|
+
}
|
|
546
|
+
vo.newMode = false //點選既有API即離開新增狀態
|
|
622
547
|
},
|
|
623
548
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
549
|
+
onChangeMode: function(msg) {
|
|
550
|
+
let vo = this
|
|
551
|
+
vo.mode = msg.id
|
|
552
|
+
//切到非編輯模式時取消新增狀態
|
|
553
|
+
if (vo.newMode && msg.id !== 'edit') {
|
|
554
|
+
vo.newMode = false
|
|
555
|
+
}
|
|
629
556
|
},
|
|
630
557
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
// console.log(j)
|
|
635
|
-
let o = j2o(j)
|
|
636
|
-
return o
|
|
558
|
+
onClickMainMenu: function(m) {
|
|
559
|
+
let vo = this
|
|
560
|
+
vo.section = m.id
|
|
637
561
|
},
|
|
638
562
|
|
|
639
|
-
|
|
563
|
+
onClickAddApi: function() {
|
|
640
564
|
let vo = this
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
565
|
+
//種子空白API(預設值, 送出時後端funNew會配id/時間)
|
|
566
|
+
vo.blankApi = {
|
|
567
|
+
name: '',
|
|
568
|
+
description: '',
|
|
569
|
+
url: '',
|
|
570
|
+
method: 'get',
|
|
571
|
+
version: 'v1',
|
|
572
|
+
group: '',
|
|
573
|
+
levels: '',
|
|
574
|
+
keywords: '',
|
|
575
|
+
state: 'ok',
|
|
576
|
+
authType: 'none',
|
|
577
|
+
contentType: 'application/json',
|
|
648
578
|
}
|
|
649
|
-
|
|
579
|
+
vo.newMode = true
|
|
580
|
+
vo.mode = 'edit'
|
|
650
581
|
},
|
|
651
582
|
|
|
652
|
-
|
|
653
|
-
// console.log('changeOptputMenuItemSelect', msg)
|
|
583
|
+
onEditSaved: function() {
|
|
654
584
|
let vo = this
|
|
655
|
-
vo.
|
|
585
|
+
vo.newMode = false
|
|
586
|
+
vo.mode = 'docs'
|
|
656
587
|
},
|
|
657
588
|
|
|
658
|
-
|
|
659
|
-
// console.log('ckItem', item)
|
|
589
|
+
onEditDeleted: function() {
|
|
660
590
|
let vo = this
|
|
661
|
-
vo.
|
|
591
|
+
vo.newMode = false
|
|
592
|
+
vo.apiSelect = null
|
|
593
|
+
vo.mode = 'docs'
|
|
594
|
+
},
|
|
595
|
+
|
|
596
|
+
onEditCancel: function() {
|
|
597
|
+
let vo = this
|
|
598
|
+
vo.newMode = false
|
|
599
|
+
vo.mode = 'docs'
|
|
662
600
|
},
|
|
663
601
|
|
|
664
602
|
}
|
|
@@ -666,23 +604,34 @@ export default {
|
|
|
666
604
|
</script>
|
|
667
605
|
|
|
668
606
|
<style scoped>
|
|
669
|
-
.
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
padding:
|
|
673
|
-
|
|
607
|
+
.w-mm-btn {
|
|
608
|
+
width: 100%;
|
|
609
|
+
box-sizing: border-box;
|
|
610
|
+
padding: 10px 4px;
|
|
611
|
+
border: none;
|
|
612
|
+
border-left: 3px solid transparent;
|
|
613
|
+
background: transparent;
|
|
614
|
+
color: var(--c-2);
|
|
615
|
+
cursor: pointer;
|
|
616
|
+
display: flex;
|
|
617
|
+
flex-direction: column;
|
|
618
|
+
align-items: center;
|
|
619
|
+
gap: 4px;
|
|
620
|
+
transition: background .15s, color .15s, border-color .15s;
|
|
621
|
+
}
|
|
622
|
+
.w-mm-btn:hover {
|
|
623
|
+
background: rgba(0, 153, 255, 0.06);
|
|
624
|
+
color: var(--c-1);
|
|
674
625
|
}
|
|
675
|
-
.
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
626
|
+
.w-mm-btn.on {
|
|
627
|
+
color: var(--accent);
|
|
628
|
+
border-left-color: var(--accent);
|
|
629
|
+
background: rgba(0, 153, 255, 0.08);
|
|
679
630
|
}
|
|
680
|
-
.
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
border:1px solid #ddd;
|
|
686
|
-
background:#fff;
|
|
631
|
+
.w-mm-label {
|
|
632
|
+
font-size: 11px;
|
|
633
|
+
line-height: 1.2;
|
|
634
|
+
text-align: center;
|
|
635
|
+
word-break: break-word;
|
|
687
636
|
}
|
|
688
637
|
</style>
|