提交 b18317d5 authored 作者: 祖铭松's avatar 祖铭松

feat: chart

上级 fdd10457
差异被折叠。
......@@ -20,6 +20,7 @@
"plotly.js-dist": "^2.23.2",
"pubsub-js": "^1.9.4",
"tiff.js": "^1.0.0",
"unipept-visualizations": "^2.1.0",
"vue": "^3.3.4"
},
"devDependencies": {
......
<template>
<div ref="chartRef"></div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as Plot from '@observablehq/plot';
// import * as d3 from 'd3';
import { chartData } from '../mock';
import PubSub from 'pubsub-js';
// 根节点实例
const chartRef = ref(null);
// 配置项数据
const options = ref({});
// pubsub实例
const pubSubInstance = ref(null);
// plot实例
const plotInstance = ref(null);
// 生成图表
const generateChart = (data) => {
const ret = options.value;
// console.log(ret);
// 点,线,阴影颜色设置
let point_color = '#FF5733';
let line_color = '#39FDBE';
let shadow_color = '#D6D6D6';
if (ret.data_color_list?.length) {
point_color = ret.data_color_list[0];
line_color = ret.data_color_list[1];
shadow_color = ret.data_color_list[2];
}
plotInstance.value = Plot.plot({
width: ret.chart_width || 800,
height: ret.chart_height || 700,
marginTop: ret.margin_top || 60,
marginRight: ret.margin_right || 60,
marginBottom: ret.margin_bottom || 60,
marginLeft: ret.margin_left || 60,
color: {
legend: ret.show_legend
},
x: {
grid: ret.show_x_gridLine,
domain: ['Normal', 'Tumor']
},
y: {
grid: ret.show_y_gridLine,
domain: [0, 10]
},
marks: [
// Plot.frame(),
Plot.ruleX([0]),
Plot.ruleY([0]),
Plot.axisX({
label: !!ret.x_title ? ret.x_title : null,
stroke: ret.x_tickMarks_color || 'currentColor',
tickSize: ret.show_x_tickMarks ? ret.x_tickMarks_length : 0,
fill: ret.xtitle_font_color || 'currentColor',
fontSize: ret.xtitle_font_size || 16,
fontStyle: ret.xtitle_font_style ? 'oblique' : 'normal',
fontFamily: ret.xtitle_font_family || 'Arial',
fontWeight: ret.xtitle_font_weight ? 'bold' : 'normal'
}),
Plot.axisY({
label: !!ret.y_title ? ret.y_title : null,
stroke: ret.y_tickMarks_color || 'currentColor',
tickSize: ret.show_x_tickMarks ? ret.y_tickMarks_length : 0,
fill: ret.ytitle_font_color || 'currentColor',
fontSize: ret.ytitle_font_size || 16,
fontStyle: ret.ytitle_font_style ? 'oblique' : 'normal',
fontFamily: ret.ytitle_font_family || 'Arial',
fontWeight: ret.ytitle_font_weight ? 'bold' : 'normal'
}),
Plot.dot(
data,
Plot.dodgeX({
dx: 170,
filter: (d) => d.group === 'Tumor',
y: 'number',
fill: 'group',
anchor: 'middle',
padding: 0.5
})
),
Plot.dot(
data,
Plot.dodgeX({
dx: -170,
y: 'number',
filter: (d) => d.group === 'Normal',
fill: 'group',
anchor: 'middle',
padding: 0.5
})
),
Plot.tip(
data,
Plot.pointer({
x: 'group',
y: 'number'
})
),
Plot.text([ret.title || ''], {
x: 1,
fill: ret.title_font_color || 'currentColor',
fontSize: ret.title_font_size || 20,
fontStyle: ret.title_font_style ? 'oblique' : 'normal',
fontFamily: ret.title_font_family || 'Arial',
fontWeight: ret.title_font_weight ? 'bold' : 'normal',
frameAnchor: 'top',
textAnchor: 'start'
})
]
});
chartRef.value.appendChild(plotInstance.value);
};
onMounted(() => {
pubSubInstance.value = PubSub.subscribe('options', (m, data) => {
options.value = data;
chartRef.value.removeChild(plotInstance.value);
generateChart(chartData);
});
generateChart(chartData);
});
onUnmounted(() => {
PubSub.unsubscribe(pubSubInstance.value);
});
</script>
<style scoped></style>
<template>
<div class="form-container">
<h3 class="type content">画布</h3>
<div class="form-item">
<span>宽:</span>
<input type="text" v-model="dynamic_parameter.chart_width" />
</div>
<div class="form-item">
<span>高:</span>
<input type="text" v-model="dynamic_parameter.chart_height" />
</div>
<div class="form-item">
<span>上边距:</span>
<input type="text" v-model="dynamic_parameter.margin_top" />
</div>
<div class="form-item">
<span>右边距:</span>
<input type="text" v-model="dynamic_parameter.margin_right" />
</div>
<div class="form-item">
<span>下边距:</span>
<input type="text" v-model="dynamic_parameter.margin_bottom" />
</div>
<div class="form-item">
<span>左边距:</span>
<input type="text" v-model="dynamic_parameter.margin_left" />
</div>
<h3 class="type content">文本</h3>
<div class="form-item">
<span>字体:</span>
<input type="text" v-model="dynamic_parameter.title_font_family" />
</div>
<div class="form-item">
<span>字号:</span>
<input type="text" v-model="dynamic_parameter.title_font_size" />
</div>
<div class="form-item">
<span>字体颜色:</span>
<input type="text" v-model="dynamic_parameter.title_font_color" />
</div>
<div class="form-item">
<span>字体加粗:</span>
<input type="checkbox" v-model="dynamic_parameter.title_font_weight" />
</div>
<div class="form-item">
<span>斜体:</span>
<input type="checkbox" v-model="dynamic_parameter.title_font_style" />
</div>
<h3 class="type content">X轴配置</h3>
<div class="form-item">
<span>显示坐标轴:</span>
<input type="checkbox" v-model="dynamic_parameter.show_x_axis" />
</div>
<div class="form-item">
<span>显示刻度线:</span>
<input type="checkbox" v-model="dynamic_parameter.show_x_tickMarks" />
</div>
<div class="form-item">
<span>显示网格:</span>
<input type="checkbox" v-model="dynamic_parameter.show_x_gridLine" />
</div>
<div class="form-item">
<span>刻度线颜色:</span>
<input type="text" v-model="dynamic_parameter.x_tickMarks_color" />
</div>
<div class="form-item">
<span>刻度线长度:</span>
<input type="text" v-model="dynamic_parameter.x_tickMarks_length" />
</div>
<div class="form-item">
<span>字体:</span>
<input type="text" v-model="dynamic_parameter.xtitle_font_family" />
</div>
<div class="form-item">
<span>字号:</span>
<input type="text" v-model="dynamic_parameter.xtitle_font_size" />
</div>
<div class="form-item">
<span>字体颜色:</span>
<input type="text" v-model="dynamic_parameter.xtitle_font_color" />
</div>
<div class="form-item">
<span>字体加粗:</span>
<input type="checkbox" v-model="dynamic_parameter.xtitle_font_weight" />
</div>
<div class="form-item">
<span>斜体:</span>
<input type="checkbox" v-model="dynamic_parameter.xtitle_font_style" />
</div>
<div class="form-item">
<span>x轴标题:</span>
<input type="text" v-model="dynamic_parameter.x_title" />
</div>
<h3 class="type content">Y轴配置</h3>
<div class="form-item">
<span>显示坐标轴:</span>
<input type="checkbox" v-model="dynamic_parameter.show_y_axis" />
</div>
<div class="form-item">
<span>显示刻度线:</span>
<input type="checkbox" v-model="dynamic_parameter.show_y_tickMarks" />
</div>
<div class="form-item">
<span>显示网格:</span>
<input type="checkbox" v-model="dynamic_parameter.show_y_gridLine" />
</div>
<div class="form-item">
<span>刻度线颜色:</span>
<input type="text" v-model="dynamic_parameter.y_tickMarks_color" />
</div>
<div class="form-item">
<span>刻度线长度:</span>
<input type="text" v-model="dynamic_parameter.y_tickMarks_length" />
</div>
<div class="form-item">
<span>字体:</span>
<input type="text" v-model="dynamic_parameter.ytitle_font_family" />
</div>
<div class="form-item">
<span>字号:</span>
<input type="text" v-model="dynamic_parameter.ytitle_font_size" />
</div>
<div class="form-item">
<span>字体颜色:</span>
<input type="text" v-model="dynamic_parameter.ytitle_font_color" />
</div>
<div class="form-item">
<span>字体加粗:</span>
<input type="checkbox" v-model="dynamic_parameter.ytitle_font_weight" />
</div>
<div class="form-item">
<span>斜体:</span>
<input type="checkbox" v-model="dynamic_parameter.ytitle_font_style" />
</div>
<div class="form-item">
<span>x轴标题:</span>
<input type="text" v-model="dynamic_parameter.y_title" />
</div>
<h3 class="type content">图表配置</h3>
<div class="form-item">
<span>散点图半径:</span>
<input type="text" v-model="dynamic_parameter.point_radius" />
</div>
<h3 class="type content">图例</h3>
<div class="form-item">
<span>显示图例:</span>
<input type="checkbox" v-model="dynamic_parameter.show_legend" />
</div>
<div class="btn">
<button @click="submit">提交</button>
</div>
</div>
</template>
<script setup>
import { reactive, onMounted } from 'vue';
import PubSub from 'pubsub-js';
//动态图参数,固定参数
const dynamic_parameter = reactive({
//长宽间距
chart_width: 800,
chart_height: 700,
margin_top: 60,
margin_right: 60,
margin_bottom: 60,
margin_left: 60,
//标题配置
title: '',
title_font_family: 'Arial',
title_font_weight: true,
title_font_size: 20,
title_font_color: '#333333',
title_font_style: true,
//横轴标题
xtitle_font_family: 'Arial',
xtitle_font_weight: false,
xtitle_font_style: false,
xtitle_font_size: 16,
xtitle_font_color: '#333333',
//纵轴标题
ytitle_font_family: 'Arial',
ytitle_font_weight: false,
ytitle_font_style: false,
ytitle_font_size: 16,
ytitle_font_color: '#333333',
//颜色设置
data_color_list: ['#FF5733', '#39FDBE', '#D6D6D6'],
point_radius: 5, // 散点图半径
//图例
show_legend: true,
legend_position: '1,1',
//坐标轴
show_x_axis: true,
show_x_line: true,
show_x_tickMarks: true,
show_x_gridLine: false,
show_x_title: true,
x_title: '',
x_tickMarks_color: '#333333',
x_tickMarks_length: '5',
show_y_axis: true,
show_y_line: true,
show_y_tickMarks: true,
show_y_gridLine: false,
show_y_title: true,
y_title: '',
y_tickMarks_color: '#333333',
y_tickMarks_length: '5',
//节点相关的参数,可能还会有更多
node_id: null, //节点id
node_color: null, //节点颜色
is_delect_node: false, //是否删除节点
node_search_name: null, //节点名称搜索
node_search_color: '#FF0404', //节点搜索后的颜色
node_edit_name: null, //修改节点名称
node_height: 100, //节点大小
path_id: null
});
const submit = () => {
PubSub.publish('options', dynamic_parameter);
};
onMounted(() => {
submit();
});
</script>
<style scoped>
.form-item span {
display: inline-block;
width: 100px;
text-align: left;
}
.btn {
width: 100%;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
</style>
<template>
<div class="chart-container">
<Chart />
<Config />
</div>
</template>
<script setup>
import Chart from './components/chart.vue';
import Config from './components/config.vue';
</script>
<style scoped>
.chart-container {
display: flex;
}
</style>
export const chartData = [
{ "group": "Tumor", "number": 7.0308 },
{ "group": "Tumor", "number": 3.8729 },
{ "group": "Tumor", "number": 5.3257 },
{ "group": "Tumor", "number": 4.8477 },
{ "group": "Tumor", "number": 6.1162 },
{ "group": "Tumor", "number": 7.5744 },
{ "group": "Tumor", "number": 5.3557 },
{ "group": "Tumor", "number": 5.3544 },
{ "group": "Tumor", "number": 5.0955 },
{ "group": "Tumor", "number": 7.7152 },
{ "group": "Tumor", "number": 5.0863 },
{ "group": "Tumor", "number": 9.8778 },
{ "group": "Tumor", "number": 4.0259 },
{ "group": "Tumor", "number": 5.4196 },
{ "group": "Tumor", "number": 4.6379 },
{ "group": "Tumor", "number": 4.4749 },
{ "group": "Tumor", "number": 4.0435 },
{ "group": "Tumor", "number": 4.7368 },
{ "group": "Tumor", "number": 5.255 },
{ "group": "Tumor", "number": 4.5886 },
{ "group": "Tumor", "number": 4.2559 },
{ "group": "Tumor", "number": 4.0685 },
{ "group": "Tumor", "number": 4.3016 },
{ "group": "Tumor", "number": 2.9406 },
{ "group": "Tumor", "number": 3.7416 },
{ "group": "Tumor", "number": 3.4882 },
{ "group": "Tumor", "number": 4.1748 },
{ "group": "Tumor", "number": 3.657 },
{ "group": "Tumor", "number": 3.9284 },
{ "group": "Tumor", "number": 4.1116 },
{ "group": "Tumor", "number": 4.7684 },
{ "group": "Tumor", "number": 4.3211 },
{ "group": "Tumor", "number": 5.0288 },
{ "group": "Normal", "number": 3.544 },
{ "group": "Normal", "number": 5.6149 },
{ "group": "Normal", "number": 3.4715 },
{ "group": "Normal", "number": 4.4848 },
{ "group": "Normal", "number": 2.6186 },
{ "group": "Normal", "number": 3.0442 },
{ "group": "Normal", "number": 3.8911 },
{ "group": "Normal", "number": 3.7701 },
{ "group": "Normal", "number": 5.3266 },
{ "group": "Normal", "number": 4.965 },
{ "group": "Normal", "number": 4.6714 },
{ "group": "Normal", "number": 5.5333 },
{ "group": "Normal", "number": 4.7076 },
{ "group": "Normal", "number": 2.985 },
{ "group": "Normal", "number": 3.4718 },
{ "group": "Normal", "number": 3.7863 },
{ "group": "Normal", "number": 4.51 },
{ "group": "Normal", "number": 5.4634 },
{ "group": "Normal", "number": 3.6466 },
{ "group": "Normal", "number": 3.9576 },
{ "group": "Normal", "number": 4.8457 },
{ "group": "Normal", "number": 3.5451 },
{ "group": "Normal", "number": 3.0502 },
{ "group": "Normal", "number": 3.3396 },
{ "group": "Normal", "number": 5.2439 },
{ "group": "Normal", "number": 5.6978 },
{ "group": "Normal", "number": 5.4231 },
{ "group": "Normal", "number": 4.7475 },
{ "group": "Normal", "number": 4.0119 },
{ "group": "Normal", "number": 3.4602 },
{ "group": "Normal", "number": 6.1859 },
{ "group": "Normal", "number": 4.5073 },
{ "group": "Normal", "number": 5.7934 }
]
<template>
<div ref="chartRef"></div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { chartData, chartData1 } from '../mock';
import PubSub from 'pubsub-js';
// import * as d3 from 'd3';
import * as uv from 'unipept-visualizations';
// 根节点实例
const chartRef = ref(null);
// 配置项数据
const options = ref({});
// pubsub实例
const pubSubInstance = ref(null);
// 生成数据
const generateData = () => {
const items = [];
const rows = [];
const columns = Object.keys(chartData1[0]).filter((k) => k !== 'Family');
for (const row of chartData1) {
const newRow = [];
for (const [key, value] of Object.entries(row)) {
if (key === 'Family') {
rows.push(value);
} else {
newRow.push(Number.parseFloat(value));
}
}
items.push(newRow);
}
return {
items,
rows,
columns
};
};
// 生成图表
const generateChart = (data) => {
const { items, rows, columns } = data;
new uv.Heatmap(chartRef.value, items, rows, columns, {
width: 1200,
height: 800,
dendrogramEnabled: true,
squarePadding: 0
});
};
onMounted(() => {
const mackData = generateData();
pubSubInstance.value = PubSub.subscribe('options', (m, data) => {
options.value = data;
generateChart(mackData);
});
generateChart(mackData);
});
onUnmounted(() => {
PubSub.unsubscribe(pubSubInstance.value);
});
</script>
<style scoped></style>
<template>
<div class="form-container">
<h3 class="type content">画布</h3>
<div class="form-item">
<span>宽:</span>
<input type="text" v-model="dynamic_parameter.chart_width" />
</div>
<div class="form-item">
<span>高:</span>
<input type="text" v-model="dynamic_parameter.chart_height" />
</div>
<div class="form-item">
<span>上边距:</span>
<input type="text" v-model="dynamic_parameter.margin_top" />
</div>
<div class="form-item">
<span>右边距:</span>
<input type="text" v-model="dynamic_parameter.margin_right" />
</div>
<div class="form-item">
<span>下边距:</span>
<input type="text" v-model="dynamic_parameter.margin_bottom" />
</div>
<div class="form-item">
<span>左边距:</span>
<input type="text" v-model="dynamic_parameter.margin_left" />
</div>
<h3 class="type content">文本</h3>
<div class="form-item">
<span>字体:</span>
<input type="text" v-model="dynamic_parameter.title_font_family" />
</div>
<div class="form-item">
<span>字号:</span>
<input type="text" v-model="dynamic_parameter.title_font_size" />
</div>
<div class="form-item">
<span>字体颜色:</span>
<input type="text" v-model="dynamic_parameter.title_font_color" />
</div>
<div class="form-item">
<span>字体加粗:</span>
<input type="checkbox" v-model="dynamic_parameter.title_font_weight" />
</div>
<div class="form-item">
<span>斜体:</span>
<input type="checkbox" v-model="dynamic_parameter.title_font_style" />
</div>
<h3 class="type content">X轴配置</h3>
<div class="form-item">
<span>显示坐标轴:</span>
<input type="checkbox" v-model="dynamic_parameter.show_x_axis" />
</div>
<div class="form-item">
<span>显示刻度线:</span>
<input type="checkbox" v-model="dynamic_parameter.show_x_tickMarks" />
</div>
<div class="form-item">
<span>显示网格:</span>
<input type="checkbox" v-model="dynamic_parameter.show_x_gridLine" />
</div>
<div class="form-item">
<span>刻度线颜色:</span>
<input type="text" v-model="dynamic_parameter.x_tickMarks_color" />
</div>
<div class="form-item">
<span>刻度线长度:</span>
<input type="text" v-model="dynamic_parameter.x_tickMarks_length" />
</div>
<div class="form-item">
<span>字体:</span>
<input type="text" v-model="dynamic_parameter.xtitle_font_family" />
</div>
<div class="form-item">
<span>字号:</span>
<input type="text" v-model="dynamic_parameter.xtitle_font_size" />
</div>
<div class="form-item">
<span>字体颜色:</span>
<input type="text" v-model="dynamic_parameter.xtitle_font_color" />
</div>
<div class="form-item">
<span>字体加粗:</span>
<input type="checkbox" v-model="dynamic_parameter.xtitle_font_weight" />
</div>
<div class="form-item">
<span>斜体:</span>
<input type="checkbox" v-model="dynamic_parameter.xtitle_font_style" />
</div>
<div class="form-item">
<span>x轴标题:</span>
<input type="text" v-model="dynamic_parameter.x_title" />
</div>
<h3 class="type content">Y轴配置</h3>
<div class="form-item">
<span>显示坐标轴:</span>
<input type="checkbox" v-model="dynamic_parameter.show_y_axis" />
</div>
<div class="form-item">
<span>显示刻度线:</span>
<input type="checkbox" v-model="dynamic_parameter.show_y_tickMarks" />
</div>
<div class="form-item">
<span>显示网格:</span>
<input type="checkbox" v-model="dynamic_parameter.show_y_gridLine" />
</div>
<div class="form-item">
<span>刻度线颜色:</span>
<input type="text" v-model="dynamic_parameter.y_tickMarks_color" />
</div>
<div class="form-item">
<span>刻度线长度:</span>
<input type="text" v-model="dynamic_parameter.y_tickMarks_length" />
</div>
<div class="form-item">
<span>字体:</span>
<input type="text" v-model="dynamic_parameter.ytitle_font_family" />
</div>
<div class="form-item">
<span>字号:</span>
<input type="text" v-model="dynamic_parameter.ytitle_font_size" />
</div>
<div class="form-item">
<span>字体颜色:</span>
<input type="text" v-model="dynamic_parameter.ytitle_font_color" />
</div>
<div class="form-item">
<span>字体加粗:</span>
<input type="checkbox" v-model="dynamic_parameter.ytitle_font_weight" />
</div>
<div class="form-item">
<span>斜体:</span>
<input type="checkbox" v-model="dynamic_parameter.ytitle_font_style" />
</div>
<div class="form-item">
<span>x轴标题:</span>
<input type="text" v-model="dynamic_parameter.y_title" />
</div>
<h3 class="type content">图例</h3>
<div class="form-item">
<span>显示图例:</span>
<input type="checkbox" v-model="dynamic_parameter.show_legend" />
</div>
<div class="form-item">
<span>图例位置:</span>
<input type="text" v-model="dynamic_parameter.legend_position" />
</div>
<div class="btn">
<button @click="submit">提交</button>
</div>
</div>
</template>
<script setup>
import { reactive, onMounted } from 'vue';
import PubSub from 'pubsub-js';
//动态图参数,固定参数
const dynamic_parameter = reactive({
//长宽间距
chart_width: 800,
chart_height: 600,
margin_top: 60,
margin_right: 60,
margin_bottom: 60,
margin_left: 60,
//标题配置
title: '',
title_font_family: 'Arial',
title_font_weight: true,
title_font_size: 20,
title_font_color: '#333333',
title_font_style: true,
//横轴标题
xtitle_font_family: 'Arial',
xtitle_font_weight: false,
xtitle_font_style: false,
xtitle_font_size: 16,
xtitle_font_color: '#333333',
//纵轴标题
ytitle_font_family: 'Arial',
ytitle_font_weight: false,
ytitle_font_style: false,
ytitle_font_size: 16,
ytitle_font_color: '#333333',
//颜色设置
data_color_list: [],
//图例
show_legend: true,
legend_position: '0,0',
//坐标轴
show_x_axis: true,
show_x_line: true,
show_x_tickMarks: true,
show_x_gridLine: false,
show_x_title: true,
x_title: '',
x_tickMarks_color: '#333333',
x_tickMarks_length: '5',
show_y_axis: true,
show_y_line: true,
show_y_tickMarks: true,
show_y_gridLine: false,
show_y_title: true,
y_title: '',
y_tickMarks_color: '#333333',
y_tickMarks_length: '5',
//节点相关的参数,可能还会有更多
node_id: null, //节点id
node_color: null, //节点颜色
is_delect_node: false, //是否删除节点
node_search_name: null, //节点名称搜索
node_search_color: '#FF0404', //节点搜索后的颜色
node_edit_name: null, //修改节点名称
node_height: 100, //节点大小
path_id: null
});
const submit = () => {
PubSub.publish('options', dynamic_parameter);
};
onMounted(() => {
submit();
});
</script>
<style scoped>
.form-item span {
display: inline-block;
width: 100px;
text-align: left;
}
.btn {
width: 100%;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
</style>
<template>
<div class="chart-container">
<Chart />
<!-- <Config /> -->
</div>
</template>
<script setup>
import Chart from './components/chart.vue';
import Config from './components/config.vue';
</script>
<style scoped>
.chart-container {
display: flex;
}
</style>
This source diff could not be displayed because it is too large. You can view the blob instead.
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论