Skip to content

Commit d3ec516

Browse files
committed
feat: swagger_toolkit: 完成浏览历史部分
1 parent 048e7ea commit d3ec516

File tree

1 file changed

+269
-0
lines changed

1 file changed

+269
-0
lines changed

swagger_toolkit.js

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
;(() => {
2+
const TIMES = 30
3+
let current = 0
4+
let isLoaded = false
5+
const interval = setInterval(() => {
6+
if (++current >= TIMES) {
7+
clearInterval(interval)
8+
return
9+
}
10+
const item = document.querySelector('.opblock-summary')
11+
if (!item) return
12+
if (!isLoaded) {
13+
const wrapper = document.querySelector('.swagger-ui')
14+
wrapper.addEventListener('click', evt => {
15+
// 点击接口标题时在当前 URL 中加入锚点
16+
const linkTitleDom = evt.target.closest('.opblock-summary')
17+
if (linkTitleDom) {
18+
const linkDom = linkTitleDom.parentNode
19+
const isOpen = !linkDom.classList.contains('is-open')
20+
const hash = isOpen ? linkDom.id : ''
21+
if (hash) location.hash = hash
22+
return
23+
}
24+
// 点击接口中的 Model 时同步展开下方数据结构
25+
const modelLinkDom = evt.target.closest('ul.tab')
26+
if (modelLinkDom && evt.target.innerText.trim() === 'Model') {
27+
setTimeout(() => {
28+
const icons = modelLinkDom.nextElementSibling.querySelectorAll('.model-toggle.collapsed')
29+
if (icons.length) icons[icons.length - 1].click()
30+
}, 300)
31+
return
32+
}
33+
})
34+
if (location.hash) {
35+
observeHash()
36+
window.addEventListener('hashchange', observeHash)
37+
}
38+
isLoaded = true
39+
return
40+
}
41+
}, 300);
42+
const observeHash = evt => {
43+
console.log('observe hash')
44+
const linkedDom = document.getElementById(location.hash.length > 0 ? location.hash.substr(1) : '')
45+
if (linkedDom) {
46+
const isOpen = linkedDom.classList.contains('is-open')
47+
linkedDom.scrollIntoView()
48+
if (!isOpen) linkedDom.querySelector('.opblock-summary').click()
49+
console.log('scroll into view: ', linkedDom, linkedDom.querySelector('.opblock-summary'))
50+
}
51+
}
52+
class Sheets {
53+
static sheets = `
54+
body {
55+
--row-width: 13vw;
56+
--row-min-width: 245px;
57+
--row-title-font-size: 14px;
58+
--body-wrapper-width: 80vw;
59+
--body-wrapper-margin-right: 3vw;
60+
--body-wrapper-min-width: 800px;
61+
}
62+
63+
/* 页面内容主体布局 */
64+
#swagger-ui div.topbar { display: flex; justify-content: flex-end; }
65+
#swagger-ui div.topbar .wrapper { margin: 0; width: var(--body-wrapper-width); min-width: var(--body-wrapper-min-width); margin-right: var(--body-wrapper-margin-right) }
66+
#swagger-ui div.swagger-ui { display: flex; justify-content: flex-end; }
67+
#swagger-ui div.swagger-ui .wrapper { margin: 0; width: var(--body-wrapper-width); min-width: var(--body-wrapper-min-width); margin-right: var(--body-wrapper-margin-right) }
68+
69+
/* sidebar part */
70+
#swagger-toolkit-sidebar {
71+
width: var(--row-width);
72+
min-width: var(--row-min-width);
73+
display: flex;
74+
position: fixed;
75+
top: 0;
76+
left: 0;
77+
height: 100vh;
78+
flex-direction: column;
79+
justify-content: space-between;
80+
background-color: #FAFAFA;
81+
border-right: 1px solid #c4d6d6;
82+
}
83+
#swagger-toolkit-sidebar .list { width: 100%; }
84+
#swagger-toolkit-sidebar .list > header { font-size: 18px; background-color: #999; }
85+
#swagger-toolkit-sidebar .list > header > .title { color: #FFF; text-align: center; font-weight: 200; }
86+
#swagger-toolkit-sidebar .row { display: flex; padding-bottom: 5px; width: 100%; cursor: pointer; text-decoration: none; }
87+
#swagger-toolkit-sidebar .row.method-DELETE { background-color: rgba(249,62,62,.1); }
88+
#swagger-toolkit-sidebar .row.method-DELETE:hover { background-color: rgba(249,62,62,.5); }
89+
#swagger-toolkit-sidebar .row.method-GET { background-color: rgba(97,175,254,.1); }
90+
#swagger-toolkit-sidebar .row.method-GET:hover { background-color: rgba(97,175,254,.5); }
91+
#swagger-toolkit-sidebar .row.method-POST { background-color: rgba(73,204,144,.1); }
92+
#swagger-toolkit-sidebar .row.method-POST:hover { background-color: rgba(73,204,144,.5); }
93+
#swagger-toolkit-sidebar .row.method-PUT { background-color: rgba(252,161,48,.1); }
94+
#swagger-toolkit-sidebar .row.method-PUT:hover { background-color: rgba(252,161,48,.5); }
95+
#swagger-toolkit-sidebar .row.method-PATCH { background-color: rgba(80,227,194,.1); }
96+
#swagger-toolkit-sidebar .row.method-PATCH:hover { background-color: rgba(80,227,194,.5); }
97+
98+
#swagger-toolkit-sidebar .row .description { color: #333; font-size: 14px; width: var(--row-width); min-width: var(--row-min-width); }
99+
#swagger-toolkit-sidebar .row .method { display: flex; line-height: 45px; min-width: 64px; }
100+
#swagger-toolkit-sidebar .row .path > a { color: #409EFF; }
101+
102+
/* helper */
103+
.tool-text-size-fixed { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
104+
`
105+
static inject() {
106+
const sheet = document.createTextNode(Sheets.sheets)
107+
const el = document.createElement('style')
108+
el.id = 'swagger-toolkit-sheets'
109+
el.appendChild(sheet)
110+
document.getElementsByTagName('head')[0].appendChild(el)
111+
}
112+
}
113+
class LinkStore {
114+
key = ''
115+
path = ''
116+
method = ''
117+
description = '' // 接口名
118+
id = ''
119+
createdat = 0
120+
static MAX_LENGTH = 10
121+
static save(row, key) {
122+
const store = new LinkStore()
123+
store.id = row.id
124+
store.key = key
125+
store.method = row.querySelector('.opblock-summary-method').innerText
126+
store.path = row.querySelector('.opblock-summary-path').innerText
127+
store.description = row.querySelector('.opblock-summary-description').innerText
128+
LinkStore.add(key, store)
129+
}
130+
static add(key, store) {
131+
let data = LinkStore.getStore(key)
132+
data.unshift(store)
133+
if (data.length > LinkStore.MAX_LENGTH) data = data.slice(0, LinkStore.MAX_LENGTH)
134+
localStorage.setItem(key, JSON.stringify(data))
135+
}
136+
static getStore(key) {
137+
let store = []
138+
try {
139+
const _store = localStorage.getItem(key)
140+
if (_store) store = JSON.parse(_store)
141+
} catch (err) {
142+
console.error(err)
143+
}
144+
return store
145+
}
146+
}
147+
class Pane {
148+
dom = null
149+
localKey = null
150+
title = null
151+
placeholder = '暂无数据'
152+
generateDom(isUpdate) {
153+
if (isUpdate) this.dom.innerHTML = ''
154+
const list = isUpdate ? this.dom : document.createElement('div')
155+
list.classList.add('list')
156+
list.classList.add(this.localKey)
157+
// 添加 header
158+
const header = document.createElement('header')
159+
const title = document.createElement('div')
160+
title.classList.add('title')
161+
title.innerText = this.title
162+
list.appendChild(header)
163+
header.appendChild(title)
164+
// 添加数据
165+
const data = LinkStore.getStore(this.localKey)
166+
for (const dataRow of data) {
167+
const row = document.createElement('a')
168+
row.href = '#' + dataRow.id
169+
const method = document.createElement('div')
170+
method.innerText = dataRow.method
171+
const contents = document.createElement('div')
172+
const description = document.createElement('div')
173+
description.innerText = dataRow.description
174+
const path = document.createElement('div')
175+
const pathLink = document.createElement('a')
176+
pathLink.innerText = dataRow.path
177+
pathLink.href = '#' + dataRow.id
178+
const btnGroup = document.createElement('div')
179+
const markBtn = document.createElement('div')
180+
markBtn.innerText = '⭐️'
181+
const deleteBtn = document.createElement('div')
182+
deleteBtn.innerText = '❌'
183+
184+
row.classList.add('row')
185+
row.classList.add('method-' + dataRow.method)
186+
method.classList.add('method')
187+
contents.classList.add('contents')
188+
description.classList.add('description')
189+
description.classList.add('tool-text-size-fixed')
190+
path.classList.add('path')
191+
btnGroup.classList.add('btn-group')
192+
markBtn.classList.add('btn-mark')
193+
deleteBtn.classList.add('btn-delete')
194+
195+
path.appendChild(pathLink)
196+
contents.appendChild(description)
197+
contents.appendChild(path)
198+
// contents.appendChild(btnGroup)
199+
// btnGroup.appendChild(markBtn)
200+
// btnGroup.appendChild(deleteBtn)
201+
// row.appendChild(method)
202+
row.appendChild(contents)
203+
list.appendChild(row)
204+
}
205+
if (data.length === 0) list.appendChild(this.getPlaceholderDom())
206+
this.dom = list
207+
if (typeof this.afterGenerageDom === 'function') this.afterGenerageDom()
208+
return list
209+
}
210+
getPlaceholderDom() {
211+
const dom = document.createElement('section')
212+
dom.innerText = this.placeholder
213+
return dom
214+
}
215+
}
216+
class HistoryPane extends Pane {
217+
localKey = 'swagger-toolkit-history'
218+
title = '浏览历史'
219+
placeholder = '暂无浏览历史数据'
220+
}
221+
class MarkPane extends Pane {
222+
localKey = 'swagger-toolkit-mark'
223+
title = '收藏'
224+
placeholder = '暂无收藏数据, 点击收藏按钮添加'
225+
afterGenerageDom() {
226+
this.dom
227+
}
228+
}
229+
class SideBar {
230+
static dom = null
231+
static panes = []
232+
addListeners() {
233+
window.addEventListener('hashchange', () => {
234+
console.log('SideBar onhashchange')
235+
const row = document.getElementById(location.hash.length > 0 ? location.hash.substr(1) : '')
236+
LinkStore.save(row, 'swagger-toolkit-history')
237+
this._updatePane('swagger-toolkit-history')
238+
})
239+
return this
240+
}
241+
generateDom() {
242+
const sidebar = document.createElement('sidebar')
243+
sidebar.id = 'swagger-toolkit-sidebar'
244+
SideBar.dom = sidebar
245+
return this
246+
}
247+
inject() {
248+
document.body.appendChild(SideBar.dom)
249+
return this
250+
}
251+
appendPanes() {
252+
for (const pane of SideBar.panes) {
253+
SideBar.dom.appendChild(pane.generateDom())
254+
}
255+
return this
256+
}
257+
_updatePane(key) {
258+
for (const pane of SideBar.panes) {
259+
if (pane.localKey !== key) continue
260+
pane.generateDom(true)
261+
}
262+
}
263+
}
264+
Sheets.inject()
265+
SideBar.panes.push(new HistoryPane())
266+
SideBar.panes.push(new MarkPane())
267+
window.$$_SideBar = new SideBar()
268+
window.$$_SideBar.addListeners().generateDom().appendPanes().inject()
269+
})();

0 commit comments

Comments
 (0)