Files
jinshen-website/app/components/shared/QuestionList.vue
R2m1liA 54d0e297ea feat: 将Markdown渲染改为HTML渲染
- CMS相关字段由Markdown改为WYSIWYG,前端做出对应更改
- AssetUrl重写:将CMS地址重写为本地API
2025-11-14 11:06:00 +08:00

102 lines
2.2 KiB
Vue

<template>
<div class="question-list">
<el-collapse v-model="activeNames" class="question-collapse">
<el-collapse-item
v-for="question in questions"
:id="`q-${question.id}`"
:key="question.id"
:title="question.title"
:name="question.id"
>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="!hydrated" v-html="question.content" />
<div v-else>
<html-renderer
class="html-typography"
:html="question.content || ''"
/>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
const props = defineProps({
questions: {
type: Array as PropType<ProductQuestionView[]>,
default: () => [],
},
});
const route = useRoute();
const activeNames = ref<(string | number)[]>([]);
const hydrated = ref(false);
// 当路由变化(包括初次挂载)时,检查是否需要聚焦
watch(
() => route.query.focus,
async (focusId) => {
if (!focusId) return;
if (!import.meta.client) return;
// 确保渲染完成后再操作 DOM
await nextTick();
const target = props.questions.find((q) => q.id === Number(focusId));
if (!target) return;
// 展开目标项
activeNames.value = [target.id];
// 平滑滚动到对应位置
const el = document.querySelector(`#q-${target.id}`);
el?.scrollIntoView({ behavior: 'smooth', block: 'start' });
},
{ immediate: true }
);
onMounted(() => {
hydrated.value = true;
});
</script>
<style scoped>
.question-list {
width: 100%;
}
.question-collapse {
border: none;
}
.question-collapse :deep(.el-collapse-item) {
margin-bottom: 1rem;
border-radius: 8px;
}
.question-collapse :deep(.el-collapse-item__header) {
font-size: 1rem;
padding: 1rem;
border-radius: 8px;
background-color: #f5f7fa;
transition: all 0.3s ease;
&.is-active {
background-color: #e1e6eb;
color: var(--el-color-primary);
}
}
.question-collapse :deep(.el-collapse-item__wrap) {
border: none;
}
.question-collapse :deep(.el-collapse-item__content) {
padding: 1rem;
font-size: 0.9rem;
}
</style>