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

feat: PCA

上级 ad01d3f6
...@@ -40,7 +40,7 @@ const generateChart = (data) => { ...@@ -40,7 +40,7 @@ const generateChart = (data) => {
const titleConfig = { const titleConfig = {
label: !!ret.title ? ret.title : null, label: !!ret.title ? ret.title : null,
color: ret.title_font_color || '#333333', color: ret.title_font_color || '#333333',
fontSize: ret.title_font_size || 30, fontSize: ret.title_font_size || 20,
fontStyle: ret.title_font_style ? 'oblique' : 'normal', fontStyle: ret.title_font_style ? 'oblique' : 'normal',
fontFamily: ret.title_font_family || 'Arial', fontFamily: ret.title_font_family || 'Arial',
fontWeight: ret.title_font_weight ? 'bold' : 'normal' fontWeight: ret.title_font_weight ? 'bold' : 'normal'
......
<template> <template>
<div ref="chartRef"></div> <div ref="chartRef" class="chart-box">
<div class="tip"></div>
</div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted, onUnmounted } from 'vue'; import { ref, onMounted, onUnmounted } from 'vue';
import * as Plot from '@observablehq/plot'; import { chartMockData } from '../mock.js';
// import * as d3 from 'd3'; import * as d3 from 'https://cdn.jsdelivr.net/npm/d3@7/+esm';
import { chartData } from '../mock';
import PubSub from 'pubsub-js'; import PubSub from 'pubsub-js';
import { setTips, setColor, setLegend, setScale, setAxis, setPoint, setLabel, setElliptic } from '../utils/index';
// 根节点实例
const chartRef = ref(null); const chartRef = ref(null);
// 配置项数据 // 配置项数据
const options = ref({}); const options = ref({});
// 图表实例
const svgInstance = ref(null);
// pubsub实例 // pubsub实例
const pubSubInstance = ref(null); const pubSubInstance = ref(null);
// plot实例
const plotInstance = ref(null);
// 生成图表 // 生成图表
const generateChart = (data) => { const generateChart = (data) => {
const ret = options.value; const ret = options.value;
// console.log(ret);
let width = Number(ret.chart_width) || 700;
// 点,线,阴影颜色设置 let height = Number(ret.chart_height) || 800;
let point_color = '#FF5733'; let marginTop = Number(ret.margin_top) || 120;
let line_color = '#39FDBE'; let marginRight = Number(ret.margin_Right) || 120;
let shadow_color = '#D6D6D6'; let marginBottom = Number(ret.margin_Bottom) || 120;
if (ret.data_color_list?.length) { let marginLeft = Number(ret.margin_Left) || 120;
point_color = ret.data_color_list[0]; let legendPosition = ret.legend_position?.split(',') || [0.5, 1];
line_color = ret.data_color_list[1]; let colors = ret.data_color_list || ['#155C9C', '#44A79D'];
shadow_color = ret.data_color_list[2]; let legendData = ['AD', 'Non_AD'];
}
plotInstance.value = Plot.plot({ // 标题配置
width: ret.chart_width || 800, const titleConfig = {
height: ret.chart_height || 600, label: !!ret.title ? ret.title : null,
marginTop: ret.margin_top || 60, color: ret.title_font_color || '#333333',
marginRight: ret.margin_right || 60, fontSize: ret.title_font_size || 20,
marginBottom: ret.margin_bottom || 60, fontStyle: ret.title_font_style ? 'oblique' : 'normal',
marginLeft: ret.margin_left || 60, fontFamily: ret.title_font_family || 'Arial',
color: { fontWeight: ret.title_font_weight ? 'bold' : 'normal'
legend: ret.show_legend };
},
x: { // 大椭圆
grid: ret.show_x_gridLine, let ellipticOne = {
domain: [-100, 100] rx: (width - marginLeft - marginRight) / 2 - 20,
}, ry: (height - marginTop - marginBottom) / 2 - 20,
y: { cx: (marginLeft + width - marginLeft - marginRight) / 2 + 60,
grid: ret.show_y_gridLine, cy: (marginTop + height - marginTop - marginBottom) / 2 + 60
domain: [-100, 100] };
},
marks: [ // 椭圆 AD
Plot.frame(), let ellipticAD = {
Plot.ruleX([0], { strokeDasharray: '5' }), rx: 80,
Plot.ruleY([0], { strokeDasharray: '5' }), ry: 350,
Plot.axisX({ cx: (marginLeft + width - marginLeft - marginRight) / 2 + 60,
interval: 50, cy: (marginTop + height - marginTop - marginBottom) / 2 + 60,
label: !!ret.x_title ? ret.x_title : null, rotate: 10,
stroke: ret.x_tickMarks_color || 'currentColor', fill: colors[0],
tickSize: ret.show_x_tickMarks ? ret.x_tickMarks_length : 0, fillOpacity: 0.2
fill: ret.xtitle_font_color || 'currentColor', };
fontSize: ret.xtitle_font_size || 16,
fontStyle: ret.xtitle_font_style ? 'oblique' : 'normal', // 椭圆 Non_AD
fontFamily: ret.xtitle_font_family || 'Arial', let ellipticNon_AD = {
fontWeight: ret.xtitle_font_weight ? 'bold' : 'normal' rx: 350,
}), ry: 60,
Plot.axisY({ cx: (marginLeft + width - marginLeft - marginRight) / 2 + 60,
interval: 50, cy: (marginTop + height - marginTop - marginBottom) / 2 + 20,
label: !!ret.y_title ? ret.y_title : null, rotate: 20,
stroke: ret.y_tickMarks_color || 'currentColor', fill: colors[1],
tickSize: ret.show_x_tickMarks ? ret.y_tickMarks_length : 0, fillOpacity: 0.2
fill: ret.ytitle_font_color || 'currentColor', };
fontSize: ret.ytitle_font_size || 16,
fontStyle: ret.ytitle_font_style ? 'oblique' : 'normal', const svg = d3.create('svg').attr('width', width).attr('height', height);
fontFamily: ret.ytitle_font_family || 'Arial',
fontWeight: ret.ytitle_font_weight ? 'bold' : 'normal' let configObj = {
}), ...ret,
Plot.dot(data, { x: 'x', y: 'y', fill: point_color, r: Number(ret.point_radius) || 5, tip: true }), svg,
// Plot.linearRegressionX(data, { width,
// x: 'x', height,
// y: 'y', marginTop,
// ci: 0.5, marginRight,
// stroke: line_color, marginBottom,
// fill: shadow_color, marginLeft,
// fillOpacity: ret.shadow_opacity || 0.5 titleConfig,
// }), colors,
Plot.text([ret.title || ''], { legendData,
x: 1, legendPosition,
fill: ret.title_font_color || 'currentColor', ellipticOne,
fontSize: ret.title_font_size || 20, ellipticAD,
fontStyle: ret.title_font_style ? 'oblique' : 'normal', ellipticNon_AD
fontFamily: ret.title_font_family || 'Arial', };
fontWeight: ret.title_font_weight ? 'bold' : 'normal',
frameAnchor: 'top', setChart(configObj, data);
textAnchor: 'start'
}) svgInstance.value = svg.node();
]
}); // Append the SVG element.
chartRef.value.appendChild(plotInstance.value); chartRef.value.appendChild(svgInstance.value);
};
const setChart = (config, data) => {
const color = setColor(config);
const { x, y } = setScale(config, data);
let configObj = {
...config,
color,
x,
y
};
// rect && elliptic
setElliptic(configObj);
// rects
const points = setPoint(configObj, data);
// tips
setTips(points);
// axis
setAxis(configObj);
// legend
config.show_legend && setLegend(configObj);
// label
setLabel(configObj);
}; };
onMounted(() => { onMounted(() => {
const chartData = chartMockData;
pubSubInstance.value = PubSub.subscribe('options', (m, data) => { pubSubInstance.value = PubSub.subscribe('options', (m, data) => {
options.value = data; options.value = data;
chartRef.value.removeChild(plotInstance.value); chartRef.value.removeChild(svgInstance.value);
generateChart(chartData); generateChart(chartData);
}); });
generateChart(chartData); generateChart(chartData);
...@@ -117,4 +149,12 @@ onUnmounted(() => { ...@@ -117,4 +149,12 @@ onUnmounted(() => {
}); });
</script> </script>
<style scoped></style> <style scoped>
.chart-box {
position: relative;
}
.main {
width: 500px;
overflow: hidden;
}
</style>
...@@ -26,6 +26,10 @@ ...@@ -26,6 +26,10 @@
<input type="text" v-model="dynamic_parameter.margin_left" /> <input type="text" v-model="dynamic_parameter.margin_left" />
</div> </div>
<h3 class="type content">文本</h3> <h3 class="type content">文本</h3>
<div class="form-item">
<span>标题:</span>
<input type="text" v-model="dynamic_parameter.title" />
</div>
<div class="form-item"> <div class="form-item">
<span>字体:</span> <span>字体:</span>
<input type="text" v-model="dynamic_parameter.title_font_family" /> <input type="text" v-model="dynamic_parameter.title_font_family" />
...@@ -55,10 +59,6 @@ ...@@ -55,10 +59,6 @@
<span>显示刻度线:</span> <span>显示刻度线:</span>
<input type="checkbox" v-model="dynamic_parameter.show_x_tickMarks" /> <input type="checkbox" v-model="dynamic_parameter.show_x_tickMarks" />
</div> </div>
<div class="form-item">
<span>显示网格:</span>
<input type="checkbox" v-model="dynamic_parameter.show_x_gridLine" />
</div>
<div class="form-item"> <div class="form-item">
<span>刻度线颜色:</span> <span>刻度线颜色:</span>
<input type="text" v-model="dynamic_parameter.x_tickMarks_color" /> <input type="text" v-model="dynamic_parameter.x_tickMarks_color" />
...@@ -88,7 +88,11 @@ ...@@ -88,7 +88,11 @@
<input type="checkbox" v-model="dynamic_parameter.xtitle_font_style" /> <input type="checkbox" v-model="dynamic_parameter.xtitle_font_style" />
</div> </div>
<div class="form-item"> <div class="form-item">
<span>x轴标题:</span> <span>标题展示:</span>
<input type="checkbox" v-model="dynamic_parameter.show_x_title" />
</div>
<div class="form-item">
<span>标题:</span>
<input type="text" v-model="dynamic_parameter.x_title" /> <input type="text" v-model="dynamic_parameter.x_title" />
</div> </div>
<h3 class="type content">Y轴配置</h3> <h3 class="type content">Y轴配置</h3>
...@@ -100,10 +104,6 @@ ...@@ -100,10 +104,6 @@
<span>显示刻度线:</span> <span>显示刻度线:</span>
<input type="checkbox" v-model="dynamic_parameter.show_y_tickMarks" /> <input type="checkbox" v-model="dynamic_parameter.show_y_tickMarks" />
</div> </div>
<div class="form-item">
<span>显示网格:</span>
<input type="checkbox" v-model="dynamic_parameter.show_y_gridLine" />
</div>
<div class="form-item"> <div class="form-item">
<span>刻度线颜色:</span> <span>刻度线颜色:</span>
<input type="text" v-model="dynamic_parameter.y_tickMarks_color" /> <input type="text" v-model="dynamic_parameter.y_tickMarks_color" />
...@@ -133,23 +133,22 @@ ...@@ -133,23 +133,22 @@
<input type="checkbox" v-model="dynamic_parameter.ytitle_font_style" /> <input type="checkbox" v-model="dynamic_parameter.ytitle_font_style" />
</div> </div>
<div class="form-item"> <div class="form-item">
<span>x轴标题:</span> <span>标题展示:</span>
<input type="text" v-model="dynamic_parameter.y_title" /> <input type="checkbox" v-model="dynamic_parameter.show_y_title" />
</div>
<h3 class="type content">图表配置</h3>
<div class="form-item">
<span>散点图半径:</span>
<input type="text" v-model="dynamic_parameter.point_radius" />
</div> </div>
<div class="form-item"> <div class="form-item">
<span>阴影透明度:</span> <span>标题:</span>
<input type="text" v-model="dynamic_parameter.shadow_opacity" /> <input type="text" v-model="dynamic_parameter.y_title" />
</div> </div>
<h3 class="type content">图例</h3> <h3 class="type content">图例</h3>
<div class="form-item"> <div class="form-item">
<span>显示图例:</span> <span>显示图例:</span>
<input type="checkbox" v-model="dynamic_parameter.show_legend" /> <input type="checkbox" v-model="dynamic_parameter.show_legend" />
</div> </div>
<div class="form-item">
<span>图例位置:</span>
<input type="text" v-model="dynamic_parameter.legend_position" />
</div>
<div class="btn"> <div class="btn">
<button @click="submit">提交</button> <button @click="submit">提交</button>
</div> </div>
...@@ -165,39 +164,39 @@ const dynamic_parameter = reactive({ ...@@ -165,39 +164,39 @@ const dynamic_parameter = reactive({
//长宽间距 //长宽间距
chart_width: 700, chart_width: 700,
chart_height: 800, chart_height: 800,
margin_top: 60, margin_top: 120,
margin_right: 60, margin_right: 120,
margin_bottom: 60, margin_bottom: 120,
margin_left: 60, margin_left: 120,
//标题配置 //标题配置
title: '', title: 'Principal',
title_font_family: 'Arial', title_font_family: 'Arial',
title_font_weight: true, title_font_weight: true,
title_font_size: 20, title_font_size: 20,
title_font_color: '#333333', title_font_color: '#333333',
title_font_style: true, title_font_style: false,
//横轴标题 //横轴标题
xtitle_font_family: 'Arial', xtitle_font_family: 'Arial',
xtitle_font_weight: false, xtitle_font_weight: false,
xtitle_font_style: false, xtitle_font_style: false,
xtitle_font_size: 16, xtitle_font_size: 14,
xtitle_font_color: '#333333', xtitle_font_color: '#333333',
//纵轴标题 //纵轴标题
ytitle_font_family: 'Arial', ytitle_font_family: 'Arial',
ytitle_font_weight: false, ytitle_font_weight: false,
ytitle_font_style: false, ytitle_font_style: false,
ytitle_font_size: 16, ytitle_font_size: 14,
ytitle_font_color: '#333333', ytitle_font_color: '#333333',
//颜色设置 //颜色设置
data_color_list: ['#FF5733', '#39FDBE', '#D6D6D6'], data_color_list: ['#155C9C', '#44A79D'],
point_radius: 5, // 散点图半径 point_radius: 5, // 散点图半径
shadow_opacity: 0.5, // 阴影透明度 0~1 shadow_opacity: 0.5, // 阴影透明度 0~1
//图例 //图例
show_legend: true, show_legend: true,
legend_position: '1,1', legend_position: '0.5,1',
//坐标轴 //坐标轴
show_x_axis: true, show_x_axis: true,
show_x_line: true, show_x_line: true,
...@@ -240,7 +239,7 @@ onMounted(() => { ...@@ -240,7 +239,7 @@ onMounted(() => {
<style scoped> <style scoped>
.form-item span { .form-item span {
display: inline-block; display: inline-block;
width: 100px; width: 120px;
text-align: left; text-align: left;
} }
.btn { .btn {
......
export const chartData = [ export const chartMockData = [
{ type: 'type1', x: '-30', y: '-90' }, {
{ type: 'type1', x: '-30', y: '10' }, "name": "AD",
{ type: 'type1', x: '-15', y: '20' }, "PC1": -13.6199,
{ type: 'type1', x: '15', y: '18' }, "PC2": 19.2571
] },
\ No newline at end of file {
"name": "AD",
"PC1": -26.9176,
"PC2": -83.0864
},
{
"name": "AD",
"PC1": 10.7576,
"PC2": 16.4817
},
{
"name": "AD",
"PC1": -26.8031,
"PC2": 10.3311
},
{
"name": "Non_AD",
"PC1": -31.6168,
"PC2": 11.5461
},
{
"name": "Non_AD",
"PC1": 122.4475,
"PC2": -8.1394
},
{
"name": "Non_AD",
"PC1": -5.0412,
"PC2": 19.3962
},
{
"name": "Non_AD",
"PC1": -29.2065,
"PC2": 14.2135
}
]
import * as d3 from 'https://cdn.jsdelivr.net/npm/d3@7/+esm';
// color config
export const setColor = (config) => {
let colors = ['#2265A1', '#44A79D'];
if (Array.isArray(config.colors)) {
colors = !!config.colors.length ? config.colors : colors;
} else if (!!config.colors) {
colors = config.colors.split(',');
}
return d3.scaleOrdinal(colors).domain(config.legendData);
};
// tips
export const setTips = (rect) => {
const tips = d3
.select('.tip')
.style('visibility', 'hidden')
.style('position', 'relative')
.style('background-color', 'rgba(0, 0, 0, .8)')
.style('color', '#fff')
.style('border-radius', '5px')
.style('width', '120px')
.style('height', '65px')
.style('text-align', 'left')
.style('padding', '5px 10px')
rect.on('mouseover', (event, d) => {
tips.style('visibility', 'visible');
})
.on('mousemove', function (event, d) {
tips.style('visibility', 'visible')
.style('left', event.offsetX + 15 + 'px')
.style('top', event.offsetY + 55 + 'px')
.html(`
<div>${d.name}</div>
<div>x: ${d.PC1}</div>
<div>y: ${d.PC2}</div>
`);
})
.on('mouseleave', function () {
tips.style('visibility', 'hidden');
});
};
// legend
export const setLegend = (config) => {
const g = config.svg.append('g')
.attr('transform', `translate(${(config.width - 210) * config.legendPosition[0]}, ${(config.height - 35) * config.legendPosition[1]})`)
g.append('rect').attr('width', 200)
.attr('height', 30)
.attr('stroke', 'black')
.attr("stroke-width", 2)
.attr('fill', 'transparent')
g.append('g')
.attr('transform', `translate(0, 15)`)
.selectAll()
.data(config.legendData)
.join('g')
.attr('transform', (d, i) => `translate(${i * 90 + 20}, 0)`)
.call((g) =>
g.append("path")
.attr("class", "dot")
.attr("opacity", 1)
.attr("stroke-opacity", 1)
.attr("stroke-width", 2)
.attr("stroke", (d) => config.color(d))
.attr("fill", 'transparent')
.attr("fill-opacity", 1)
.attr("d", (d) => setSymbol(d)))
.call((g) =>
g
.append('text')
.attr('x', 15)
.attr('y', 0)
.attr('dy', '0.3em')
.style('font', '13px sans-serif')
.text((d) => d)
);
};
// scale
export const setScale = (config, data) => {
const xMax = d3.max(data, (d) => d.PC1)
const xMin = d3.min(data, (d) => d.PC1)
const yMax = d3.min(data, (d) => d.PC2)
const yMin = d3.min(data, (d) => d.PC2)
const xDomain = (Math.abs(xMax) >= Math.abs(xMin) ? Math.abs(xMax) : Math.abs(xMin)) + 10
const yDomain = (Math.abs(yMax) >= Math.abs(yMin) ? Math.abs(yMax) : Math.abs(yMin)) + 10
const x = d3
.scaleLinear()
.domain([-xDomain, xDomain])
.range([config.marginLeft, config.width - config.marginRight])
const y = d3
.scaleLinear()
.domain([-yDomain, yDomain])
.range([config.height - config.marginBottom, config.marginTop])
return { x, y }
}
// axis
export const setAxis = (config) => {
config.show_x_axis && config.svg.append('g')
.attr('transform', `translate(0, ${config.height - config.marginBottom})`)
.style('font', `${config.xtitle_font_size}px ${config.xtitle_font_family}`)
.style('color', `${config.xtitle_font_color}`)
.style('font-weight', `${config.xtitle_font_weight ? 'bold' : 'normal'}`)
.style('font-style', `${config.xtitle_font_style ? 'oblique' : 'normal'}`)
.call(d3.axisBottom(config.x).tickSize(Number(config.show_x_tickMarks ? config.x_tickMarks_length : 0)))
.call(g => g.selectAll("line").clone()
.attr("display", d => d == 0 ? 'block' : 'none')
.attr("stroke-dasharray", '3')
.attr("y2", -(config.height - config.marginBottom - config.marginTop))
)
.call((g) => g.selectAll('.domain').remove())
.selectAll('text')
.style('text-anchor', 'middle');
config.show_x_title && config.svg.append('g')
.attr('transform', `translate(${config.width / 2}, ${config.height - config.marginBottom + 50})`)
.append('text')
.style('text-anchor', 'middle')
.text(config.x_title)
config.show_y_axis && config.svg.append('g')
.attr('transform', `translate(${config.marginLeft}, 0)`)
.style('font', `${config.ytitle_font_size}px ${config.ytitle_font_family}`)
.style('color', `${config.ytitle_font_color}`)
.style('font-weight', `${config.ytitle_font_weight ? 'bold' : 'normal'}`)
.style('font-style', `${config.ytitle_font_style ? 'oblique' : 'normal'}`)
.call(d3.axisLeft(config.y).tickSize(Number(config.show_y_tickMarks ? config.y_tickMarks_length : 0)))
.call(g => g.selectAll("line").clone()
.attr("display", d => d == 0 ? 'block' : 'none')
.attr("stroke-dasharray", '3')
.attr("x2", config.width - config.marginLeft - config.marginRight))
.call((g) => g.selectAll('.domain').remove())
config.show_y_title && config.svg.append('g')
.attr('transform', `translate(${config.marginLeft - 50}, ${config.height / 2})`)
.append('text')
.style('text-anchor', 'middle')
.attr('transform', `rotate(-90)`)
.text(config.y_title)
}
// symbols
export const setSymbol = (type) => {
let symbol = d3.symbol().size(30)
return type == 'AD' ? symbol.type(d3.symbols[0])() : symbol.type(d3.symbols[5])()
}
// rect && elliptic
export const setElliptic = (config) => {
let rects = config.svg
.append('g')
.attr('max-width', config.width - config.marginLeft - config.marginRight)
.attr('height', config.height - config.marginTop - config.marginBottom);
rects
.append('rect')
.attr('x', `${config.marginLeft}`)
.attr('y', `${config.marginTop}`)
.attr('width', `${config.width - config.marginLeft - config.marginRight}`)
.attr('height', `${config.height - config.marginTop - config.marginBottom}`)
.attr('stroke', '#000000')
.attr('stroke-width', 2)
.attr('fill', 'white');
rects
.append('ellipse')
.attr('cx', `${config.ellipticOne.cx}`)
.attr('cy', `${config.ellipticOne.cy}`)
.attr('rx', `${config.ellipticOne.rx}`)
.attr('ry', `${config.ellipticOne.ry}`)
.attr('stroke', '#ADADAD')
.attr('stroke-width', 3)
.attr('fill', 'transparent');
rects
.append('ellipse')
.attr('cx', `${config.ellipticAD.cx}`)
.attr('cy', `${config.ellipticAD.cy}`)
.attr('rx', `${config.ellipticAD.rx}`)
.attr('ry', `${config.ellipticAD.ry}`)
.attr('transform-origin', 'center')
.attr('transform', `rotate(${config.ellipticAD.rotate})`)
.attr('fill', config.ellipticAD.fill)
.attr('fill-opacity', config.ellipticAD.fillOpacity);
rects
.append('ellipse')
.attr('cx', `${config.ellipticNon_AD.cx}`)
.attr('cy', `${config.ellipticNon_AD.cy}`)
.attr('rx', `${config.ellipticNon_AD.rx}`)
.attr('ry', `${config.ellipticNon_AD.ry}`)
.attr('transform-origin', 'center')
.attr('transform', `rotate(${config.ellipticNon_AD.rotate})`)
.attr('fill', config.ellipticNon_AD.fill)
.attr('fill-opacity', config.ellipticNon_AD.fillOpacity);
}
// point
export const setPoint = (config, data) => {
return config.svg
.append("g")
.selectAll("path")
.data(data)
.join("path")
.attr("class", "dot")
.attr("opacity", 1)
.attr("stroke-opacity", 1)
.attr("stroke-width", 2)
.attr("stroke", (d) => config.color(d.name))
.attr("fill", 'transparent')
.attr("fill-opacity", 1)
.attr("d", (d) => setSymbol(d.name))
.attr("transform", d => `translate(${config.x(d.PC1)}, ${config.y(d.PC2)})`)
}
// title
export const setLabel = (config) => {
config.svg.append('text')
.attr('x', config.width / 2)
.attr('y', config.marginTop - 50)
.attr('fill', config.titleConfig.color)
.attr('font-size', `${config.titleConfig.fontSize}px`)
.attr('font-family', `${config.titleConfig.fontFamily}`)
.attr('font-style', `${config.titleConfig.fontStyle}`)
.attr('font-weight', `${config.titleConfig.fontWeight}`)
.style('text-anchor', 'middle')
.text(`${config.titleConfig.label}`);
};
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { chartMockData } from './mock.js'; import { chartMockData } from './mock.js';
import * as d3 from 'https://cdn.jsdelivr.net/npm/d3@7/+esm'; import * as d3 from 'https://cdn.jsdelivr.net/npm/d3@7/+esm';
import { setTips, setMaxMinNumber, setLegend, setScale, setAxis, setRect, setTopLeftColor, getRootConfig, setTree } from './utils'; import { setTips, setColor, setLegend, setScale, setAxis, setPoint, setLabel, setElliptic } from './utils';
const chartRef = ref(null); const chartRef = ref(null);
...@@ -19,21 +19,17 @@ const options = ref({}); ...@@ -19,21 +19,17 @@ const options = ref({});
const generateChart = (data) => { const generateChart = (data) => {
const ret = options.value; const ret = options.value;
let width = 1000; let width = 700;
let height = 700; let height = 800;
let marginTop = 120; let marginTop = 120;
let marginRight = 120; let marginRight = 120;
let marginBottom = 60; let marginBottom = 120;
let marginLeft = 120; let marginLeft = 120;
let dendWidth = 100; let legendData = ['AD', 'Non_AD'];
let legendData = [0.8, 0.85, 0.9, 0.95, 1]; let legendPosition = [0.5, 1];
let colorList = [ let colors = ['#155C9C', '#44A79D'];
['#3DD378', '#EC0B88', '#444444', '#5E35B1', '#F9791E', '#C6C6C6', '#F7DC05', '#3D98D3'],
['#017A4A', '#FFCE4E', '#3D98D3', '#FF363C', '#794924', '#8CDB5E', '#D6D6D6', '#FB8C00']
];
let legendPosition = [0, 0];
let heatWidth = width - marginLeft - marginRight;
// 标题配置
const titleConfig = { const titleConfig = {
label: !!ret.title ? ret.title : null, label: !!ret.title ? ret.title : null,
color: ret.title_font_color || '#333333', color: ret.title_font_color || '#333333',
...@@ -43,6 +39,32 @@ const generateChart = (data) => { ...@@ -43,6 +39,32 @@ const generateChart = (data) => {
fontWeight: ret.title_font_weight ? 'bold' : 'normal' fontWeight: ret.title_font_weight ? 'bold' : 'normal'
}; };
// 大椭圆
let ellipticOne = {
rx: (width - marginLeft - marginRight) / 2 - 20,
ry: (height - marginTop - marginBottom) / 2 - 20,
cx: (marginLeft + width - marginLeft - marginRight) / 2 + 60,
cy: (marginTop + height - marginTop - marginBottom) / 2 + 60
};
// 椭圆 AD
let ellipticAD = {
rx: 80,
ry: 350,
cx: (marginLeft + width - marginLeft - marginRight) / 2 + 60,
cy: (marginTop + height - marginTop - marginBottom) / 2 + 60,
rotate: 10
};
// 椭圆 Non_AD
let ellipticNon_AD = {
rx: 350,
ry: 60,
cx: (marginLeft + width - marginLeft - marginRight) / 2 + 60,
cy: (marginTop + height - marginTop - marginBottom) / 2 + 20,
rotate: 20
};
const svg = d3.create('svg').attr('width', width).attr('height', height); const svg = d3.create('svg').attr('width', width).attr('height', height);
let configObj = { let configObj = {
...@@ -53,12 +75,13 @@ const generateChart = (data) => { ...@@ -53,12 +75,13 @@ const generateChart = (data) => {
marginRight, marginRight,
marginBottom, marginBottom,
marginLeft, marginLeft,
dendWidth,
titleConfig, titleConfig,
colors,
legendData, legendData,
colorList, legendPosition,
heatWidth, ellipticOne,
legendPosition ellipticAD,
ellipticNon_AD
}; };
setChart(configObj, data); setChart(configObj, data);
...@@ -68,45 +91,33 @@ const generateChart = (data) => { ...@@ -68,45 +91,33 @@ const generateChart = (data) => {
}; };
const setChart = (config, data) => { const setChart = (config, data) => {
const categories = data.map((d) => d.index); const color = setColor(config);
const newArr = data.map((d) => Object.keys(d).map((e) => d[e])).map((item) => ({ index: item[0], values: item.slice(1) })); const { x, y } = setScale(config, data);
const { maxNumber, minNumber } = setMaxMinNumber(data);
const color = d3.scaleSequential([minNumber, maxNumber], d3.interpolateMagma);
let configObj = { let configObj = {
...config, ...config,
categories, color,
color
};
const { x, y } = setScale(config, categories);
const { root } = getRootConfig(config, newArr);
configObj = {
...configObj,
root,
x, x,
y y
}; };
// elliptic
setElliptic(configObj);
// rects // rects
const rects = setRect(configObj, data); const points = setPoint(configObj, data);
// tips // tips
setTips(rects); setTips(points);
// axis // axis
setAxis(configObj); setAxis(configObj);
// topLeftColor
setTopLeftColor(configObj, data);
// legend // legend
setLegend(configObj); setLegend(configObj);
// tree // label
setTree(configObj); setLabel(configObj);
}; };
onMounted(() => { onMounted(() => {
...@@ -119,4 +130,8 @@ onMounted(() => { ...@@ -119,4 +130,8 @@ onMounted(() => {
.chart-box { .chart-box {
position: relative; position: relative;
} }
.main {
width: 500px;
overflow: hidden;
}
</style> </style>
export const chartMockData = [ export const chartMockData = [
{ {
"index": "AD1", "name": "AD",
"AD1": "1", "x": -13.6199,
"AD2": "0.84", "y": 19.2571
"AD3": "0.83",
"AD4": "0.9",
"Non_AD_5": "0.95",
"Non_AD_6": "0.81",
"Non_AD_7": "0.98",
"Non_AD_8": "1.0",
}, },
{ {
"index": "AD2", "name": "AD",
"AD1": "0.8", "x": -26.9176,
"AD2": "0.9", "y": -83.0864
"AD3": "0.85",
"AD4": "0.95",
"Non_AD_5": "1",
"Non_AD_6": "0.92",
"Non_AD_7": "0.91",
"Non_AD_8": "0.81",
}, },
{ {
"index": "AD3", "name": "AD",
"AD1": "1", "x": 10.7576,
"AD2": "0.99", "y": 16.4817
"AD3": "0.91",
"AD4": "0.98",
"Non_AD_5": "0.95",
"Non_AD_6": "0.93",
"Non_AD_7": "0.94",
"Non_AD_8": "1.0",
}, },
{ {
"index": "AD4", "name": "AD",
"AD1": "0.99", "x": -26.8031,
"AD2": "0.81", "y": 10.3311
"AD3": "0.82",
"AD4": "0.93",
"Non_AD_5": "0.99",
"Non_AD_6": "0.98",
"Non_AD_7": "0.91",
"Non_AD_8": "0.97",
}, },
{ {
"index": "Non_AD_5", "name": "Non_AD",
"AD1": "0.99", "x": -31.6168,
"AD2": "0.98", "y": 11.5461
"AD3": "0.97",
"AD4": "0.95",
"Non_AD_5": "0.91",
"Non_AD_6": "0.99",
"Non_AD_7": "0.98",
"Non_AD_8": "0.95",
}, },
{ {
"index": "Non_AD_6", "name": "Non_AD",
"AD1": "0.98", "x": 122.4475,
"AD2": "0.97", "y": -8.1394
"AD3": "0.92",
"AD4": "0.9",
"Non_AD_5": "0.95",
"Non_AD_6": "0.99",
"Non_AD_7": "0.89",
"Non_AD_8": "0.87",
}, },
{ {
"index": "Non_AD_7", "name": "Non_AD",
"AD1": "0.99", "x": -5.0412,
"AD2": "0.98", "y": 19.3962
"AD3": "0.89",
"AD4": "0.91",
"Non_AD_5": "0.92",
"Non_AD_6": "0.89",
"Non_AD_7": "0.92",
"Non_AD_8": "1",
}, },
{ {
"index": "Non_AD_8", "name": "Non_AD",
"AD1": "0.96", "x": -29.2065,
"AD2": "0.99", "y": 14.2135
"AD3": "0.97",
"AD4": "0.94",
"Non_AD_5": "0.94",
"Non_AD_6": "0.89",
"Non_AD_7": "0.91",
"Non_AD_8": "1",
} }
] ]
...@@ -21,7 +21,7 @@ import ClusterHeatmapSecond from '../../components/ClusterHeatmapSecond/index.vu ...@@ -21,7 +21,7 @@ import ClusterHeatmapSecond from '../../components/ClusterHeatmapSecond/index.vu
import CircularHeatmap from '../../components/CircularHeatmap/index.vue'; import CircularHeatmap from '../../components/CircularHeatmap/index.vue';
import Test from '../../components/Test/index.vue'; import Test from '../../components/Test/index.vue';
const chartCurrent = ref(6); const chartCurrent = ref(0);
const chartList = shallowRef([ const chartList = shallowRef([
{ title: '线性拟合图', value: 1, comName: LinearFitting }, { title: '线性拟合图', value: 1, comName: LinearFitting },
......
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论