series-calculator.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /**
  2. * 系列数据处理函数
  3. *
  4. * 从 u-charts 核心库搬迁的系列数据处理相关函数
  5. * 用于处理图表系列数据的填充、合并、颜色设置等操作
  6. */
  7. // 类型定义
  8. export interface SeriesItem {
  9. data: (number | null)[] | number[] | { value: number }[];
  10. name: string;
  11. color?: string;
  12. index?: number;
  13. linearIndex?: number;
  14. type?: string;
  15. show?: boolean;
  16. pointShape?: string;
  17. legendShape?: string;
  18. formatter?: (item: any, titleText: string, index: number, opts: ChartOptions) => string;
  19. style?: string;
  20. disableLegend?: boolean;
  21. connectNulls?: boolean;
  22. [key: string]: any;
  23. }
  24. export interface ChartOptions {
  25. type: string;
  26. categories?: string[];
  27. extra?: ChartExtraOptions;
  28. xAxis?: XAxisOptions;
  29. yAxis?: YAxisOptions;
  30. area: number[];
  31. width: number;
  32. height: number;
  33. pix: number;
  34. enableScroll?: boolean;
  35. chartData?: ChartData;
  36. background?: string;
  37. fontColor?: string;
  38. _scrollDistance_?: number;
  39. series?: any[];
  40. tooltip?: any;
  41. dataLabel?: boolean;
  42. title?: any;
  43. subtitle?: any;
  44. dataPointShapeType?: string;
  45. legend?: any;
  46. [key: string]: any;
  47. }
  48. export interface ChartExtraOptions {
  49. bar?: BarOptions;
  50. column?: ColumnOptions;
  51. tooltip?: TooltipOptions;
  52. mount?: MountOptions;
  53. [key: string]: any;
  54. }
  55. export interface BarOptions {
  56. type?: string;
  57. [key: string]: any;
  58. }
  59. export interface ColumnOptions {
  60. type?: 'group' | 'stack' | 'meter';
  61. width?: number;
  62. meterBorder?: number;
  63. meterFillColor?: string;
  64. barBorderCircle?: boolean;
  65. barBorderRadius?: number[];
  66. seriesGap?: number;
  67. linearType?: string;
  68. linearOpacity?: number;
  69. customColor?: string[];
  70. colorStop?: number;
  71. labelPosition?: string;
  72. borderWidth?: number;
  73. widthRatio?: number;
  74. [key: string]: any;
  75. }
  76. export interface TooltipOptions {
  77. legendShape?: string;
  78. [key: string]: any;
  79. }
  80. export interface MountOptions {
  81. widthRatio?: number;
  82. type?: 'bar' | 'triangle' | 'mount' | 'sharp';
  83. [key: string]: any;
  84. }
  85. export interface XAxisOptions {
  86. lineHeight?: number;
  87. marginTop?: number;
  88. fontSize?: number;
  89. disabled?: boolean;
  90. rotateLabel?: boolean;
  91. rotateAngle?: number;
  92. scrollShow?: boolean;
  93. boundaryGap?: string;
  94. itemCount?: number;
  95. splitNumber?: number;
  96. formatter?: (item: any, index: number, opts: ChartOptions) => string;
  97. [key: string]: any;
  98. }
  99. export interface YAxisOptions {
  100. fontSize?: number;
  101. disabled?: boolean;
  102. data?: YAxisDataItem[];
  103. formatter?: (val: number, index: number, opts: ChartOptions) => string;
  104. tofix?: number;
  105. unit?: string;
  106. min?: number;
  107. max?: number;
  108. [key: string]: any;
  109. }
  110. export interface YAxisDataItem {
  111. type?: string;
  112. disabled?: boolean;
  113. formatter?: (val: any, index: number, opts: ChartOptions) => string;
  114. categories?: string[];
  115. tofix?: number;
  116. unit?: string;
  117. min?: number;
  118. max?: number;
  119. position?: string;
  120. calibration?: boolean;
  121. fontSize?: number;
  122. textAlign?: string;
  123. axisLineColor?: string;
  124. fontColor?: string;
  125. axisLine?: boolean;
  126. titleFontSize?: number;
  127. title?: string;
  128. titleFontColor?: string;
  129. titleOffsetX?: number;
  130. titleOffsetY?: number;
  131. }
  132. export interface ChartData {
  133. calPoints?: any[][];
  134. xAxisPoints?: number[];
  135. eachSpacing?: number;
  136. [key: string]: any;
  137. }
  138. export interface UChartsConfig {
  139. version: string;
  140. color: string[];
  141. linearColor: string[];
  142. yAxisWidth: number;
  143. xAxisHeight: number;
  144. padding: number[];
  145. rotate: boolean;
  146. fontSize: number;
  147. fontColor: string;
  148. dataPointShape: string[];
  149. pieChartLinePadding: number;
  150. pieChartTextPadding: number;
  151. titleFontSize: number;
  152. subtitleFontSize: number;
  153. radarLabelTextMargin: number;
  154. toolTipBackground?: string;
  155. toolTipOpacity?: number;
  156. _pieTextMaxLength_?: number;
  157. _xAxisTextAngle_?: number;
  158. }
  159. export interface DataRange {
  160. minRange: number;
  161. maxRange: number;
  162. }
  163. /**
  164. * 修复饼图系列数据
  165. * 处理饼图的百分比和累计值
  166. *
  167. * @param series - 系列数据数组
  168. * @param opts - 图表配置选项
  169. * @param config - uCharts配置对象
  170. * @returns 修复后的系列数据数组
  171. */
  172. export function fixPieSeries(
  173. series: SeriesItem[],
  174. opts: ChartOptions,
  175. config: UChartsConfig
  176. ): SeriesItem[] {
  177. const pieSeriesArr: SeriesItem[] = [];
  178. if (series.length > 0 && series[0].data.constructor.toString().indexOf('Array') > -1) {
  179. (opts as any)._pieSeries_ = series;
  180. const oldseries = series[0].data as any[];
  181. for (let i = 0; i < oldseries.length; i++) {
  182. oldseries[i].formatter = series[0].formatter;
  183. oldseries[i].data = oldseries[i].value;
  184. pieSeriesArr.push(oldseries[i]);
  185. }
  186. opts.series = pieSeriesArr;
  187. } else {
  188. return series;
  189. }
  190. return pieSeriesArr;
  191. }
  192. /**
  193. * 填充系列数据
  194. * 确保所有系列数据都有完整的属性配置
  195. *
  196. * @param series - 系列数据数组
  197. * @param opts - 图表配置选项
  198. * @param config - uCharts配置对象
  199. * @returns 填充后的系列数据数组
  200. */
  201. export function fillSeries(
  202. series: SeriesItem[],
  203. opts: ChartOptions,
  204. config: UChartsConfig
  205. ): SeriesItem[] {
  206. let index = 0;
  207. for (let i = 0; i < series.length; i++) {
  208. const item = series[i];
  209. if (!item.color) {
  210. item.color = config.color[index];
  211. index = (index + 1) % config.color.length;
  212. }
  213. if (!item.linearIndex) {
  214. item.linearIndex = i;
  215. }
  216. if (!item.index) {
  217. item.index = 0;
  218. }
  219. if (!item.type) {
  220. item.type = opts.type;
  221. }
  222. if (typeof item.show === "undefined") {
  223. item.show = true;
  224. }
  225. if (!item.type) {
  226. item.type = opts.type;
  227. }
  228. if (!item.pointShape) {
  229. item.pointShape = "circle";
  230. }
  231. if (!item.legendShape) {
  232. switch (item.type) {
  233. case 'line':
  234. item.legendShape = "line";
  235. break;
  236. case 'column':
  237. case 'bar':
  238. item.legendShape = "rect";
  239. break;
  240. case 'area':
  241. case 'mount':
  242. item.legendShape = "triangle";
  243. break;
  244. default:
  245. item.legendShape = "circle";
  246. }
  247. }
  248. }
  249. return series;
  250. }
  251. /**
  252. * 填充自定义颜色
  253. * 为系列数据设置自定义颜色或使用默认渐变色
  254. *
  255. * @param linearType - 线性类型 ('custom' 或其他)
  256. * @param customColor - 自定义颜色数组
  257. * @param series - 系列数据数组
  258. * @param config - uCharts配置对象
  259. * @returns 颜色数组
  260. */
  261. export function fillCustomColor(
  262. linearType: string,
  263. customColor: string[],
  264. series: SeriesItem[],
  265. config: UChartsConfig
  266. ): string[] {
  267. let newcolor = customColor || [];
  268. if (linearType == 'custom' && newcolor.length == 0) {
  269. newcolor = config.linearColor;
  270. }
  271. if (linearType == 'custom' && newcolor.length < series.length) {
  272. const chazhi = series.length - newcolor.length;
  273. for (let i = 0; i < chazhi; i++) {
  274. newcolor.push(config.linearColor[(i + 1) % config.linearColor.length]);
  275. }
  276. }
  277. return newcolor;
  278. }
  279. /**
  280. * 获取数据范围
  281. * 计算最小值和最大值的范围
  282. *
  283. * @param minData - 最小数据值
  284. * @param maxData - 最大数据值
  285. * @returns 数据范围对象
  286. */
  287. export function getDataRange(minData: number, maxData: number): DataRange {
  288. let limit = 0;
  289. const range = maxData - minData;
  290. if (range >= 10000) {
  291. limit = 1000;
  292. } else if (range >= 1000) {
  293. limit = 100;
  294. } else if (range >= 100) {
  295. limit = 10;
  296. } else if (range >= 10) {
  297. limit = 5;
  298. } else if (range >= 1) {
  299. limit = 1;
  300. } else if (range >= 0.1) {
  301. limit = 0.1;
  302. } else if (range >= 0.01) {
  303. limit = 0.01;
  304. } else if (range >= 0.001) {
  305. limit = 0.001;
  306. } else if (range >= 0.0001) {
  307. limit = 0.0001;
  308. } else if (range >= 0.00001) {
  309. limit = 0.00001;
  310. } else {
  311. limit = 0.000001;
  312. }
  313. return {
  314. minRange: findRange(minData, 'lower', limit),
  315. maxRange: findRange(maxData, 'upper', limit)
  316. };
  317. }
  318. /**
  319. * 查找范围边界
  320. * 根据类型和限制查找合适的边界值
  321. *
  322. * @param num - 数值
  323. * @param type - 类型 ('upper' 或 'lower')
  324. * @param limit - 限制值
  325. * @returns 边界值
  326. */
  327. function findRange(num: number, type: 'upper' | 'lower', limit: number): number {
  328. if (isNaN(num)) {
  329. throw new Error('[uCharts] series数据需为Number格式');
  330. }
  331. limit = limit || 10;
  332. type = type ? type : 'upper';
  333. let multiple = 1;
  334. while (limit < 1) {
  335. limit *= 10;
  336. multiple *= 10;
  337. }
  338. if (type === 'upper') {
  339. num = Math.ceil(num * multiple);
  340. } else {
  341. num = Math.floor(num * multiple);
  342. }
  343. while (num % limit !== 0) {
  344. if (type === 'upper') {
  345. if (num == num + 1) {
  346. break;
  347. }
  348. num++;
  349. } else {
  350. num--;
  351. }
  352. }
  353. return num / multiple;
  354. }
  355. /**
  356. * 数据合并
  357. * 合并多个系列的数据
  358. *
  359. * @param series - 系列数据数组
  360. * @returns 合并后的数据数组
  361. */
  362. export function dataCombine(series: SeriesItem[]): (number | null)[] {
  363. return series.reduce(function(a, b) {
  364. return (a.data ? a.data : a).concat(b.data);
  365. }, [] as any);
  366. }
  367. /**
  368. * 堆叠数据合并
  369. * 处理堆叠图表的数据合并
  370. *
  371. * @param series - 系列数据数组
  372. * @param len - 数据长度
  373. * @returns 合并后的数据数组
  374. */
  375. export function dataCombineStack(
  376. series: SeriesItem[],
  377. len: number
  378. ): (number | null)[] {
  379. const sum = new Array(len);
  380. for (let j = 0; j < sum.length; j++) {
  381. sum[j] = 0;
  382. }
  383. for (let i = 0; i < series.length; i++) {
  384. for (let j = 0; j < sum.length; j++) {
  385. sum[j] += (series[i].data as number[])[j];
  386. }
  387. }
  388. return series.reduce(function(a, b) {
  389. return (a.data ? a.data : a).concat(b.data).concat(sum);
  390. }, [] as any);
  391. }