Files
mp-10579/src/components/ByteMdViewer/index.vue
赵忠林 1856a611ce chore(config): 初始化项目配置文件
- 添加 .editorconfig 文件统一代码风格
- 配置 .env.development 环境变量文件
- 创建 .env.example 环境变量示例文件
- 设置 .eslintignore 忽略检查规则
- 配置 .eslintrc.js 代码检查规则
- 添加 .gitignore 文件忽略版本控制
- 设置 .prettierignore 忽略格式化规则
- 新增隐私政策HTML页面文件
- 创建API密钥编辑组件基础结构
2025-12-15 13:29:17 +08:00

94 lines
1.9 KiB
Vue

<!-- markdown 解析 -->
<template>
<!-- eslint-disable vue/no-v-html -->
<div
ref="rootRef"
v-html="content"
class="markdown-body"
@click="handleClick"
>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue';
import type { BytemdPlugin } from 'bytemd';
import { getProcessor } from 'bytemd';
const props = defineProps<{
value: string;
plugins?: BytemdPlugin[];
sanitize?: (schema: any) => any;
}>();
const rootRef = ref<HTMLElement | null>(null);
const content = ref<any | null>(null);
const cbs = ref<(void | (() => void))[]>([]);
const on = () => {
if (props.plugins && rootRef.value && content.value) {
cbs.value = props.plugins.map(({ viewerEffect }) => {
return (
viewerEffect &&
viewerEffect({
markdownBody: rootRef.value as HTMLElement,
file: content.value
})
);
});
}
};
const off = () => {
if (cbs.value) {
cbs.value.forEach((cb) => cb && cb());
}
};
const handleClick = (e: MouseEvent) => {
const $ = e.target as HTMLElement;
if ($.tagName !== 'A') {
return;
}
const href = $.getAttribute('href');
if (!href || !href.startsWith('#')) {
return;
}
const dest = rootRef.value?.querySelector('#user-content-' + href.slice(1));
if (dest) {
dest.scrollIntoView();
}
};
watch(
[() => props.value, () => props.plugins, () => props.sanitize],
() => {
try {
content.value = getProcessor({
plugins: props.plugins,
sanitize: props.sanitize
}).processSync(props.value);
} catch (e) {
console.error(e);
}
off();
nextTick(() => {
on();
});
},
{
immediate: true
}
);
onMounted(() => {
on();
});
onBeforeUnmount(() => {
off();
});
</script>