683 lines
19 KiB
Vue
683 lines
19 KiB
Vue
<template>
|
||
<el-header class = 'containerHead'>
|
||
<HeaderPage :pageTitle = 'pageTitle'></HeaderPage>
|
||
<el-button class = 'btnReturnHomePage' @click="returnHome">
|
||
<el-tooltip content = '返回首页'>
|
||
<el-icon><ArrowLeft /></el-icon>
|
||
</el-tooltip>
|
||
</el-button>
|
||
</el-header>
|
||
<el-container>
|
||
<div class="container">
|
||
<div class="left-grid">
|
||
<el-card>
|
||
<el-row>
|
||
<el-form>
|
||
<div class="demo-select">
|
||
<div class="block">
|
||
<span class="demonstration">数据可视化</span>
|
||
<el-select v-model="selectedEntity" placeholder="选择实体">
|
||
<el-option
|
||
v-for="entity in entityOptions"
|
||
:key="entity.value"
|
||
:label="entity.label"
|
||
:value="entity.value"
|
||
></el-option>
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
<!-- 动态输入组件 -->
|
||
<component
|
||
:is="currentComponent"
|
||
ref="dynamicComponent"
|
||
@change="handlerow"
|
||
@update:selectedEntity="handleSelectedEntity"
|
||
@update:selectedrow="handleSelectedRow"
|
||
@update:selectedChartType="handleSelectedChartType"
|
||
@update:selectedrow2="handleSelectedRow2"
|
||
:rowOptions="convertedColumns"
|
||
:rowOptions2="convertedColumns"
|
||
>
|
||
</component>
|
||
<el-form-item>
|
||
<el-button type="primary" size="medium" @click="handleGenerateChart">
|
||
点击生成图表
|
||
</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-row>
|
||
</el-card>
|
||
</div>
|
||
<div class="right-grid">
|
||
<div class="upper-right" v-if="hasUpperContent">
|
||
<el-button type="danger" @click="clearchart">取消</el-button>
|
||
<div ref="chartContainer" class="chart-container"></div>
|
||
</div>
|
||
<div class="lower-right">
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-container>
|
||
</template>
|
||
|
||
<script>
|
||
|
||
import { useRouter } from 'vue-router';
|
||
import * as echarts from 'echarts';
|
||
import Entity1Form from '@/components/graph/Entity1Form.vue';
|
||
import Entity2Form from '@/components/graph/Entity2Form.vue';
|
||
import Entity3Form from '@/components/graph/Entitu3Form.vue';
|
||
import Entity4Form from '@/components/graph/Entity4Form.vue';
|
||
import Entity5Form from '@/components/graph//Entity5Form.vue';
|
||
import HeaderPage from "@/components/HeaderPage.vue";
|
||
export default {
|
||
|
||
components: {
|
||
Entity1Form,
|
||
Entity2Form,
|
||
Entity3Form,
|
||
Entity4Form,
|
||
Entity5Form,
|
||
HeaderPage
|
||
},
|
||
props: ['columns','tabledata'],
|
||
data() {
|
||
|
||
return {
|
||
xz:[],
|
||
yz:[],
|
||
pageTitle:'图形可视化',
|
||
mytable:[],
|
||
mycolumns:'',
|
||
item:[],
|
||
records:[],
|
||
inputStr:[],
|
||
Rdata: [], // 示例数据,可以替换为实际数据
|
||
interval: '', // 间隔x,可以根据需求调整
|
||
stype:'',
|
||
srow:'',
|
||
srow2:'',
|
||
gap:'',
|
||
monthKey:'',
|
||
chartType: 'bar',
|
||
hasUpperContent: false,
|
||
chartInstance: null ,// 存储图表实例的引用
|
||
selectedEntity: '', // 默认选中的实体
|
||
entityOptions: [
|
||
{ value: 'entity1', label: '某年/月/日开始的线索' },
|
||
{value:'entity2',label:'某年/月/日正在进行的线索'},
|
||
{ value: 'entity3', label: '实数区间' },
|
||
{ value: 'entity4', label: '实体线索数或其他指标统计' },
|
||
{
|
||
value: 'entity5', label: '三维数据'
|
||
},
|
||
],
|
||
dates: [],
|
||
};
|
||
},
|
||
// watch: {
|
||
// mytable: {
|
||
// handler(newVal, oldVal) {
|
||
// console.log('mytable changed');
|
||
// console.log('New Value:', newVal);
|
||
// console.log('Old Value:', oldVal);
|
||
// // 在这里可以添加你希望在mytable变化时执行的逻辑
|
||
// },
|
||
// deep: true // 如果需要深度监听对象内部的变化,可以设置deep为true
|
||
// }
|
||
// },
|
||
computed: {
|
||
convertedColumns() {
|
||
return this.mycolumns.map(item => {
|
||
return { value: item.prop, label: item.label };
|
||
})
|
||
},
|
||
currentComponent() {
|
||
switch (this.selectedEntity) {
|
||
case 'entity1':
|
||
return 'Entity1Form';
|
||
case 'entity2':
|
||
return 'Entity2Form';
|
||
case 'entity3':
|
||
return 'Entity3Form';
|
||
case 'entity4':
|
||
return 'Entity4Form';
|
||
case 'entity5':
|
||
return 'Entity5Form';
|
||
default:
|
||
return null;
|
||
}
|
||
}
|
||
},
|
||
created()
|
||
{
|
||
console.log(this.$route.params.tabledata)
|
||
this.mytable=JSON.parse(this.$route.params.tabledata);
|
||
this.mycolumns = JSON.parse(this.$route.params.columns);
|
||
|
||
console.log('Parsed columns:', this.mycolumns,this.table);
|
||
},
|
||
methods: {
|
||
returnHome(){
|
||
window.history.back();
|
||
},
|
||
countsumbystring() {
|
||
// 创建一个对象来存储每个名字对应的实数总和
|
||
this.hasUpperContent = true;
|
||
const sumByName = {};
|
||
|
||
// 遍历数据数组,累加同名字的实数
|
||
this.item.forEach(item => {
|
||
const name = item[0];
|
||
const number = parseFloat(item[1]);
|
||
|
||
// 如果该名字还没有在 sumByName 对象中,则初始化为 0
|
||
if (!sumByName[name]) {
|
||
sumByName[name] = number;
|
||
}
|
||
|
||
// 累加实数
|
||
sumByName[name] += number;
|
||
})
|
||
|
||
// 返回结果对象
|
||
return sumByName;
|
||
},
|
||
handlerow()
|
||
{
|
||
this.srow='';
|
||
this.srow2='';
|
||
},
|
||
|
||
calculateMonthlyRecords() {
|
||
// 假设你有一个包含所有记录的表格,存储在一个数组中
|
||
this.hasUpperContent = true;
|
||
// 创建一个对象来存储每个月的记录条数
|
||
this.records.sort((a, b) => {
|
||
const startDateA = new Date(a[0]);
|
||
const startDateB = new Date(b[0]);
|
||
return startDateA - startDateB;
|
||
});
|
||
const monthlyRecords = {};
|
||
const cords=this.records;
|
||
cords.sort((a,b)=>{const endDateA=new Date(a[1]);
|
||
const endDateB=new Date(b[1]);
|
||
return endDateB-endDateA;
|
||
})
|
||
// 遍历每一条记录
|
||
const startDate1 = new Date(this.records[0][0]);
|
||
|
||
//初始化
|
||
const endDate1 = new Date(this.records[0][1]);
|
||
const currentDate = new Date(startDate1);
|
||
|
||
while (currentDate <= endDate1) {
|
||
if (this.gap === 'month') {
|
||
let monthKey = `${currentDate.getFullYear()}-${currentDate.getMonth() + 1}`;
|
||
monthlyRecords[monthKey] = 0;
|
||
currentDate.setMonth(currentDate.getMonth() + 1);
|
||
} else if (this.gap === 'year') {
|
||
let yearKey = `${currentDate.getFullYear()}`;
|
||
monthlyRecords[yearKey] = 0;
|
||
currentDate.setFullYear(currentDate.getFullYear() + 1);
|
||
} else {
|
||
let dayKey = `${currentDate.getFullYear()}-${currentDate.getMonth() + 1}-${currentDate.getDate()}`;
|
||
monthlyRecords[dayKey] = 0;
|
||
currentDate.setDate(currentDate.getDate()+1);
|
||
}
|
||
}
|
||
|
||
for (var i = 0; i < this.records.length; i++) {
|
||
const record = this.records[i];
|
||
var startDate = new Date(record[0]);
|
||
var endDate = new Date(record[1]);
|
||
|
||
// 遍历开始日期到结束日期之间的每个月
|
||
if (this.gap=="month"){
|
||
const currentMonth = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
|
||
while (currentMonth <= endDate) {
|
||
const monthKey1 = currentMonth.getFullYear() + '-' + (currentMonth.getMonth() + 1);
|
||
|
||
// 如果该月份不存在于monthlyRecords对象中,则创建并初始化为0
|
||
if (!monthlyRecords[monthKey1]) {
|
||
monthlyRecords[monthKey1] = 1;
|
||
}
|
||
|
||
// 增加该月份的记录条数
|
||
monthlyRecords[monthKey1]++;
|
||
|
||
// 下一个月
|
||
currentMonth.setMonth(currentMonth.getMonth() + 1);
|
||
}
|
||
}else if (this.gap=='year'){
|
||
const currentyear = new Date(startDate.getFullYear(), 1, 1);
|
||
while (currentyear <= endDate) {
|
||
const monthKey1 = currentyear.getFullYear() ;
|
||
|
||
// 如果该月份不存在于monthlyRecords对象中,则创建并初始化为0
|
||
if (!monthlyRecords[monthKey1]) {
|
||
monthlyRecords[monthKey1] = 1;
|
||
}
|
||
|
||
// 增加该月份的记录条数
|
||
monthlyRecords[monthKey1]++;
|
||
|
||
// 下一个月
|
||
currentyear.setFullYear(currentyear.getFullYear()+1);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
const currentday=new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
|
||
while (currentday <= endDate) {
|
||
const monthKey1 = currentday.getFullYear() + '-' + (currentday.getMonth() + 1)+(currentday.getDate());
|
||
|
||
// 如果该月份不存在于monthlyRecords对象中,则创建并初始化为0
|
||
if (!monthlyRecords[monthKey1]) {
|
||
monthlyRecords[monthKey1] = 1;
|
||
}
|
||
|
||
// 增加该月份的记录条数
|
||
monthlyRecords[monthKey1]++;
|
||
|
||
// 下一个月
|
||
currentday.setDate(currentday.getDate() + 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 返回每个月的记录条数对象
|
||
return monthlyRecords;
|
||
},
|
||
|
||
countStrings() {
|
||
// Initialize the object to hold string counts
|
||
this.hasUpperContent = true;
|
||
const stringCounts = {};
|
||
|
||
// Split the input string by newlines, trim whitespace, and filter out empty strings
|
||
const strings = this.inputStr;
|
||
|
||
// Iterate over each string and count occurrences
|
||
strings.forEach(str => {
|
||
if (stringCounts[str]) {
|
||
stringCounts[str]++;
|
||
} else {
|
||
stringCounts[str] = 1;
|
||
}
|
||
});
|
||
|
||
// Update the component's data with the computed string counts
|
||
return stringCounts;
|
||
},
|
||
|
||
handleSelectedEntity(value) {
|
||
console.log('Selected Entity:', value);
|
||
// 在这里处理接收到的 selectedEntity 值
|
||
this.interval=value;
|
||
this.gap=value;
|
||
},
|
||
|
||
handleSelectedRow(value) {
|
||
console.log('Selected Row:', value);
|
||
// 在这里处理接收到的 selectedrow 值
|
||
this.srow=value
|
||
},
|
||
|
||
handleSelectedRow2(value) {
|
||
console.log('Selected Row2:', value);
|
||
this.srow2=value
|
||
},
|
||
|
||
handleSelectedChartType(value) {
|
||
console.log('Selected Chart Type:', value);
|
||
this.stype=value;
|
||
// 在这里处理接收到的 selectedChartType 值
|
||
},
|
||
|
||
countMonth(){
|
||
this.hasUpperContent = true;
|
||
this.dates.sort((a, b) => new Date(a) - new Date(b));
|
||
const monthCount = {};
|
||
const startDate = new Date(this.dates[0]);
|
||
|
||
//初始化
|
||
const endDate = new Date(this.dates[this.dates.length - 1]);
|
||
const currentDate = new Date(startDate);
|
||
|
||
while (currentDate <= endDate) {
|
||
if (this.gap === 'month') {
|
||
const monthKey = `${currentDate.getFullYear()}-${currentDate.getMonth() + 1}`;
|
||
monthCount[monthKey] = 0;
|
||
currentDate.setMonth(currentDate.getMonth() + 1);
|
||
} else if (this.gap === 'year') {
|
||
const yearKey = `${currentDate.getFullYear()}`;
|
||
monthCount[yearKey] = 0;
|
||
currentDate.setFullYear(currentDate.getFullYear() + 1);
|
||
} else {
|
||
const dayKey = `${currentDate.getFullYear()}-${currentDate.getMonth() + 1}-${currentDate.getDate()}`;
|
||
monthCount[dayKey] = 0;
|
||
currentDate.setDate(currentDate.getDate()+1);
|
||
}
|
||
}
|
||
|
||
for (const dateStr of this.dates) {
|
||
const date = new Date(dateStr);
|
||
|
||
if (this.gap=='month'){
|
||
|
||
this.monthKey = `${date.getFullYear()}-${date.getMonth() + 1}`;
|
||
}else if(this.gap=='year'){
|
||
this.monthKey = `${date.getFullYear()}`;
|
||
}else
|
||
{
|
||
this.monthKey = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
|
||
}
|
||
if (monthCount[this.monthKey]) {
|
||
monthCount[this.monthKey]++;
|
||
} else {
|
||
monthCount[this.monthKey] = 1;
|
||
}
|
||
}
|
||
|
||
// Format data for ECharts
|
||
const xAxisData = [];
|
||
const seriesData = [];
|
||
for (const key in monthCount) {
|
||
if (Object.prototype.hasOwnProperty.call(monthCount, key)) {
|
||
xAxisData.push(key);
|
||
seriesData.push(monthCount[key]);
|
||
}
|
||
}
|
||
return monthCount;
|
||
},
|
||
|
||
countDataByInterval() {
|
||
this.hasUpperContent = true;
|
||
this.Rdata.sort((a, b) => a - b);
|
||
const counts = {};
|
||
const interval = this.interval;
|
||
this.Rdata.forEach((number) => {
|
||
const index = Math.floor(number / interval);
|
||
const key = `${index * interval}-${(index + 1) * interval}`;
|
||
if (counts[key]) {
|
||
counts[key]++;
|
||
} else {
|
||
counts[key] = 1;
|
||
}
|
||
});
|
||
return counts;
|
||
},
|
||
|
||
drawChart(counts) {
|
||
try
|
||
{
|
||
const chartContainer = this.$refs.chartContainer;
|
||
this.chartInstance = echarts.init(chartContainer);
|
||
|
||
let option;
|
||
if (this.stype === 'bar' || this.stype == 'line') {
|
||
option = {
|
||
xAxis: {
|
||
type: 'category',
|
||
data: Object.keys(counts),
|
||
name: '区间',
|
||
axisLabel:
|
||
{
|
||
frontSize:12,
|
||
interval:0,
|
||
rotate:30
|
||
}
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
},
|
||
series: [{
|
||
data: Object.values(counts),
|
||
type: this.stype,
|
||
}],
|
||
};
|
||
} else if (this.stype === 'pie') {
|
||
const data = Object.entries(counts).map(([name, value]) => ({ value, name }));
|
||
option = {
|
||
series: [{
|
||
type: 'pie',
|
||
radius: '60%',
|
||
data,
|
||
}],
|
||
xAxis: { show: false }, // 不显示区间横轴
|
||
};
|
||
}
|
||
|
||
this.chartInstance.setOption(option);
|
||
}catch(error)
|
||
{
|
||
console.log('错误')
|
||
}
|
||
},
|
||
|
||
clearchart() {
|
||
// 清空图表容器
|
||
if (this.hasUpperContent )
|
||
this.chartInstance.dispose();
|
||
this.hasUpperContent = false;
|
||
// 在这里可以清理之前的图表实例或其他操作
|
||
this.item=[],
|
||
this.records=[],
|
||
this.inputStr=[],
|
||
this.Rdata= [] // 示例数据,可以替换为实际数据
|
||
},
|
||
|
||
handledata(){
|
||
if (!this.mytable || this.mytable.length === 0) {
|
||
alert("没有数据");
|
||
} else {
|
||
// 检查是否有选定的列名
|
||
if (!this.srow) {
|
||
alert("请选择列名");
|
||
return null;
|
||
} else {
|
||
this.inputStr = this.mytable.map(row => {
|
||
// 检查 row 是否存在以及选定的列是否存在
|
||
if (row && row[this.srow] !== undefined) {
|
||
return row[this.srow];
|
||
} else {
|
||
// 如果选定的列不存在,返回一个默认值或者处理错误
|
||
return null; // 你可以根据需要更改此处的行为
|
||
}
|
||
});
|
||
this.dates=this.inputStr;
|
||
// 输出提取的列数据
|
||
this.Rdata=this.inputStr;
|
||
this.xz=this.inputStr;
|
||
|
||
}
|
||
if (!this.srow2) {
|
||
return null;
|
||
} else {
|
||
this.inputStr = this.mytable.map(row => {
|
||
// 检查 row 是否存在以及选定的列是否存在
|
||
if (row && row[this.srow] !== undefined) {
|
||
return row[this.srow];
|
||
} else {
|
||
// 如果选定的列不存在,返回一个默认值或者处理错误
|
||
return null; // 你可以根据需要更改此处的行为
|
||
}
|
||
});
|
||
this.yz=this.inputStr;
|
||
// 输出提取的列数据
|
||
|
||
|
||
}
|
||
}
|
||
},
|
||
|
||
handletwodata() {
|
||
if (!this.mytable || this.mytable.length === 0) {
|
||
alert("没有数据");
|
||
} else {
|
||
if (!this.srow || !this.srow2) {
|
||
return null;
|
||
} else {
|
||
const combinedData = this.mytable.map(row => {
|
||
if (row && row[this.srow] !== undefined) {
|
||
return [row[this.srow], row[this.srow2]];
|
||
} else {
|
||
return [null, null]; // 如果选定的属性不存在,返回默认值或者处理错误
|
||
}
|
||
});
|
||
this.records = combinedData;
|
||
this.item = combinedData;
|
||
console.log(combinedData);
|
||
}
|
||
}
|
||
},
|
||
|
||
handleGenerateChart() {
|
||
this.clearchart();
|
||
console.log(this.columns)
|
||
try{
|
||
this.handledata();
|
||
this.handletwodata();
|
||
} catch(error) {
|
||
alert("请选择数据")
|
||
}
|
||
|
||
|
||
if (this.selectedEntity === 'entity1') {
|
||
// Count occurrences of each month-year combination
|
||
if (!this.gap || !this.srow || !this.stype) {
|
||
console.log(this.gap,this.srow , this.stype);
|
||
alert("请选择实体、列名或者类型");
|
||
return;
|
||
}
|
||
const counts= this.countMonth()
|
||
setTimeout(()=>this.drawChart(counts),500);
|
||
} else if (this.selectedEntity=='entity2'){
|
||
const counts=this.calculateMonthlyRecords();
|
||
console.log(counts);
|
||
setTimeout(()=>this.drawChart(counts),500);
|
||
} else if (this.selectedEntity=='entity3'){
|
||
const counts=this.countDataByInterval();
|
||
setTimeout(()=>this.drawChart(counts),500);
|
||
}else if(this.selectedEntity=='entity4'){
|
||
if (this.srow2!='entity'){
|
||
const counts=this.countsumbystring();
|
||
setTimeout(()=>this.drawChart(counts),500);
|
||
} else {
|
||
const counts=this.countStrings();
|
||
setTimeout(()=>this.drawChart(counts),500);
|
||
}
|
||
}
|
||
else if (this.selectedEntity=='entity5'){
|
||
this.hasUpperContent = true;
|
||
const counts={}
|
||
for (let i = 0; i < this.xz.length; i++) {
|
||
counts[this.xz[i]] = this.yz[i];
|
||
}
|
||
setTimeout(()=>this.drawChart(counts),500);}
|
||
else {
|
||
this.$message.warning('请选择实体');
|
||
}
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.container {
|
||
display: grid;
|
||
grid-template-columns: 35% 65%;
|
||
width: 100%;
|
||
height: 80vh;
|
||
gap: 10px;
|
||
text-align: center
|
||
}
|
||
|
||
.left-grid, .right-grid {
|
||
background-color: #f0f0f0;
|
||
border-radius: 2%;
|
||
padding: 20px;
|
||
height: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.right-grid {
|
||
display: grid;
|
||
grid-template-rows: 600px auto; /* 上半部分固定为600px,下半部分自动填充 */
|
||
background-color: #e9d7df;
|
||
}
|
||
|
||
.upper-right {
|
||
/* 上半部分样式 */
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
/* 如果内容溢出,隐藏溢出部分 */
|
||
}
|
||
|
||
.lower-right {
|
||
/* 下半部分样式 */
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
overflow-y: auto; /* 如果内容过多,显示滚动条 */
|
||
}
|
||
.chart-container {
|
||
width: 90%;
|
||
height: 90%;
|
||
text-align: center;
|
||
}
|
||
|
||
.el-row {
|
||
margin-top: 5px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.demo-select {
|
||
display: flex;
|
||
width: 100%;
|
||
padding: 0;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.demo-select .block {
|
||
padding: 10px 0;
|
||
text-align: center;
|
||
border-right: solid 1px var(--el-border-color);
|
||
flex: 1;
|
||
}
|
||
|
||
.demo-select .block:last-child {
|
||
border-right: none;
|
||
}
|
||
|
||
.demo-select .demonstration {
|
||
display: block;
|
||
color: var(--el-text-color-secondary);
|
||
font-size: 14px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.containerHead{
|
||
padding:0;
|
||
height:8%;
|
||
padding :0;
|
||
width: 100%;
|
||
display:flex;
|
||
justify-content: space-between;
|
||
background-color: white;
|
||
align-items: center;
|
||
}
|
||
.HeaderPage{
|
||
width: 90%;
|
||
}
|
||
.btnReturnHomePage{
|
||
font-size: large;
|
||
width: 3%;
|
||
font-weight: 25px;
|
||
}
|
||
</style>
|
||
|