SsCommonProblem.vue 是一个展示常见问题的 Vue 3 组件,采用 Composition API 和 TypeScript 编写。该组件提供了一个常见问题列表,用户可以点击问题快速发送到聊天框,还可以刷新获取新的问题列表。组件采用了响应式设计,在不同设备上都有良好的显示效果。
import {ref, defineEmits, onMounted} from 'vue'
import {Refresh} from '@element-plus/icons-vue'
import {useGlobalStore} from "@/stores/global";
功能解析:
ref(创建响应式引用)、defineEmits(定义组件事件)、onMounted(生命周期钩子)Refresh 图标组件useGlobalStoreconst emit = defineEmits(['finish', 'quickSend', 'loaded'])
功能解析:
finish:刷新完成时触发quickSend:用户点击问题时触发,用于快速发送问题到聊天框loaded:组件挂载完成时触发const global = useGlobalStore()
const isRefresh = ref(false);
功能解析:
global:获取全局状态管理实例,用于访问和修改全局状态isRefresh:响应式布尔值,用于控制刷新按钮的加载状态const refresh = () => {
isRefresh.value = true;
global.loadCommonProblem('', () => {
isRefresh.value = false;
emit('finish')
})
}
const quickSend = (msg: string) => {
emit('quickSend', msg)
}
功能解析:
refresh 方法:
loadCommonProblem 方法加载常见问题finish 事件quickSend 方法:
quickSend 事件,将问题内容传递给父组件defineExpose({
refresh
});
功能解析:
defineExpose 显式暴露 refresh 方法,使父组件可以通过 ref 调用此方法onMounted(() => {
emit('loaded', 'footer')
})
功能解析:
loaded 事件,传递参数 'footer',可能用于标识组件位置<div class="problem hover-scale">
功能解析:
problem 类作为主容器hover-scale 类可能提供悬停时的缩放效果<div class="problem-content">
功能解析:
<div class="header">
<el-image class="header-logo" src="/images/common_problem.png" fit="contain"/>
<div class="header-content">
<div class="header-title">常见问题</div>
<div class="header-subject">聚焦商贸投资热点,回应企业民生关切</div>
<el-button class="refresh is-mobile" :icon="Refresh" :loading="isRefresh" @click="refresh">
换一换
</el-button>
</div>
</div>
功能解析:
is-mobile 类可能用于移动端样式控制isRefresh 状态控制加载动画<div class="problem-list">
<template v-for="(item, index) in global.commonProblem">
<div class="problem-item ellipsis" @click="quickSend(item.questionContent)">
{{ index + 1 }}.{{ item.questionContent }}
</div>
</template>
</div>
功能解析:
v-for 遍历全局状态中的 commonProblem 数组ellipsis 类可能用于文本溢出时显示省略号quickSend 方法,传递问题内容<el-image class="problem-image is-pc" fit="contain" src="/images/bg01.png"/>
<el-image class="problem-image is-mobile" fit="contain" src="/images/bg011.png"/>
功能解析:
is-pc 和 is-mobile 类进行响应式控制.problem {
border-radius: 1.25rem;
position: relative;
overflow: hidden;
color: #ffffff;
background: linear-gradient(180deg, #5A92F8, #2943D6);
box-shadow: 0 0.25rem 1.25rem 0 rgba(52, 149, 239, 0.4);
min-height: 16rem;
}
功能解析:
.problem-content {
position: relative;
z-index: 2;
padding: 1.5rem 1.25rem;
}
功能解析:
.header {
display: flex;
align-items: center;
margin-bottom: 2.45rem;
.header-logo {
width: 2.13rem;
height: 2.13rem;
margin-right: 0.5rem;
}
.header-content {
width: calc(100% - 2.65rem);
position: relative;
.header-title {
font-size: 1rem;
font-weight: 600;
margin-bottom: 0.3rem;
line-height: 0.88rem;
cursor: pointer;
}
.header-subject {
font-size: 0.8rem;
font-weight: 400;
line-height: 0.88rem;
cursor: pointer;
}
.refresh {
position: absolute;
right: 0;
top: 0;
padding: 0;
line-height: 0.75rem;
font-size: 0.75rem;
height: 0.75rem;
color: #FFEF7C;
background-color: transparent;
border: 0;
}
}
}
功能解析:
.problem-list {
.problem-item {
font-size: 0.8rem;
line-height: 0.8rem;
margin-bottom: 0.65rem;
cursor: pointer;
padding-bottom: 0.1rem;
&:last-child {
margin-bottom: 0;
}
&:hover {
text-decoration: underline;
}
}
}
功能解析:
.problem-image {
position: absolute;
bottom: 0;
right: 1rem;
width: 18rem;
height: 10rem;
z-index: 1;
background-size: contain;
}
功能解析:
@media screen and (max-width: 750px) {
.problem {
.problem-content {
.header {
margin-bottom: 1.5rem;
}
}
.problem-image {
width: 7.94rem;
height: 7.81rem;
bottom: 1rem;
right: 0.75rem;
}
}
}
功能解析:
组件通过 useGlobalStore 访问全局状态,主要使用:
global.commonProblem:存储常见问题列表global.loadCommonProblem:加载常见问题的方法根据 api.d.ts 中的类型定义,常见问题项的数据结构为:
interface commonProblemItem {
questionContent: string, // 问题内容
remark: string | null, // 备注
}
global.loadCommonProblem 加载常见问题global.commonProblem 访问并显示问题列表quickSend 事件将问题内容传递给父组件组件向父组件发射三种事件:
finish:刷新完成时quickSend(msg):用户点击问题时,传递问题内容loaded:组件挂载完成时组件通过 defineExpose 暴露 refresh 方法,使父组件可以主动触发刷新:
// 父组件中可以这样调用
const commonProblemRef = ref()
commonProblemRef.value.refresh()
组件使用 Vue 3 的 Composition API,具有以下优势:
使用 Pinia 进行全局状态管理:
采用媒体查询实现响应式设计:
可以添加搜索框,允许用户搜索特定问题:
<template>
<div class="search-box">
<el-input v-model="searchKeyword" placeholder="搜索问题" @input="handleSearch"/>
</div>
<!-- 其他内容 -->
</template>
<script setup lang="ts">
const searchKeyword = ref('')
const handleSearch = () => {
global.loadCommonProblem(searchKeyword.value)
}
</script>
可以添加问题分类,允许用户按类别筛选问题:
<template>
<div class="category-tabs">
<el-tabs v-model="activeCategory" @tab-click="handleCategoryChange">
<el-tab-pane label="全部" name="all"/>
<el-tab-pane label="政策咨询" name="policy"/>
<el-tab-pane label="办事指南" name="guide"/>
</el-tabs>
</div>
<!-- 其他内容 -->
</template>
点击问题时,可以显示弹窗展示问题的详细内容:
<template>
<!-- 其他内容 -->
<el-dialog v-model="dialogVisible" title="问题详情">
<div class="question-detail">
<h3>{{ selectedQuestion?.questionContent }}</h3>
<p>{{ selectedQuestion?.remark }}</p>
</div>
</el-dialog>
</template>
<script setup lang="ts">
const dialogVisible = ref(false)
const selectedQuestion = ref<API.commonProblemItem | null>(null)
const showQuestionDetail = (item: API.commonProblemItem) => {
selectedQuestion.value = item
dialogVisible.value = true
}
</script>
允许用户收藏常用问题:
<template>
<div class="problem-item" @click="quickSend(item.questionContent)">
{{ index + 1 }}.{{ item.questionContent }}
<el-icon class="favorite-icon" :class="{ 'is-favorite': isFavorite(item.id) }" @click.stop="toggleFavorite(item)">
<StarFilled v-if="isFavorite(item.id)" />
<Star v-else />
</el-icon>
</div>
</template>
SsCommonProblem.vue 是一个设计良好的 Vue 3 组件,展示了现代前端开发的多个最佳实践:
该组件不仅功能完整,而且代码结构清晰,易于维护和扩展,是学习 Vue 3 组件开发的良好示例。