rapid-spreadjs 1.0.104 → 1.0.106

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -8835,6 +8835,518 @@ const EChartsUtilsAll = {
8835
8835
  };
8836
8836
  return option;
8837
8837
  },
8838
+ /**
8839
+ * 土工合成材料(有效孔径)
8840
+ * @param config 折线配置
8841
+ * @param xDataArr x轴原始数据(二维数组)
8842
+ * @param yDataArr y轴原始数据(二维数组)
8843
+ * @returns 返回ECharts配置项
8844
+ */
8845
+ chart470: (config, xDataArr, yDataArr, sheet) => {
8846
+ let lineData = JSON.parse(config.chartLinesJson);
8847
+ const chartExtJson = config.chartExtJson == null || config.chartExtJson == undefined ? null : JSON.parse(config.chartExtJson);
8848
+ let title = config.chartTitle, xName = config.chartXName, yName = config.chartYName, color = lineData[0].lineColor;
8849
+ // 原始数据
8850
+ const xData = xDataArr[0];
8851
+ const yData = yDataArr[0];
8852
+ // 线性内插法函数:根据y值计算对应的x值
8853
+ function interpolateX(targetY, xData, yData) {
8854
+ // 查找目标y值所在区间
8855
+ for (let i = 0; i < yData.length - 1; i++) {
8856
+ if ((yData[i] >= targetY && yData[i + 1] <= targetY) || (yData[i] <= targetY && yData[i + 1] >= targetY)) {
8857
+ // 线性插值公式: x = x1 + (x2 - x1) * (targetY - y1) / (y2 - y1)
8858
+ const x1 = xData[i];
8859
+ const x2 = xData[i + 1];
8860
+ const y1 = yData[i];
8861
+ const y2 = yData[i + 1];
8862
+ return x1 + ((x2 - x1) * (targetY - y1)) / (y2 - y1);
8863
+ }
8864
+ }
8865
+ return null; // 如果目标y值不在数据范围内
8866
+ }
8867
+ // 计算y=5和y=10对应的x值
8868
+ const xAtY5 = interpolateX(5, xData, yData);
8869
+ const xAtY10 = interpolateX(10, xData, yData);
8870
+ // console.log('y=5时,x=' + (xAtY5 ? xAtY5 : ''));
8871
+ // console.log('y=10时,x=' + (xAtY10 ? xAtY10 : ''));
8872
+ let yValIsAllNull = false;
8873
+ if (xData.length == 0 ||
8874
+ yData.length == 0 ||
8875
+ (!xData.some((x) => x != '' && x != '/' && x != null && x != undefined) &&
8876
+ !yData.some((x) => x != '' && x != '/' && x != null && x != undefined))) {
8877
+ yValIsAllNull = true;
8878
+ }
8879
+ if (xData.filter((x) => x == 0).length == xData.length || yData.filter((x) => x == 0).length == yData.length) {
8880
+ yValIsAllNull = true;
8881
+ }
8882
+ // 输出x轴内插值
8883
+ if (chartExtJson != null) {
8884
+ if (chartExtJson.tghcclYxkj5 != null && chartExtJson.tghcclYxkj5 != undefined) {
8885
+ sheet.setValue(chartExtJson.tghcclYxkj5.row, chartExtJson.tghcclYxkj5.col, xAtY5 ? EChartsUtilsComm.getRound(xAtY5, 0.001) : '/');
8886
+ }
8887
+ if (chartExtJson.tghcclYxkj10 != null && chartExtJson.tghcclYxkj10 != undefined) {
8888
+ sheet.setValue(chartExtJson.tghcclYxkj10.row, chartExtJson.tghcclYxkj10.col, xAtY10 ? EChartsUtilsComm.getRound(xAtY10, 0.001) : '/');
8889
+ }
8890
+ }
8891
+ // 配置图表选项
8892
+ const option = {
8893
+ title: [
8894
+ {
8895
+ show: true,
8896
+ text: title,
8897
+ left: 'center',
8898
+ top: 2,
8899
+ textStyle: {
8900
+ fontSize: 14,
8901
+ fontWeight: 'normal',
8902
+ },
8903
+ },
8904
+ {
8905
+ show: true,
8906
+ text: xName,
8907
+ left: 'center',
8908
+ bottom: 0,
8909
+ textStyle: {
8910
+ fontSize: 12,
8911
+ fontWeight: 'normal',
8912
+ },
8913
+ },
8914
+ ],
8915
+ tooltip: {
8916
+ trigger: 'axis',
8917
+ formatter: function (params) {
8918
+ return `标准颗粒直径: ${params[0].value[0].toFixed(3)} mm<br>过筛率: ${params[0].value[1].toFixed(2)} %`;
8919
+ },
8920
+ },
8921
+ grid: {
8922
+ top: 25,
8923
+ left: 25,
8924
+ right: 15,
8925
+ bottom: 20,
8926
+ containLabel: true,
8927
+ },
8928
+ xAxis: {
8929
+ name: xName,
8930
+ nameLocation: 'end',
8931
+ nameGap: 20,
8932
+ nameTextStyle: {
8933
+ fontSize: 12,
8934
+ },
8935
+ type: 'log', // 设置为对数坐标轴
8936
+ axisLine: {
8937
+ lineStyle: {
8938
+ color: '#333',
8939
+ },
8940
+ },
8941
+ min: 0.01, // 设置最小值为0.01
8942
+ max: 1, // 设置最大值为1
8943
+ axisLabel: {
8944
+ formatter: function (value) {
8945
+ return value.toFixed(2); // 格式化x轴标签显示
8946
+ },
8947
+ },
8948
+ // 增加纵向网格线
8949
+ splitLine: {
8950
+ show: true,
8951
+ lineStyle: {
8952
+ color: '#f0f0f0',
8953
+ width: 1,
8954
+ type: 'solid',
8955
+ },
8956
+ },
8957
+ // 设置更多的刻度点
8958
+ minorTick: {
8959
+ show: true,
8960
+ },
8961
+ minorSplitLine: {
8962
+ show: true,
8963
+ lineStyle: {
8964
+ color: '#f0f0f0',
8965
+ width: 1,
8966
+ type: 'solid',
8967
+ },
8968
+ },
8969
+ },
8970
+ yAxis: {
8971
+ type: 'value', // 普通坐标轴
8972
+ name: yName,
8973
+ nameLocation: 'middle', // 名称居中显示
8974
+ nameGap: 25, // 名称与轴线的距离
8975
+ nameTextStyle: {
8976
+ fontSize: 12,
8977
+ },
8978
+ axisLine: {
8979
+ lineStyle: {
8980
+ color: '#333',
8981
+ },
8982
+ },
8983
+ min: 0,
8984
+ max: 25,
8985
+ // 保持横向网格线
8986
+ splitLine: {
8987
+ show: true,
8988
+ lineStyle: {
8989
+ color: '#f0f0f0',
8990
+ width: 1,
8991
+ },
8992
+ },
8993
+ },
8994
+ series: yValIsAllNull
8995
+ ? []
8996
+ : [
8997
+ {
8998
+ name: '孔径分布',
8999
+ type: 'line',
9000
+ data: xData.map((x, i) => [x, yData[i]]), // 组合x和y数据
9001
+ smooth: true, // 平滑曲线
9002
+ lineStyle: {
9003
+ width: 2,
9004
+ type: 'solid', // 实线
9005
+ },
9006
+ // 设置数据点样式为实心点
9007
+ showSymbol: true, // 确保显示数据点
9008
+ symbol: 'circle', // 实心圆点
9009
+ symbolSize: 8, // 点的大小
9010
+ itemStyle: {
9011
+ color: color, // 数据点颜色
9012
+ borderColor: '#fff', // 数据点边框颜色
9013
+ borderWidth: 1, // 数据点边框宽度
9014
+ },
9015
+ markPoint: {
9016
+ symbol: 'circle', // 使用简单的圆点作为标记
9017
+ symbolSize: 1, // 标记点大小
9018
+ data: [
9019
+ {
9020
+ name: 'y=5',
9021
+ coord: [xAtY5, 5], // 标记点坐标
9022
+ value: 5,
9023
+ itemStyle: {
9024
+ color: 'red', // 红色标记点
9025
+ },
9026
+ label: {
9027
+ show: true,
9028
+ formatter: `(${xAtY5.toFixed(3)},5)`, // 显示(x值,y值)
9029
+ position: 'right',
9030
+ fontSize: 12,
9031
+ },
9032
+ },
9033
+ {
9034
+ name: 'y=10',
9035
+ coord: [xAtY10, 10],
9036
+ value: 10,
9037
+ itemStyle: {
9038
+ color: 'red', // 红色标记点
9039
+ },
9040
+ label: {
9041
+ show: true,
9042
+ formatter: `(${xAtY10.toFixed(3)},10)`, // 显示(x值,y值)
9043
+ position: 'right',
9044
+ fontSize: 12,
9045
+ },
9046
+ },
9047
+ ],
9048
+ },
9049
+ markLine: {
9050
+ symbol: 'none', // 去掉连线箭头
9051
+ data: [
9052
+ // y=5的水平虚线(从y轴到点)
9053
+ [
9054
+ {
9055
+ coord: [0.01, 5], // 起点:x轴最小值,y=5
9056
+ lineStyle: {
9057
+ type: 'dashed',
9058
+ color: 'red',
9059
+ width: 1,
9060
+ },
9061
+ },
9062
+ {
9063
+ coord: [xAtY5, 5], // 终点:标记点的x坐标,y=5
9064
+ lineStyle: {
9065
+ type: 'dashed',
9066
+ color: 'red',
9067
+ width: 1,
9068
+ },
9069
+ },
9070
+ ],
9071
+ // y=5的垂直虚线(从x轴到点)
9072
+ [
9073
+ {
9074
+ coord: [xAtY5, 0], // 起点:标记点的x坐标,y轴最小值
9075
+ lineStyle: {
9076
+ type: 'dashed',
9077
+ color: 'red',
9078
+ width: 1,
9079
+ },
9080
+ },
9081
+ {
9082
+ coord: [xAtY5, 5], // 终点:标记点的坐标
9083
+ lineStyle: {
9084
+ type: 'dashed',
9085
+ color: 'red',
9086
+ width: 1,
9087
+ },
9088
+ },
9089
+ ],
9090
+ // y=10的水平虚线(从y轴到点)
9091
+ [
9092
+ {
9093
+ coord: [0.01, 10], // 起点:x轴最小值,y=10
9094
+ lineStyle: {
9095
+ type: 'dashed',
9096
+ color: 'red',
9097
+ width: 1,
9098
+ },
9099
+ },
9100
+ {
9101
+ coord: [xAtY10, 10], // 终点:标记点的x坐标,y=10
9102
+ lineStyle: {
9103
+ type: 'dashed',
9104
+ color: 'red',
9105
+ width: 1,
9106
+ },
9107
+ },
9108
+ ],
9109
+ // y=10的垂直虚线(从x轴到点)
9110
+ [
9111
+ {
9112
+ coord: [xAtY10, 0], // 起点:标记点的x坐标,y轴最小值
9113
+ lineStyle: {
9114
+ type: 'dashed',
9115
+ color: 'red',
9116
+ width: 1,
9117
+ },
9118
+ },
9119
+ {
9120
+ coord: [xAtY10, 10], // 终点:标记点的坐标
9121
+ lineStyle: {
9122
+ type: 'dashed',
9123
+ color: 'red',
9124
+ width: 1,
9125
+ },
9126
+ },
9127
+ ],
9128
+ ],
9129
+ },
9130
+ },
9131
+ ],
9132
+ };
9133
+ return option;
9134
+ },
9135
+ /**
9136
+ * 温度-针入度曲线
9137
+ * @param config 折线配置
9138
+ * @param xDataArr x轴原始数据(二维数组)
9139
+ * @param yDataArr y轴原始数据(二维数组)
9140
+ * @returns 返回ECharts配置项
9141
+ */
9142
+ chart490: (config, xDataArr, yDataArr, sheet) => {
9143
+ let lineData = JSON.parse(config.chartLinesJson);
9144
+ const chartExtJson = config.chartExtJson == null || config.chartExtJson == undefined ? null : JSON.parse(config.chartExtJson);
9145
+ let title = config.chartTitle, xName = config.chartXName, yName = config.chartYName, color = lineData[0].lineColor;
9146
+ // 原始数据
9147
+ const xData = xDataArr[0];
9148
+ const yData = yDataArr[0];
9149
+ // 手动计算线性回归参数
9150
+ function linearRegression(x, y) {
9151
+ const n = x.length;
9152
+ const xMean = x.reduce((sum, val) => sum + val, 0) / n;
9153
+ const yMean = y.reduce((sum, val) => sum + val, 0) / n;
9154
+ let numerator = 0;
9155
+ let denominator = 0;
9156
+ for (let i = 0; i < n; i++) {
9157
+ numerator += (x[i] - xMean) * (y[i] - yMean);
9158
+ denominator += (x[i] - xMean) * (x[i] - xMean);
9159
+ }
9160
+ const slope = numerator / denominator;
9161
+ const intercept = yMean - slope * xMean;
9162
+ return { slope, intercept };
9163
+ }
9164
+ function calculateRSquared(x, y, slope, intercept) {
9165
+ const n = x.length;
9166
+ const yMean = y.reduce((sum, val) => sum + val, 0) / n;
9167
+ let totalSumOfSquares = 0;
9168
+ let residualSumOfSquares = 0;
9169
+ for (let i = 0; i < n; i++) {
9170
+ totalSumOfSquares += Math.pow(y[i] - yMean, 2);
9171
+ const yPredicted = slope * x[i] + intercept;
9172
+ residualSumOfSquares += Math.pow(y[i] - yPredicted, 2);
9173
+ }
9174
+ return 1 - residualSumOfSquares / totalSumOfSquares;
9175
+ }
9176
+ const logYData = yData.map((y) => Math.log10(y));
9177
+ const regressionParams = linearRegression(xData, logYData);
9178
+ const rSquared = calculateRSquared(xData, logYData, regressionParams.slope, regressionParams.intercept);
9179
+ // document.getElementById('formulaDisplay').innerHTML = `公式: log<sub>10</sub>(y) = ${regressionParams.slope.toFixed(
9180
+ // 4
9181
+ // )}x + ${regressionParams.intercept.toFixed(4)}<br> R² = ${rSquared.toFixed(4)}`;
9182
+ let yValIsAllNull = false;
9183
+ if (xData.length == 0 ||
9184
+ yData.length == 0 ||
9185
+ (!xData.some((x) => x != '' && x != '/' && x != null && x != undefined) &&
9186
+ !yData.some((x) => x != '' && x != '/' && x != null && x != undefined))) {
9187
+ yValIsAllNull = true;
9188
+ }
9189
+ if (xData.filter((x) => x == 0).length == xData.length || yData.filter((x) => x == 0).length == yData.length) {
9190
+ yValIsAllNull = true;
9191
+ }
9192
+ // 1. 定义期望显示网格线的Y轴数值(在对数空间中是均匀的,对应实际空间中的特定值)
9193
+ const customGridLines = yValIsAllNull ? [] : [30, 70, 100, 150, 200];
9194
+ // 2. 为markLine准备数据
9195
+ const markLineData = customGridLines.map((value) => ({
9196
+ yAxis: value, // 标记线的位置(实际值)
9197
+ label: {
9198
+ show: true,
9199
+ formatter: '{c}', // 显示数值
9200
+ },
9201
+ lineStyle: {
9202
+ color: '#e0e0e0',
9203
+ width: 1,
9204
+ type: 'solid',
9205
+ },
9206
+ }));
9207
+ // 输出x轴内插值
9208
+ if (chartExtJson != null) {
9209
+ if (chartExtJson.wdzrdXlA != null && chartExtJson.wdzrdXlA != undefined) {
9210
+ sheet.setValue(chartExtJson.wdzrdXlA.row, chartExtJson.wdzrdXlA.col, regressionParams.slope ? EChartsUtilsComm.getRound(regressionParams.slope, 0.0001) : '/');
9211
+ }
9212
+ if (chartExtJson.wdzrdCsB != null && chartExtJson.wdzrdCsB != undefined) {
9213
+ sheet.setValue(chartExtJson.wdzrdCsB.row, chartExtJson.wdzrdCsB.col, regressionParams.intercept ? EChartsUtilsComm.getRound(regressionParams.intercept, 0.0001) : '/');
9214
+ }
9215
+ if (chartExtJson.wdzrdXsR != null && chartExtJson.wdzrdXsR != undefined) {
9216
+ sheet.setValue(chartExtJson.wdzrdXsR.row, chartExtJson.wdzrdXsR.col, rSquared ? EChartsUtilsComm.getRound(rSquared, 0.0001) : '/');
9217
+ }
9218
+ }
9219
+ const option = {
9220
+ title: [
9221
+ {
9222
+ show: true,
9223
+ text: title,
9224
+ left: 'center',
9225
+ top: 2,
9226
+ textStyle: {
9227
+ fontSize: 14,
9228
+ fontWeight: 'normal',
9229
+ },
9230
+ },
9231
+ {
9232
+ show: true,
9233
+ text: xName,
9234
+ left: 'center',
9235
+ bottom: 0,
9236
+ textStyle: {
9237
+ fontSize: 12,
9238
+ fontWeight: 'normal',
9239
+ },
9240
+ },
9241
+ ],
9242
+ grid: {
9243
+ top: 25,
9244
+ left: yValIsAllNull ? 20 : 40,
9245
+ right: 15,
9246
+ bottom: 20,
9247
+ containLabel: true,
9248
+ },
9249
+ xAxis: {
9250
+ type: 'value',
9251
+ name: xName,
9252
+ min: Math.min(...xData),
9253
+ //max: 35
9254
+ },
9255
+ yAxis: {
9256
+ type: 'log',
9257
+ name: yName,
9258
+ nameLocation: 'middle', // 名称居中显示
9259
+ nameGap: yValIsAllNull ? 5 : 25, // 名称与轴线的距离
9260
+ logBase: 10,
9261
+ min: 10,
9262
+ min: function (value) {
9263
+ // 确保最小值不会太小
9264
+ return Math.max(1, Math.pow(10, Math.floor(Math.log10(value.min))));
9265
+ },
9266
+ max: function (value) {
9267
+ const maxValue = value.max;
9268
+ // 处理特殊情况
9269
+ if (maxValue <= 0)
9270
+ return 100;
9271
+ // 计算对数值
9272
+ const logValue = Math.log10(maxValue);
9273
+ const exponent = Math.floor(logValue);
9274
+ const fraction = logValue - exponent;
9275
+ // 根据小数部分决定如何取整
9276
+ let maxExponent;
9277
+ if (fraction < 0.3) {
9278
+ maxExponent = exponent + 0.3; // 使用5倍关系
9279
+ }
9280
+ else if (fraction < 0.7) {
9281
+ maxExponent = exponent + 1; // 使用10倍关系
9282
+ }
9283
+ else {
9284
+ maxExponent = exponent + 1.5; // 使用30倍关系,为大数据留更多空间
9285
+ }
9286
+ return Math.pow(10, maxExponent);
9287
+ },
9288
+ // 关键步骤:抑制默认的y轴网格线和部分标签,让markLine主导
9289
+ splitLine: {
9290
+ show: false, // 隐藏y轴默认的网格线
9291
+ },
9292
+ axisLabel: {
9293
+ show: false,
9294
+ // 可以控制只显示某些主要刻度,或者也设为false完全自定义
9295
+ // interval: 0,
9296
+ // 或者使用formatter精细控制,这里我们先展示默认标签
9297
+ },
9298
+ zlevel: 1,
9299
+ minorSplitLine: { show: false }, // 隐藏次要网格线
9300
+ },
9301
+ series: [
9302
+ {
9303
+ name: '原始数据',
9304
+ type: 'scatter',
9305
+ data: xData.map((x, i) => [x, yData[i]]),
9306
+ symbolSize: 6,
9307
+ zlevel: 4,
9308
+ itemStyle: { color: color },
9309
+ },
9310
+ {
9311
+ name: '趋势线',
9312
+ type: 'line',
9313
+ data: (() => {
9314
+ const minX = Math.min(...xData);
9315
+ const maxX = Math.max(...xData);
9316
+ const trendlineData = [];
9317
+ for (let x = minX; x <= maxX; x += 0.5) {
9318
+ const logY = regressionParams.slope * x + regressionParams.intercept;
9319
+ const y = Math.pow(10, logY);
9320
+ trendlineData.push([x, y]);
9321
+ }
9322
+ return trendlineData;
9323
+ })(),
9324
+ showSymbol: false,
9325
+ lineStyle: { color: color, width: 1 },
9326
+ // 关键修改:提升趋势线的绘制层级,确保它显示在网格线之上
9327
+ zlevel: 5, // 这个值大于网格线所在的层级即可
9328
+ },
9329
+ {
9330
+ // 关键步骤:添加一个透明的系列,专门用于承载markLine
9331
+ name: '自定义网格线',
9332
+ type: 'line',
9333
+ data: [], // 没有实际数据点
9334
+ markLine: {
9335
+ symbol: 'none', // 线条两端不显示符号
9336
+ lineStyle: { color: '#e0e0e0', width: 1, type: 'solid' },
9337
+ label: {
9338
+ show: true,
9339
+ position: 'start',
9340
+ formatter: '{c}', // 标签显示数值
9341
+ },
9342
+ data: markLineData, // 使用我们自定义的网格线数据
9343
+ },
9344
+ silent: true, // 此系列不响应鼠标事件
9345
+ },
9346
+ ],
9347
+ };
9348
+ return option;
9349
+ },
8838
9350
  };
8839
9351
 
8840
9352
  /**
@@ -9104,6 +9616,12 @@ const EChartsUtils = {
9104
9616
  else if (config.chartType == 450) {
9105
9617
  option = EChartsUtilsAll.chart450(config, xDataArr, yDataArr);
9106
9618
  }
9619
+ else if (config.chartType == 470) {
9620
+ option = EChartsUtilsAll.chart470(config, xDataArr, yDataArr, sheet);
9621
+ }
9622
+ else if (config.chartType == 490) {
9623
+ option = EChartsUtilsAll.chart490(config, xDataArr, yDataArr, sheet);
9624
+ }
9107
9625
  if (option && typeof option === 'object') {
9108
9626
  //如果是隐藏的图表,则需要禁用ECharts的动画效果
9109
9627
  //原因是:如果ECharts使用了动画效果,图表渲染完成可能需要0.5秒,但是在导出ECharts统计图为图片的场景下时,可能在图表还没有渲染完成(动画效果还没结束)前就要获取图像了,此时可能就会造成获取到的图像内容丢失的情况