LiRen-qiu 4 months ago
parent 1f732bfb2a
commit 07159c6dfe

@ -0,0 +1,165 @@
<template>
<div class="wordcloud-container">
<div v-if="loading" class="loading-overlay">
<i class="el-icon-loading"></i>
<span>加载词云中...</span>
</div>
<div id="wordCloudChart" ref="wordCloudChart" class="wordcloud-chart"></div>
</div>
</template>
<script>
import { getSampleWordCloudData } from '@/utils/wordcloud';
import * as echarts from 'echarts';
import 'echarts-wordcloud';
export default {
name: 'WordCloud',
props: {
useSample: {
type: Boolean,
default: true
}
},
data() {
return {
loading: true,
chart: null,
wordData: []
};
},
mounted() {
console.log("词云组件已挂载");
this.initChart();
window.addEventListener('resize', this.resizeChart);
},
beforeDestroy() {
window.removeEventListener('resize', this.resizeChart);
if (this.chart) {
this.chart.dispose();
}
},
methods: {
initChart() {
this.loading = true;
console.log("初始化词云图表");
// 使
this.wordData = getSampleWordCloudData();
console.log("词云数据已加载", this.wordData.length);
// DOM
this.$nextTick(() => {
const chartDom = this.$refs.wordCloudChart;
if (!chartDom) {
console.error("找不到图表DOM元素");
return;
}
try {
// 使echarts
this.chart = echarts.init(chartDom);
console.log("echarts实例已创建");
//
const option = {
series: [{
type: 'wordCloud',
shape: 'circle',
left: 'center',
top: 'center',
width: '80%',
height: '80%',
right: null,
bottom: null,
sizeRange: [14, 80],
rotationRange: [-45, 45],
rotationStep: 15,
gridSize: 8,
drawOutOfBound: false,
textStyle: {
fontFamily: '"Microsoft YaHei", "微软雅黑", Arial, sans-serif',
fontWeight: 'bold',
color: function() {
//
const colors = [
'#1890FF', '#2FC25B', '#FACC14', '#223273', '#8543E0',
'#13C2C2', '#3436C7', '#F04864', '#EE6666', '#749F83'
];
return colors[Math.floor(Math.random() * colors.length)];
}
},
emphasis: {
focus: 'self',
textStyle: {
shadowBlur: 10,
shadowColor: '#333'
}
},
data: this.wordData
}]
};
//
this.chart.setOption(option);
console.log("词云配置已应用");
//
this.$emit('data-loaded', this.wordData);
//
setTimeout(() => {
this.loading = false;
console.log("词云加载完成");
}, 500);
} catch (error) {
console.error("初始化词云图表时发生错误:", error);
this.loading = false;
}
});
},
resizeChart() {
if (this.chart) {
this.chart.resize();
}
}
}
}
</script>
<style scoped>
.wordcloud-container {
width: 100%;
height: 350px;
position: relative;
background: #ffffff;
margin: 0;
padding: 0;
overflow: hidden;
}
.wordcloud-chart {
width: 100%;
height: 100%;
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
}
.loading-overlay i {
font-size: 32px;
margin-bottom: 10px;
color: #409EFF;
}
</style>

@ -12,11 +12,15 @@ import * as XLSX from 'xlsx'
import Papa from 'papaparse'
import _ from 'lodash'
// 确保echarts可以全局访问
Vue.prototype.$echarts = echarts
Vue.prototype.$xlsx = XLSX
Vue.prototype.$papa = Papa
Vue.prototype.$_ = _
// 移除错误的注册方式
// echarts.use(require('echarts-wordcloud'))
Vue.use(ElementUI)
Vue.config.productionTip = false

@ -1,12 +1,158 @@
// 词云可视化Cirrus相关功能模块
// 负责词云数据生成、处理与可视化配置
export function generateWordCloudData(text) {
// TODO: 实现分词与词频统计,返回词云数据结构
return [];
// 中文常用停用词列表
const STOPWORDS = ['的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个', '上', '也', '很', '到', '说', '要', '去', '你', '会', '着', '没有', '看', '好', '自己', '这'];
/**
* 生成词云数据
* @param {string} text 输入文本
* @param {number} limit 最大词数
* @returns {Array} 词云数据数组
*/
export function generateWordCloudData(text, limit = 100) {
if (!text) return [];
// 简单分词 (仅用于示例,实际项目建议使用专业分词库)
let words = text
.replace(/[^\w\u4e00-\u9fa5]/g, ' ')
.split(/\s+/)
.filter(w => w && w.length > 1 && !STOPWORDS.includes(w));
// 统计词频
const freq = {};
words.forEach(w => {
freq[w] = (freq[w] || 0) + 1;
});
// 转为数组并按频率排序
let arr = Object.entries(freq)
.map(([text, value]) => ({ text, value }))
.sort((a, b) => b.value - a.value)
.slice(0, limit);
return arr;
}
/**
* 获取示例词云数据
* @returns {Array} 示例词云数据
*/
export function getSampleWordCloudData() {
return [
{ text: '航空', value: 120 },
{ text: '航班', value: 98 },
{ text: '飞机', value: 85 },
{ text: '数据', value: 76 },
{ text: '分析', value: 65 },
{ text: '系统', value: 60 },
{ text: '处理', value: 55 },
{ text: '可视化', value: 50 },
{ text: '统计', value: 48 },
{ text: '机场', value: 45 },
{ text: '旅客', value: 40 },
{ text: '信息', value: 38 },
{ text: '服务', value: 36 },
{ text: '航线', value: 34 },
{ text: '出发', value: 32 },
{ text: '到达', value: 30 },
{ text: '延误', value: 28 },
{ text: '安全', value: 26 },
{ text: '乘客', value: 25 },
{ text: '行李', value: 24 },
{ text: '票价', value: 22 },
{ text: '订票', value: 20 },
{ text: '登机', value: 18 },
{ text: '起飞', value: 16 },
{ text: '降落', value: 15 },
{ text: '国际', value: 45 },
{ text: '航空公司', value: 75 },
{ text: '时刻表', value: 35 },
{ text: '客运', value: 28 },
{ text: '联程', value: 23 },
{ text: '机组', value: 22 },
{ text: '头等舱', value: 19 },
{ text: '商务舱', value: 17 },
{ text: '经济舱', value: 15 },
{ text: '转机', value: 32 },
{ text: '值机', value: 26 }
];
}
export function getWordCloudOptions(data) {
// TODO: 返回词云图表的配置项
return {};
/**
* 获取词云配置
* @param {Array} data 词云数据
* @param {Object} options 自定义配置
* @returns {Object} ECharts配置项
*/
export function getWordCloudOptions(data, options = {}) {
const defaultOptions = {
sizeRange: [14, 60],
rotationRange: [-90, 90],
shape: 'circle',
fontFamily: '"Microsoft YaHei", "微软雅黑", Arial, sans-serif',
colorMode: 'random' // 'random' 或 'gradient'
};
const mergedOptions = { ...defaultOptions, ...options };
// 颜色函数
let colorFunction;
if (mergedOptions.colorMode === 'random') {
const colors = [
'#1890FF', '#2FC25B', '#FACC14', '#223273', '#8543E0',
'#13C2C2', '#3436C7', '#F04864', '#EE6666', '#749F83',
'#CA8622', '#409EFF', '#36CBCB', '#FFA74D', '#4B7AF0'
];
colorFunction = function(params) {
return colors[Math.abs(params.data.text.charCodeAt(0)) % colors.length];
};
} else {
// 渐变色
colorFunction = function(params) {
// 根据值大小返回不同深浅的颜色
const maxValue = data.length > 0 ? data[0].value : 100;
const ratio = params.data.value / maxValue;
// 从蓝色到红色的渐变
const r = Math.round(255 * ratio);
const b = Math.round(255 * (1 - ratio));
return `rgb(${r}, 100, ${b})`;
};
}
return {
series: [{
type: 'wordCloud',
shape: mergedOptions.shape,
left: 'center',
top: 'center',
width: '90%',
height: '90%',
sizeRange: mergedOptions.sizeRange,
rotationRange: mergedOptions.rotationRange,
rotationStep: 45,
gridSize: 8,
drawOutOfBound: false,
textStyle: {
fontFamily: mergedOptions.fontFamily,
fontWeight: 'bold',
color: colorFunction
},
emphasis: {
textStyle: {
fontWeight: 'bold',
shadowBlur: 10,
shadowColor: '#333'
}
},
// 新增动画配置
layoutAnimation: true,
animationDuration: 1000,
animationEasing: 'cubicOut',
animationDelay: function (idx) {
return idx * 100;
},
data: data
}]
};
}

@ -6,40 +6,20 @@
<div class="left-panel">
<div class="panel cirrus-panel">
<div class="panel-header">
<h3>词云可视化 (Cirrus)</h3>
<h3>词云可视化</h3>
<div class="panel-controls">
<el-button size="mini" icon="el-icon-refresh" circle></el-button>
<el-button size="mini" icon="el-icon-refresh" circle @click="refreshWordCloud"></el-button>
<el-button size="mini" icon="el-icon-question" circle></el-button>
</div>
</div>
<div class="panel-content">
<el-tabs type="card">
<el-tab-pane label="Cirrus">
<div class="wordcloud-container">
<div class="wordcloud-placeholder">
<i class="el-icon-picture-outline"></i>
<div>词云图将显示在这里</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="Terms">
<div class="terms-table-placeholder">
<el-table :data="termsData" size="mini" border style="width: 100%">
<el-table-column prop="term" label="Term" width="120"></el-table-column>
<el-table-column prop="freq" label="Frequency" width="100"></el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="Links">
<div class="links-table-placeholder">
<el-table :data="linksData" size="mini" border style="width: 100%">
<el-table-column prop="source" label="Source" width="120"></el-table-column>
<el-table-column prop="target" label="Target" width="120"></el-table-column>
<el-table-column prop="weight" label="Weight" width="80"></el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
<div class="wordcloud-container">
<WordCloud
:use-sample="true"
@data-loaded="onWordCloudDataLoaded"
ref="wordCloud"
/>
</div>
</div>
</div>
<!-- 语料库面板保持不变 -->
@ -181,11 +161,24 @@
</template>
<script>
import WordCloud from '@/components/features/WordCloud.vue';
export default {
name: 'StatisticsPage',
components: {
WordCloud
},
data() {
return {
termSlider: 25,
wordCloudOptions: {
//
colorMode: 'random', // 'random' 'gradient'
shape: 'circle',
sizeRange: [14, 80], //
rotationRange: [-45, 45], //
fontFamily: '"Microsoft YaHei", "微软雅黑", Arial, sans-serif' // 使
},
contextData: [
{ document: 'airline_data', left: '摩尔多瓦国际航空公司 Moldavian', term: 'air...', right: '...lines', id: 'ZM 391 基希讷乌 联盟' },
{ document: 'airline_data', left: '基希讷乌 联盟航空公司 Alliance', term: 'air...', right: '...lines', id: '3A 317 美国 东' },
@ -206,7 +199,38 @@ export default {
]
}
},
mounted() {
//
this.$nextTick(() => {
setTimeout(() => {
if (this.$refs.wordCloud) {
this.$refs.wordCloud.initChart();
}
}, 500);
});
},
methods: {
//
refreshWordCloud() {
if (this.$refs.wordCloud) {
this.$refs.wordCloud.initChart();
}
},
//
onWordCloudDataLoaded(data) {
console.log('词云数据加载完成:', data);
//
this.termsData = data.slice(0, 10).map(item => ({
term: item.text,
freq: item.value
}));
// Count
const countEl = document.querySelector('.count');
if (countEl) {
countEl.textContent = data.length.toString();
}
},
//
}
}
@ -271,28 +295,23 @@ export default {
/* 词云面板 */
.wordcloud-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
height: 500px;
overflow: hidden;
background-color: #fff;
border: 1px solid #ebeef5;
border-radius: 4px;
}
.wordcloud-placeholder {
width: 100%;
height: 90%;
max-height: 300px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #f5f7fa;
border-radius: 4px;
color: #909399;
border: 1px dashed #dcdfe6;
}
.wordcloud-placeholder i {
font-size: 32px;
font-size: 36px;
margin-bottom: 10px;
}

Loading…
Cancel
Save