feat:用shadow dom隔离html样式

This commit is contained in:
caiyuchao
2025-09-19 17:18:14 +08:00
parent 36524fa9ef
commit c1020dc5ff
5 changed files with 75 additions and 5 deletions

View File

@@ -0,0 +1 @@
export { default as ShadowContainer } from './shadow-container.vue';

View File

@@ -0,0 +1,47 @@
<script setup>
import { onMounted, ref, watch } from 'vue';
const props = defineProps({
content: {
type: String,
default: '',
},
// 可以传递样式字符串来自定义内部样式
styles: {
type: String,
default: '',
},
});
const shadowHost = ref(null);
const initShadow = () => {
if (shadowHost.value && !shadowHost.value.shadowRoot) {
const shadowRoot = shadowHost.value.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host {
all: initial;
display: block;
contain: content;
${props.styles}
}
</style>
<div class="shadow-content">${props.content}</div>
`;
} else if (shadowHost.value && shadowHost.value.shadowRoot) {
shadowHost.value.shadowRoot.querySelector('.shadow-content').innerHTML =
props.content;
}
};
onMounted(initShadow);
watch(() => props.content, initShadow);
</script>
<template>
<div ref="shadowHost"></div>
</template>

View File

@@ -10,6 +10,7 @@ import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { deleteComment } from '#/api/license/comment';
import { ShadowContainer } from '#/components/shadow-container';
import { $t } from '#/locales';
import CreateComment from './create-comment.vue';
@@ -104,7 +105,11 @@ const formatContent = computed(() => {
<template #content>
<div>
<!-- eslint-disable-next-line vue/no-v-html -->
<span class="rich-text-comment-content" v-html="formatContent"></span>
<!-- <span class="rich-text-comment-content" v-html="formatContent"></span> -->
<ShadowContainer
:content="formatContent"
styles="font-family: Helvetica,Arial,sans-serif; font-size:16px;"
/>
</div>
</template>
<template #actions>
@@ -194,6 +199,11 @@ const formatContent = computed(() => {
</template>
<style>
:where(.css-dev-only-do-not-override-14589v).ant-comment .ant-comment-actions {
margin-top: 16px;
margin-top: 0;
}
:where(.css-dev-only-do-not-override-14589v).ant-comment
.ant-comment-content-author {
margin-bottom: -2px;
}
</style>

View File

@@ -149,12 +149,13 @@ export function useProjectGridColumns(): VxeTableGridOptions<ProgressApi.Progres
{
field: 'content',
title: '进展记录',
type: 'html',
className: 'rich-text-comment-content-table',
// type: 'html',
// className: 'rich-text-comment-content-table',
showOverflow: false,
align: 'left',
// headerAlign: 'center',
minWidth: 600,
slots: { default: 'contentDefault' },
},
{
field: 'updateTime',

View File

@@ -7,6 +7,7 @@ import dayjs from 'dayjs';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { exportProject, getProgressByProjectPage } from '#/api/report/progress';
import { ShadowContainer } from '#/components/shadow-container';
import { useProjectGridColumns, useProjectGridFormSchema } from '../data';
@@ -149,5 +150,15 @@ const [Grid, gridApi] = useVbenVxeGrid({
</script>
<template>
<Grid table-title="项目进展" />
<Grid table-title="项目进展">
<template #contentDefault="{ row }">
<!-- <div v-html="row.content" style="white-space: pre-wrap"></div> -->
<ShadowContainer
:content="row.content"
styles="font-family: -apple-system, blinkmacsystemfont, 'Segoe UI', roboto, 'Helvetica Neue',
arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';"
/>
</template>
</Grid>
</template>