Files
smart_wasm/spc-vue/src/utils/chartOptions.ts
2026-06-29 09:23:34 +08:00

221 lines
6.2 KiB
TypeScript

import type { EChartsOption } from "echarts";
import type { ControlLimitChart, CpkResult } from "../types/spc";
const chartColors = ["#0f766e", "#b45309", "#991b1b", "#2563eb"];
// 生成从 1 开始的子组序号,用于控制图横轴。
export function sequenceLabels(length: number): string[] {
return Array.from({ length }, (_, index) => String(index + 1));
}
// 生成固定值线,用于 CL、UCL、LCL。
function constantSeries(length: number, value: number): number[] {
return Array.from({ length }, () => value);
}
// 计算直方图 X 轴范围,覆盖分箱、规格限、正态曲线和原始极值,并保留少量边距。
function calculateHistogramXAxisRange(cpk: CpkResult): { min: number; max: number } {
const values = [
...cpk.Xk,
...cpk.XkUp,
...cpk.XkDown,
...cpk.NormalDistributionX,
cpk.LSL,
cpk.USL,
cpk.SL,
cpk.ValueMin,
cpk.ValueMax
].filter((value) => Number.isFinite(value));
if (!values.length) {
return { min: 0, max: 1 };
}
const rawMin = Math.min(...values);
const rawMax = Math.max(...values);
const span = rawMax - rawMin;
const padding = span > 0 ? Math.max(span * 0.08, cpk.GroupWidth || 0) : Math.max(Math.abs(rawMin) * 0.08, 0.1);
return {
min: rawMin - padding,
max: rawMax + padding
};
}
// 找出越过控制限的点,作为红色散点叠加在控制图上。
function outOfLimitPoints(values: readonly number[], upper: number, lower: number): Array<[number, number]> {
return values
.map((value, index) => ({ value, index }))
.filter((point) => point.value > upper || point.value < lower)
.map((point) => [point.index, point.value]);
}
// 创建单张控制图配置,包含数据线、中心线、上下控制限和越界点。
export function createControlLimitOption(chart: ControlLimitChart): EChartsOption {
const labels = sequenceLabels(chart.values.length);
const outliers = outOfLimitPoints(chart.values, chart.upper, chart.lower);
return {
color: chartColors,
tooltip: { trigger: "axis" },
legend: { top: 0, right: 0 },
grid: { left: 56, right: 28, top: 46, bottom: 44 },
xAxis: {
type: "category",
name: "子组",
data: labels,
boundaryGap: false,
axisLabel: { color: "#667085" }
},
yAxis: {
type: "value",
name: chart.valueName,
scale: true,
axisLabel: { color: "#667085" },
splitLine: { lineStyle: { color: "#e5e7eb" } }
},
series: [
{
name: chart.valueName,
type: "line",
data: [...chart.values],
symbolSize: 6,
lineStyle: { width: 2 }
},
{
name: "CL",
type: "line",
data: constantSeries(chart.values.length, chart.center),
symbol: "none",
lineStyle: { type: "dashed", width: 1.5 }
},
{
name: "UCL",
type: "line",
data: constantSeries(chart.values.length, chart.upper),
symbol: "none",
lineStyle: { type: "dotted", width: 1.5 }
},
{
name: "LCL",
type: "line",
data: constantSeries(chart.values.length, chart.lower),
symbol: "none",
lineStyle: { type: "dotted", width: 1.5 }
},
{
name: "越界点",
type: "scatter",
data: outliers,
symbolSize: 10,
itemStyle: { color: "#dc2626" }
}
]
};
}
// 创建过程能力指标柱状图,便于快速查看 Cp/Cpk/Pp/Ppk 门槛。
export function createCapabilityBarOption(cpk: CpkResult): EChartsOption {
const labels = ["Cp", "Cpk", "Pp", "Ppk", "CPU", "CPL"];
return {
color: ["#0f766e"],
tooltip: { trigger: "axis" },
grid: { left: 48, right: 24, top: 36, bottom: 42 },
xAxis: {
type: "category",
data: labels,
axisLabel: { color: "#667085" }
},
yAxis: {
type: "value",
axisLabel: { color: "#667085" },
splitLine: { lineStyle: { color: "#e5e7eb" } }
},
series: [
{
name: "能力指标",
type: "bar",
data: labels.map((label) => cpk[label as keyof CpkResult] as number),
barMaxWidth: 34,
itemStyle: {
color: (params) => {
const value = Number(params.value);
if (value >= 1.33) return "#15803d";
if (value >= 1) return "#b45309";
return "#b91c1c";
}
},
markLine: {
symbol: "none",
data: [
{ yAxis: 1, name: "1.00" },
{ yAxis: 1.33, name: "1.33" }
],
label: { color: "#667085" },
lineStyle: { color: "#b45309", type: "dashed" }
}
}
]
};
}
// 创建直方图和正态曲线配置,用来核对分布和规格限位置。
export function createHistogramOption(cpk: CpkResult): EChartsOption {
const xRange = calculateHistogramXAxisRange(cpk);
return {
color: ["#0f766e", "#b45309"],
tooltip: { trigger: "axis" },
legend: { top: 0, right: 0 },
grid: { left: 52, right: 48, top: 46, bottom: 42 },
xAxis: {
type: "value",
name: "测量值",
min: xRange.min,
max: xRange.max,
axisLabel: { color: "#667085" }
},
yAxis: [
{
type: "value",
name: "频数",
axisLabel: { color: "#667085" },
splitLine: { lineStyle: { color: "#e5e7eb" } }
},
{
type: "value",
name: "正态曲线",
axisLabel: { color: "#667085" },
splitLine: { show: false }
}
],
series: [
{
name: "频数",
type: "bar",
data: cpk.Xk.map((value, index) => [value, cpk.YkCount[index]]),
barMaxWidth: 28,
markLine: {
symbol: "none",
data: [
{ xAxis: cpk.LSL, name: "LSL" },
{ xAxis: cpk.USL, name: "USL" },
{ xAxis: cpk.SL, name: "SL" }
],
label: { color: "#667085" },
lineStyle: { color: "#991b1b", type: "dashed" }
}
},
{
name: "正态曲线",
type: "line",
yAxisIndex: 1,
smooth: true,
symbolSize: 4,
data: cpk.NormalDistributionX.map((value, index) => [value, cpk.NormalDistributionY[index]])
}
]
};
}