md2ui 1.0.3 → 1.0.7
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/package.json +1 -1
- package/src/App.vue +83 -3
- package/src/components/ImageZoom.vue +4 -2
package/package.json
CHANGED
package/src/App.vue
CHANGED
|
@@ -57,8 +57,9 @@
|
|
|
57
57
|
</template>
|
|
58
58
|
|
|
59
59
|
<script setup>
|
|
60
|
-
import { ref, onMounted } from 'vue'
|
|
60
|
+
import { ref, onMounted, nextTick } from 'vue'
|
|
61
61
|
import { ChevronsDownUp, ChevronsUpDown, ArrowUp, PanelLeftOpen, PanelLeftClose, PanelRightOpen, Github } from 'lucide-vue-next'
|
|
62
|
+
import MD5 from 'crypto-js/md5'
|
|
62
63
|
import Logo from './components/Logo.vue'
|
|
63
64
|
import TreeNode from './components/TreeNode.vue'
|
|
64
65
|
import TableOfContents from './components/TableOfContents.vue'
|
|
@@ -76,7 +77,24 @@ const zoomVisible = ref(false)
|
|
|
76
77
|
const zoomContent = ref('')
|
|
77
78
|
|
|
78
79
|
const { htmlContent, tocItems, renderMarkdown } = useMarkdown()
|
|
79
|
-
const { scrollProgress, showBackToTop, activeHeading, handleScroll, scrollToHeading, scrollToTop } = useScroll()
|
|
80
|
+
const { scrollProgress, showBackToTop, activeHeading, handleScroll: _handleScroll, scrollToHeading: _scrollToHeading, scrollToTop } = useScroll()
|
|
81
|
+
|
|
82
|
+
// 包装滚动处理,同步锚点到 URL
|
|
83
|
+
function handleScroll(e) {
|
|
84
|
+
_handleScroll(e)
|
|
85
|
+
// 滚动时更新 URL 锚点
|
|
86
|
+
if (activeHeading.value) {
|
|
87
|
+
updateHash(activeHeading.value)
|
|
88
|
+
} else {
|
|
89
|
+
updateHash('')
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 包装目录点击,同步锚点到 URL
|
|
94
|
+
function scrollToHeading(id) {
|
|
95
|
+
_scrollToHeading(id)
|
|
96
|
+
updateHash(id)
|
|
97
|
+
}
|
|
80
98
|
const { sidebarWidth, tocWidth, startResize } = useResize()
|
|
81
99
|
|
|
82
100
|
async function loadDocsList() {
|
|
@@ -91,8 +109,21 @@ function loadFirstDoc() {
|
|
|
91
109
|
}
|
|
92
110
|
}
|
|
93
111
|
|
|
112
|
+
// 根据 key 生成 hash
|
|
113
|
+
function docHash(key) {
|
|
114
|
+
return MD5(key).toString()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 更新 URL hash(文档hash + 可选锚点)
|
|
118
|
+
function updateHash(anchor) {
|
|
119
|
+
if (!currentDoc.value) return
|
|
120
|
+
const base = docHash(currentDoc.value)
|
|
121
|
+
window.location.hash = anchor ? `${base}/${anchor}` : base
|
|
122
|
+
}
|
|
123
|
+
|
|
94
124
|
async function loadDoc(key) {
|
|
95
125
|
currentDoc.value = key
|
|
126
|
+
updateHash('')
|
|
96
127
|
const doc = findDoc(docsList.value, key)
|
|
97
128
|
if (!doc) return
|
|
98
129
|
try {
|
|
@@ -192,10 +223,59 @@ your-docs/
|
|
|
192
223
|
`)
|
|
193
224
|
}
|
|
194
225
|
|
|
226
|
+
// 根据 hash 查找文档
|
|
227
|
+
function findDocByHash(items, hash) {
|
|
228
|
+
for (const item of items) {
|
|
229
|
+
if (item.type === 'file' && docHash(item.key) === hash) return item
|
|
230
|
+
if (item.type === 'folder' && item.children) {
|
|
231
|
+
const found = findDocByHash(item.children, hash)
|
|
232
|
+
if (found) return found
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return null
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 展开文档所在的所有父级文件夹
|
|
239
|
+
function expandParents(items, targetKey) {
|
|
240
|
+
for (const item of items) {
|
|
241
|
+
if (item.type === 'file' && item.key === targetKey) return true
|
|
242
|
+
if (item.type === 'folder' && item.children) {
|
|
243
|
+
if (expandParents(item.children, targetKey)) {
|
|
244
|
+
item.expanded = true
|
|
245
|
+
return true
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return false
|
|
250
|
+
}
|
|
251
|
+
|
|
195
252
|
onMounted(async () => {
|
|
196
253
|
await loadDocsList()
|
|
197
254
|
|
|
198
|
-
|
|
255
|
+
const rawHash = window.location.hash.replace('#', '')
|
|
256
|
+
const [hash, anchor] = rawHash.includes('/')
|
|
257
|
+
? [rawHash.split('/')[0], rawHash.split('/').slice(1).join('/')]
|
|
258
|
+
: [rawHash, '']
|
|
259
|
+
|
|
260
|
+
if (hash) {
|
|
261
|
+
const doc = findDocByHash(docsList.value, hash)
|
|
262
|
+
if (doc) {
|
|
263
|
+
expandParents(docsList.value, doc.key)
|
|
264
|
+
await loadDoc(doc.key)
|
|
265
|
+
// 恢复锚点位置
|
|
266
|
+
if (anchor) {
|
|
267
|
+
await nextTick()
|
|
268
|
+
// 等待 DOM 渲染完成后再滚动
|
|
269
|
+
setTimeout(() => {
|
|
270
|
+
_scrollToHeading(anchor)
|
|
271
|
+
updateHash(anchor)
|
|
272
|
+
}, 100)
|
|
273
|
+
}
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 没有 hash 或找不到对应文档,加载第一个
|
|
199
279
|
const firstDoc = findFirstDoc(docsList.value)
|
|
200
280
|
if (firstDoc) {
|
|
201
281
|
await loadDoc(firstDoc.key)
|
|
@@ -112,8 +112,10 @@ function handleOverlayClick(event) {
|
|
|
112
112
|
function handleWheel(event) {
|
|
113
113
|
event.preventDefault()
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
const
|
|
115
|
+
// 使用比例缩放,每次滚轮缩放 2%,体感更平滑
|
|
116
|
+
const zoomFactor = 0.02
|
|
117
|
+
const direction = event.deltaY > 0 ? -1 : 1
|
|
118
|
+
const newScale = Math.max(minScale, Math.min(maxScale, scale.value * (1 + direction * zoomFactor)))
|
|
117
119
|
|
|
118
120
|
if (newScale !== scale.value) {
|
|
119
121
|
scale.value = newScale
|