|
| 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