-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
修得我想吊。
- Loading branch information
Showing
4 changed files
with
159 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,59 @@ | ||
import { tab, MarkdownItTabOptions } from "@mdit/plugin-tab"; | ||
import { logger } from '../config/sidebarControl'; | ||
import { logger } from "../config/sidebarControl"; | ||
import type { PluginSimple } from "markdown-it"; | ||
|
||
// 深拷贝函数 | ||
function deepCloneEnv(env) { | ||
return JSON.parse(JSON.stringify(env)); | ||
} | ||
|
||
export const carousels: PluginSimple = (md) => { | ||
md.use(tab, { | ||
name: "carousels", | ||
tabsOpenRenderer(info, tokens, index, opt, env) { | ||
const content = JSON.parse(JSON.stringify(env)) | ||
const IContent = content.content | ||
let token: string = "" | ||
let config: string = "" | ||
if (IContent && typeof IContent === "string") { | ||
const matches = IContent.match(/carousels#\{[^\}]*\}/g) | ||
if (matches) { | ||
matches.forEach(match => { | ||
token += match.replace("carousels#", "") | ||
}) | ||
} | ||
} | ||
// logger(JSON.stringify(tokens[index]), "jsonCheck.json") | ||
try { | ||
const configObj = JSON.parse(token) | ||
|
||
if (configObj.arrows) { | ||
if (typeof configObj.arrows === "boolean") { | ||
config += ` :show-arrows="${configObj.arrows}"` | ||
} else if (configObj.arrows === "hover") { | ||
config += ` :show-arrows="hover"` | ||
// 每次使用时深拷贝 env,确保配置不被共享 | ||
const localEnv = deepCloneEnv(env); | ||
const IContent = localEnv.content; | ||
let config = ""; | ||
|
||
// 重新解析 meta 字段中的配置,确保每次渲染时配置独立 | ||
const token = tokens[index]; | ||
if (token && token.meta && typeof token.meta.id === 'string') { | ||
try { | ||
const configObj = JSON.parse(token.meta.id); | ||
if (configObj.arrows !== undefined) { | ||
config += ` :show-arrows="${configObj.arrows}"`; | ||
} | ||
} | ||
|
||
if (configObj.undelimiters && configObj.undelimiters === true) config += ` :hide-delimiters="true"` | ||
|
||
if (configObj.cycle && configObj.cycle === true) { | ||
config += ` :cycle="true"` | ||
|
||
if (configObj.interval && typeof configObj.interval === "number") { | ||
config += ` :interval="${configObj.interval}"` | ||
if (configObj.cycle !== undefined) { | ||
config += ` :cycle="${configObj.cycle}"`; | ||
} | ||
if (configObj.interval !== undefined) { | ||
config += ` :interval="${configObj.interval}"`; | ||
} | ||
if (configObj.undelimiters !== undefined) { | ||
config += ` :hide-delimiters="${configObj.undelimiters}"`; | ||
} | ||
config += ` :continuous="true"`; | ||
} catch (error) { | ||
console.error("Error parsing carousel config from meta:", error); | ||
} | ||
|
||
if (configObj.ratio && typeof configObj.ratio === "number") config += `aspectRatio="${configObj.ratio}" ` | ||
} catch (error) { } | ||
return `<MdCarousel${config} >`; | ||
} | ||
|
||
// 确保每次返回独立配置的组件实例 | ||
return `<MdCarousel | ||
v-model="currentIndex"${config} | ||
@update:model-value="handleSlideChange" | ||
@before-change="onBeforeChange" | ||
>`; | ||
}, | ||
tabsCloseRenderer() { | ||
return `</MdCarousel>`; | ||
}, | ||
tabOpenRenderer(data) { | ||
|
||
return `\n<v-carousel-item cover src="${data.title}">\n`; | ||
tabOpenRenderer() { | ||
return `\n<v-carousel-item>\n`; | ||
}, | ||
tabCloseRenderer() { | ||
return `</v-carousel-item>`; | ||
return `</v-carousel-item>\n`; | ||
}, | ||
}) | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,97 +1,129 @@ | ||
<template> | ||
<div class="carousel"> | ||
<v-responsive :aspect-ratio="aspectRatio" ref="responsiveContainer"> | ||
<!-- 动态的 v-responsive 宽高比 --> | ||
<v-carousel | ||
:height="carouselHeight" | ||
:show-arrows="showArrows" | ||
:cycle="cycle" | ||
:interval="interval" | ||
:hide-delimiters="hideDelimiters" | ||
> | ||
<slot></slot> | ||
</v-carousel> | ||
</v-responsive> | ||
<div class="carousel" ref="carouselContainer"> | ||
<v-carousel | ||
v-if="isReady" | ||
:height="carouselHeight" | ||
:show-arrows="showArrows" | ||
:cycle="cycle" | ||
:interval="interval" | ||
:hide-delimiters="hideDelimiters" | ||
> | ||
<slot></slot> | ||
</v-carousel> | ||
</div> | ||
</template> | ||
|
||
<script setup> | ||
import { ref, onMounted, onUnmounted, nextTick } from 'vue' | ||
import { ref, onMounted, onUnmounted, nextTick, watch } from "vue"; | ||
const props = defineProps({ | ||
aspectRatio: { | ||
type: Number, | ||
default: 16 / 9, // 默认宽高比 | ||
}, | ||
showArrows: { | ||
type: [Boolean, String], | ||
default: true, // 控制箭头的显示,默认为 true,可以传入 'hover' 显示 | ||
default: true, | ||
}, | ||
cycle: { | ||
type: Boolean, | ||
default: false, // 是否启用自动轮播 | ||
default: false, | ||
}, | ||
interval: { | ||
type: Number, | ||
default: 6000, // 自动轮播的间隔 | ||
default: 6000, | ||
}, | ||
hideDelimiters: { | ||
type: Boolean, | ||
default: false, // 是否隐藏指示点 | ||
default: false, | ||
}, | ||
}) | ||
}); | ||
const carouselHeight = ref(0) | ||
const responsiveContainer = ref(null) | ||
const carouselContainer = ref(null); | ||
const carouselHeight = ref("auto"); | ||
const isReady = ref(false); | ||
const updateCarouselHeight = () => { | ||
if (responsiveContainer.value) { | ||
const containerElement = responsiveContainer.value.$el; | ||
carouselHeight.value = containerElement.offsetWidth / props.aspectRatio; | ||
if (carouselContainer.value) { | ||
const items = | ||
carouselContainer.value.querySelectorAll(".v-carousel__item"); | ||
let maxHeight = 0; | ||
items.forEach((item) => { | ||
item.style.display = "block"; | ||
const itemHeight = item.scrollHeight; | ||
item.style.display = ""; | ||
if (itemHeight > maxHeight) maxHeight = itemHeight; | ||
}); | ||
carouselHeight.value = maxHeight > 0 ? `${maxHeight}px` : "auto"; | ||
isReady.value = true; | ||
} | ||
} | ||
}; | ||
const debouncedUpdateHeight = debounce(updateCarouselHeight, 100); | ||
let resizeObserver = null; | ||
if (typeof ResizeObserver !== 'undefined') { | ||
resizeObserver = new ResizeObserver(() => { | ||
updateCarouselHeight() | ||
}); | ||
if (typeof ResizeObserver !== "undefined") { | ||
resizeObserver = new ResizeObserver(debouncedUpdateHeight); | ||
} | ||
onMounted(() => { | ||
nextTick(() => { | ||
updateCarouselHeight() | ||
if (responsiveContainer.value && resizeObserver) { | ||
resizeObserver.observe(responsiveContainer.value.$el) | ||
if (carouselContainer.value && resizeObserver) { | ||
resizeObserver.observe(carouselContainer.value); | ||
} | ||
}) | ||
}) | ||
window.addEventListener("resize", debouncedUpdateHeight); | ||
updateCarouselHeight(); | ||
}); | ||
}); | ||
onUnmounted(() => { | ||
if (responsiveContainer.value && resizeObserver) { | ||
resizeObserver.unobserve(responsiveContainer.value.$el) | ||
if (carouselContainer.value && resizeObserver) { | ||
resizeObserver.unobserve(carouselContainer.value); | ||
} | ||
if (resizeObserver) { | ||
resizeObserver.disconnect() | ||
resizeObserver.disconnect(); | ||
} | ||
}) | ||
</script> | ||
window.removeEventListener("resize", debouncedUpdateHeight); | ||
}); | ||
watch( | ||
() => props, | ||
() => { | ||
nextTick(updateCarouselHeight()); | ||
}, | ||
{ deep: true, immediate: true } | ||
); | ||
<style> | ||
function debounce(func, wait) { | ||
let timeout; | ||
return function executedFunction(...args) { | ||
const later = () => { | ||
clearTimeout(timeout); | ||
func(...args); | ||
}; | ||
clearTimeout(timeout); | ||
timeout = setTimeout(later, wait); | ||
}; | ||
} | ||
</script> | ||
|
||
<style scoped> | ||
.carousel { | ||
width: 100%; | ||
max-width: 100vw; | ||
} | ||
:deep(.v-carousel__item) { | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
width: 100%; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
width: 100%; | ||
height: auto; | ||
max-height: 100vh; | ||
position: relative; | ||
} | ||
.carousel img { | ||
width: 100%; | ||
height: 100%; | ||
object-fit: cover; | ||
:deep(.v-carousel__item img) { | ||
max-width: 100%; | ||
max-height: 100%; | ||
object-fit: contain; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters