一、父组件代码
<template>
<div class="commentDynamicMonitoring" v-loading="loading">
<div class="no-data" v-show="showNoData">
<img src="@/assets/img/no_data.png" alt="" class="img" />
<div class="noData-text">暂无数据</div>
</div>
<wordCloud
id="wordCloudChart"
:chartWidth="chartWidth"
:chartHeight="chartHeight"
:colorList="colorList"
:options="wordCloudOptions"
v-show="!showNoData"
></wordCloud>
<div class="right-box">
<infoTitle infoTitle="情绪指数" class="info-chart"></infoTitle>
<instrumentChart
id="instrumentChart"
chartWidth="100%"
chartHeight="255px"
:colorList="colorList1"
:options="instrumentChartOptions"
></instrumentChart>
<infoTitle infoTitle="评论信息" class="info-chart"></infoTitle>
<div class="detail-info">
<div class="title">{{ detailTitle }}</div>
<el-popover
placement="top-start"
:width="500"
trigger="hover"
:content="detailInfo"
popper-class="switch-popper"
v-if="showTooltip"
>
<template #reference>
<span class="detail" @mouseenter="isShowTooltip($event)">{{
detailInfo
}}</span>
</template>
</el-popover>
<span class="detail" @mouseenter="isShowTooltip($event)" v-else>{{
detailInfo
}}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import wordCloud from './wordCloud.vue'
import infoTitle from './infoTitle.vue'
import instrumentChart from './instrumentChart.vue'
import 'echarts-wordcloud'
import type { EChartsOption } from 'echarts'
import { queryCommentDynamicMonitoring } from '@/service/stockIndex/index'
const props = defineProps({
tagIndex: {
// tab索引
type: Number,
default: 0
},
securityId: {
// 证券id
type: [String, Number],
required: true
}
})
const loading = ref(false) // 加载状态
const showNoData = ref(false)
const chartWidth = ref('900px')
const chartHeight = ref('500px')
const showTooltip = ref(false) // 是否展示浮框
const currentIndex = ref(props.tagIndex) // 定义新tab索引值接收
const detailTitle = ref('')
const detailInfo = ref('')
// 词语图颜色定义
const colorList = ref<any>([
'#27D38A',
'#FFCA1C',
'#5DD1FA',
'#F88E25',
'#47A0FF',
'#FD6565'
])
// 仪表盘颜色定义
const colorList1 = ref<any>([
[0.1, '#1ec569'],
[0.2, '#72cd65'],
[0.3, '#9fcc58'],
[0.4, '#a5c852'],
[0.5, '#d6c949'],
[0.6, '#fcb743'],
[0.7, '#fe984a'],
[0.8, '#fe7c4f'],
[0.9, '#fe6b55'],
[1, '#fe4a41']
])
// 词语图属性定义
const wordCloudOptions: any = ref<EChartsOption>({
series: [
{
data: []
}
]
})
// 仪表盘图表属性定义
const instrumentChartOptions: any = ref<EChartsOption>({
series: [
{
data: []
}
]
})
const securityId1 = ref(props.securityId) // 页面新的证券id
// 监听参数重新赋值
watch(
() => [props.securityId, props.tagIndex],
(newValue: any, oldValue: any) => {
if (newValue[0] != oldValue[0]) {
securityId1.value = newValue[0]
}
currentIndex.value = newValue[1]
wordCloudOptions.value.series[0].data = []
instrumentChartOptions.value.series[0].data = []
detailTitle.value = ''
detailInfo.value = ''
getCommentDynamicMonitoring()
},
{ deep: true }
)
// 判断是否展示文本浮框
const isShowTooltip = (event: any) => {
const ev = event.target
const evHeight = ev.scrollHeight
const contentHeight = ev.clientHeight
if (evHeight > contentHeight) {
// 实际宽度 > 可视宽度 文字溢出
showTooltip.value = true
} else {
// 否则为不溢出
showTooltip.value = false
}
}
// 查询词云图数据、情绪指数、评论信息
const getCommentDynamicMonitoring = () => {
loading.value = true
queryCommentDynamicMonitoring(currentIndex.value, securityId1.value).then(
(res: any) => {
if (res.code === 200) {
if (res.data) {
// 判断是否有数据
if (res.data.wordClouds && res.data.wordClouds.length > 0) {
showNoData.value = false
} else {
showNoData.value = true
}
// 词云图数据
wordCloudOptions.value.series[0].data = res.data.wordClouds
// 看涨看跌指数
instrumentChartOptions.value.series[0].data = [
{
name: '',
value: res.data.sentimentIndex,
emphasis: {
// 设置 normal 和 emphasis 状态下的颜色保持一致
itemStyle: {
color: 'inherit'
}
}
}
]
// 评论数据
detailTitle.value = res.data.shortName
detailInfo.value = res.data.overview
}
} else {
ElMessage({
message: res.message,
type: 'error'
})
}
loading.value = false
}
)
}
onMounted(() => {
nextTick(() => {
getCommentDynamicMonitoring()
})
})
</script>
<style lang="less" scoped>
.commentDynamicMonitoring {
display: flex;
margin: 24px 0 20px;
#wordCloudChart {
border: 1px solid #ebeef8;
}
.right-box {
width: 511px;
background: #ffffff;
border-right: 1px solid #ebeef8;
border-bottom: 1px solid #ebeef8;
#instrumentChart {
border-bottom: 1px solid #ebeef8;
}
.detail-info {
margin: 0 22px;
.title {
margin: 12px 0 5px 0;
font-size: 16px;
color: #333333;
font-weight: 600;
}
.detail {
font-weight: 500;
font-size: 14px;
color: #666666;
line-height: 22px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 5;
line-clamp: 5;
-webkit-box-orient: vertical;
}
}
}
.no-data {
width: 100%;
height: 500px;
border: 1px solid #ebeef8;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.img {
width: 300px;
height: 163px;
margin-bottom: 10px;
}
.noData-text {
font-size: 12px;
color: #cacfd7;
}
}
}
</style>
二、词云图组件代码
<template>
<div :id="id" :style="{ width: chartWidth, height: chartHeight }"></div>
</template>
<script setup lang="ts">
import * as echarts from 'echarts'
import _ from 'lodash'
const props = defineProps({
id: {
type: String,
default: 'wordCloudChart'
},
chartWidth: {
type: String,
default: '353px'
},
chartHeight: {
type: String,
default: '300px'
},
colorList: {
// 词云图颜色
type: Array,
default: () => []
},
options: {
type: Object,
default: () => {}
}
})
// 定义图表
const wordCloudChart = ref<any>('')
// 图表渲染参数开始
let chartOptions = {
tooltip: {
show: false
},
grid: {
left: '0%',
right: '0%',
top: '0%',
bottom: '0%',
containLabel: true
},
series: [
{
type: 'wordCloud',
gridSize: 1,
sizeRange: [12, 100],
rotationRange: [0, 0],
textStyle: {
color: function () {
let num = Math.floor(Math.random() * (5 + 1))
return props.colorList[num]
}
},
left: 'center',
top: 'center',
width: '96%',
height: '50%',
data: []
}
]
}
//监听options变化,给图表赋值
watch(
props.options,
(newValue) => {
if (wordCloudChart.value) {
//当数据有更新时候,可使用lineChart.value.clear()清除现有的图表状态,然后再应用新的配置。
wordCloudChart.value.clear()
//数据清空,因为使用_.merge合并时,会残留历史数据
chartOptions.series.forEach((item: any) => {
item.data = []
})
_.merge(chartOptions, newValue)
wordCloudChart.value.setOption(chartOptions)
// 监听窗口大小变化,自动调整图表大小
setTimeout(() => {
wordCloudChart.value.resize()
})
}
},
{ deep: true }
)
onMounted(() => {
// 初始化 ECharts 实例
wordCloudChart.value = echarts.init(
document.getElementById(props.id) as HTMLDivElement
)
// 设置 ECharts 配置项
wordCloudChart.value.setOption(chartOptions)
// 监听窗口大小变化,自动调整图表大小
window.addEventListener('resize', () => {
wordCloudChart.value.resize()
})
})
onUnmounted(() => {
// 销毁 ECharts 实例
wordCloudChart.value.dispose()
// 移除窗口大小变化监听器
window.removeEventListener('resize', () => wordCloudChart.value.resize())
})
</script>
三、仪表盘组件代码
<template>
<div :id="id" :style="{ width: chartWidth, height: chartHeight }"></div>
</template>
<script setup lang="ts">
import * as echarts from 'echarts'
import _ from 'lodash'
const props = defineProps({
id: {
type: String,
default: 'instrumentChart'
},
chartWidth: {
type: String,
default: '511px'
},
chartHeight: {
type: String,
default: '300px'
},
options: {
type: Object,
default: () => {}
},
colorList: {
type: Array,
default: () => []
}
})
// 定义图表
const instrumentChart = ref<any>('')
// 图表渲染参数开始
let chartOptions = {
tooltip: {
show: false
},
series: [
{
name: '情绪指数',
type: 'gauge',
center: ['50%', '75%'],
startAngle: 180, // 开始角度,水平方向从正上方开始
endAngle: 0, // 结束角度,水平方向从正下方结束
radius: '100%',
min: 0,
max: 100,
axisLine: {
lineStyle: {
width: 18, // 设置宽度以适应半径
height: 30, // 设置高度以适应半径
color: props.colorList
}
},
axisTick: {
distance: -18,
length: 20,
splitNumber: 1,
lineStyle: {
color: '#fff',
width: 1
}
},
splitLine: {
show: true,
length: 18, // 设置仪表盘分割线线长
lineStyle: {
color: 'white' // 设置仪表盘分割线颜色
}
},
axisLabel: {
distance: -5,
color: 'inherit',
fontSize: 12,
// 设置仪表盘刻度
formatter: function (e: any) {
switch (e + '') {
case '10':
return '10'
case '20':
return '20'
case '30':
return '30'
case '40':
return '40'
case '50':
return '50'
case '60':
return '60'
case '70':
return '70'
case '80':
return '80'
case '90':
return '90'
case '100':
return '100'
default:
return e
}
}
},
pointer: {
// show: true,
// showAbove: true, // 指针是否显示在标题和仪表盘详情上方。
// offsetCenter: [-10, 0], // 相对于仪表盘中心的偏移位置,数组第一项是水平方向的偏移,第二项是垂直方向的偏移。可以是绝对的数值,也可以是相对于仪表盘半径的百分比。
// length: '40%', // 指针长度,可以是绝对数值,也可以是相对于半径的半分比。
// width: 8, // 指针宽度。
itemStyle: {
// 指针样式
color: 'inherit'
}
},
detail: {
offsetCenter: [0, 25], // 设置仪表盘中心文本位置
fontSize: 18, // 设置仪表盘中心文本字体大小
color: 'inherit' // 设置仪表盘中心文本颜色跟随刻度文本颜色
},
data: []
}
]
}
//监听options变化,给图表赋值
watch(
props.options,
(newValue) => {
if (instrumentChart.value) {
//当数据有更新时候,可使用lineChart.value.clear()清除现有的图表状态,然后再应用新的配置。
instrumentChart.value.clear()
//数据清空,因为使用_.merge合并时,会残留历史数据
chartOptions.series.forEach((item: any) => {
item.data = []
})
_.merge(chartOptions, newValue)
instrumentChart.value.setOption(chartOptions)
// 监听窗口大小变化,自动调整图表大小
setTimeout(() => {
instrumentChart.value.resize()
})
}
},
{ deep: true }
)
onMounted(() => {
// 初始化 ECharts 实例
instrumentChart.value = echarts.init(
document.getElementById(props.id) as HTMLDivElement
)
// 设置 ECharts 配置项
instrumentChart.value.setOption(chartOptions)
// 监听窗口大小变化,自动调整图表大小
window.addEventListener('resize', () => {
instrumentChart.value.resize()
})
// 监听轴标签颜色变化
instrumentChart.value.on('updateAxisPointer', function (event: any) {
if (event.axisType === 'category') {
let axisPointer = event.axisPointer
let tickColor = axisPointer.axis.axisTick.lineStyle.color
// 更新 detail 标签的颜色
instrumentChart.value.setOption({
series: [
{
formatter: 'text',
textStyle: {
color: tickColor
}
}
]
})
}
})
})
onUnmounted(() => {
// 销毁 ECharts 实例
instrumentChart.value.dispose()
// 移除窗口大小变化监听器
window.removeEventListener('resize', () => instrumentChart.value.resize())
})
</script>