diff --git a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj index dc5ae40405802b04fdfad6c668bff12b60b6b292..f3d280b8771a98b76e41f5c120d0054251914dff 100644 --- a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj +++ b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj @@ -34,7 +34,7 @@ - + @@ -64,6 +64,7 @@ + diff --git a/src/BootstrapBlazor.Server/Components/Samples/Charts/Bar.razor b/src/BootstrapBlazor.Server/Components/Samples/Charts/Bar.razor index 556df7c7a74751648cb42e69658eaa7a957e4df0..2a24339d3759afddac46b53fb6ddb44d4978ff16 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Charts/Bar.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Charts/Bar.razor @@ -47,3 +47,14 @@ Name="BarAspectRatio"> + + + + + + + diff --git a/src/BootstrapBlazor.Server/Components/Samples/Charts/Bar.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Charts/Bar.razor.cs index 53deddaf90fec3f2cabc220d40c9f16bbb5727c9..aae27d225c9c733f8b57252e7a1ac905140a3665 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Charts/Bar.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/Charts/Bar.razor.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Website: https://www.blazor.zone or https://argozhang.github.io/ +using DocumentFormat.OpenXml.Office2010.Excel; + namespace BootstrapBlazor.Server.Components.Samples.Charts; /// @@ -53,7 +55,6 @@ public partial class Bar { ds.Options.Title = "Bar Histogram"; } - ds.Options.X.Title = "days"; ds.Options.Y.Title = "Numerical value"; ds.Options.X.Stacked = stacked; @@ -177,4 +178,49 @@ public partial class Bar } return Task.FromResult(ds); } + + private Task OnInitShowDataLabel(bool showDataLabel, bool setTitle = true) + { + var ds = new ChartDataSource(); + if (setTitle) + { + ds.Options.Title = "Bar Histogram"; + } + ds.Options.ShowLegend = false; + ds.Options.ShowDataLabel = showDataLabel; + ds.Options.Align = ChartDataLabelPosition.Start; + ds.Options.Anchor = ChartDataLabelPosition.End; + ds.Options.ChartDataLabelColor = "black"; + ds.Options.X.Title = "days"; + ds.Options.Y.Title = "Numerical value"; + ds.Labels = Enumerable.Range(1, BarDataCount).Select(i => i.ToString()); + ds.Data.Add(new ChartDataset() + { + Label = $"Set {0}", + Data = Enumerable.Range(1, BarDataCount).Select(i => Randomer.Next(20, 37)).Cast() + }); + return Task.FromResult(ds); + } + + private Task OnInitBarColorSeparately(bool barColorSeparately, bool setTitle = true) + { + var ds = new ChartDataSource(); + if (setTitle) + { + ds.Options.Title = "Bar Histogram"; + } + ds.Options.ShowLegend = false; + ds.Options.BarColorSeparately = barColorSeparately; + ds.Options.X.Title = "days"; + ds.Options.Y.Title = "Numerical value"; + ds.Labels = Enumerable.Range(1, BarDataCount).Select(i => i.ToString()); + ds.Data.Add(new ChartDataset() + { + Label = $"Set {0}", + Data = Enumerable.Range(1, BarDataCount).Select(i => Randomer.Next(20, 37)).Cast(), + BackgroundColor = ["rgb(54, 162, 235, 0.5)", "rgb(75, 192, 192, 0.5)", "rgb(255, 99, 132, 0.5)", "rgb(255, 159, 64, 0.5)", "rgb(255, 205, 86, 0.5)", "rgb(255, 99, 71, 0.5)", "rgb(255, 192, 203, 0.5)"], + BorderColor = ["rgb(54, 162, 235)", "rgb(75, 192, 192)", "rgb(255, 99, 132)", "rgb(255, 159, 64)", "rgb(255, 205, 86)", "rgb(255, 99, 71)", "rgb(255, 192, 203)"] + }); + return Task.FromResult(ds); + } } diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index f92839a8767686c18f4356690f3a17163b8f6676..e0ad46b09de8ad0310f3074af71ff3288b5bcb49 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -566,7 +566,11 @@ "BarTwoYAxesTitle": "Double Y axis", "BarTwoYAxesIntro": "Show secondary Y axis: alignment, title and data group", "BarAspectRatioTitle": "Chart ratio", - "BarAspectRatioIntro": "Setting the height and width will automatically disable the constraint chart ratio, and the chart will fill the container" + "BarAspectRatioIntro": "Setting the height and width will automatically disable the constraint chart ratio, and the chart will fill the container", + "BarShowDataLabelTitle": "Display chart data values", + "BarShowDataLabelIntro": "By setting ShowDataLabel property,control whether to display chart data values", + "BarColorSeparatelyTitle": "Set bar chart colors separately", + "BarColorSeparatelyIntro": "By setting BarColorSeparately property,control whether to Set bar chart colors separately,and By setting BackgroundColor property, set bar chart color" }, "BootstrapBlazor.Server.Components.Samples.Transitions": { "TransitionsTitle": "Transition effect", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index fc370a707f44ef8ed5fdd9ffc59d2d6e682fcdc0..e242f50b56f148683f15039fed283a62f0e73f92 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -566,7 +566,11 @@ "BarTwoYAxesTitle": "双Y轴", "BarTwoYAxesIntro": "显示第二Y轴:对齐方式,标题和对应数据组", "BarAspectRatioTitle": "图表比例", - "BarAspectRatioIntro": "设置了高度和宽度,会自动禁用约束图表比例, 图表充满容器" + "BarAspectRatioIntro": "设置了高度和宽度,会自动禁用约束图表比例, 图表充满容器", + "BarShowDataLabelTitle": "显示图表数据值", + "BarShowDataLabelIntro": "通过设置ShowDataLabel 属性,控制是否显示数据值", + "BarColorSeparatelyTitle": "单独设置柱状图颜色", + "BarColorSeparatelyIntro": "通过设置BarColorSeparately属性,控制是否单独设置柱状图颜色,以及通过BackgroundColor 集合设置柱状图颜色" }, "BootstrapBlazor.Server.Components.Samples.Transitions": { "TransitionsTitle": "Transition 过渡效果", diff --git a/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/Chart.razor.js b/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/Chart.razor.js index 2db78cbf47bef65d18e1ebbf305d8cd65172bddb..70c73281f77c9d56aeca4d49c63fd10cb00b8eb1 100644 --- a/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/Chart.razor.js +++ b/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/Chart.razor.js @@ -1,6 +1,8 @@ import '../../js/chart.js' +import '../../js/chartjs-plugin-datalabels.js' import Data from '../../../BootstrapBlazor/modules/data.js' import EventHandler from "../../../BootstrapBlazor/modules/event-handler.js" +Chart.register(ChartDataLabels) //注册插件 const chartOption = { options: { @@ -165,12 +167,18 @@ const getChartOption = function (option) { config = { ...chartOption } - colorFunc = function (data) { - const color = chartColors[colors.shift()] + if (option.options.barColorSeparately) { + colorFunc = function (data) { + data.borderWidth = 1 + } + } else { + colorFunc = function (data) { + const color = chartColors[colors.shift()] - data.backgroundColor = Chart.helpers.color(color).alpha(0.5).rgbString() - data.borderColor = color - data.borderWidth = 1 + data.backgroundColor = Chart.helpers.color(color).alpha(0.5).rgbString() + data.borderColor = color + data.borderWidth = 1 + } } } else if (option.type === 'pie' || option.type === 'doughnut') { @@ -193,6 +201,7 @@ const getChartOption = function (option) { data.backgroundColor = colors.slice(0, data.data.length).map(function (name) { return chartColors[name] }) + data.borderColor = 'white' } if (option.type === 'doughnut') { @@ -285,6 +294,18 @@ const getChartOption = function (option) { title: { display: option.options.title != null, text: option.options.title + + }, + //配置datalabel显示 + datalabels: { + anchor: option.options.anchor, + align: option.options.align, + formatter: Math.round, + display: option.options.showDataLabel, + color: option.options.chartDataLabelColor, + font: { + weight: 'bold' + } } }, scales: scale diff --git a/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartDataLabelPosition.cs b/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartDataLabelPosition.cs new file mode 100644 index 0000000000000000000000000000000000000000..f5aa83aa4b2ab99e99195446aac9043094dfc336 --- /dev/null +++ b/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartDataLabelPosition.cs @@ -0,0 +1,31 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System.ComponentModel; + +namespace BootstrapBlazor.Components; + +/// +/// Chart DataLabel 对齐方式 +/// +public enum ChartDataLabelPosition +{ + /// + /// 上 + /// + [Description("start")] + Start = 1, + + /// + /// 下 + /// + [Description("center")] + Center = 2, + + /// + /// 左 + /// + [Description("end")] + End = 3, +} diff --git a/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartDataset.cs b/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartDataset.cs index 4cab654c0af2ea82a4756b2fdf56ee0a961d3c3c..47ebcd658d3adc353dee8eccb9c9910a7956d7c6 100644 --- a/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartDataset.cs +++ b/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartDataset.cs @@ -68,6 +68,12 @@ public class ChartDataset /// 获得/设置 折线图(Line) 宽度 默认 3 个像素 /// public double BorderWidth { get; set; } = 3; - - + /// + /// 获得/设置 柱状图的颜色数组 + /// + public string[] BackgroundColor { get; set; } = { "rgb(255, 205, 86, 0)" }; + /// + /// 获得/设置 柱状图的边框颜色数组 + /// + public string[] BorderColor { get; set; } = { "rgb(255, 205, 86, 0)" }; } diff --git a/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartOptions.cs b/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartOptions.cs index c723f2182eb115d997646ab52255a4b6e200b1cf..7cb6b698a91ca824fdd021c6705314353ec29863 100644 --- a/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartOptions.cs +++ b/src/Extensions/Components/BootstrapBlazor.Chart/Components/Chart/ChartOptions.cs @@ -153,4 +153,26 @@ public class ChartOptions /// 获得/设置 X轴网格刻度线颜色 /// public string? YScalesGridTickColor { get; set; } + /// + /// 获得/设置 是否显示柱状图数据值 + /// + public bool ShowDataLabel { get; set; } + /// + /// 获得/设置 是否单独设置柱状图颜色 + /// + public bool BarColorSeparately { get; set; } + /// + /// 获得/设置 柱状图数据值的对齐方式 默认中间对齐 + /// + [JsonConverter(typeof(ChartEnumDescriptionConverter))] + public ChartDataLabelPosition Align { get; set; } = ChartDataLabelPosition.Center; + /// + /// 获得/设置 柱状图数据值的位置 默认显示在顶部 + /// + [JsonConverter(typeof(ChartEnumDescriptionConverter))] + public ChartDataLabelPosition Anchor { get; set; } = ChartDataLabelPosition.End; + /// + /// 获得/设置 柱状图数据值的颜色 + /// + public string ChartDataLabelColor { get; set; } = "rgb(75, 192, 192, 0.9)"; } diff --git a/src/Extensions/Components/BootstrapBlazor.Chart/wwwroot/js/chartjs-plugin-datalabels.js b/src/Extensions/Components/BootstrapBlazor.Chart/wwwroot/js/chartjs-plugin-datalabels.js new file mode 100644 index 0000000000000000000000000000000000000000..77a0d51d0e590dc4a47e5fd3cd9a242f5d79433d --- /dev/null +++ b/src/Extensions/Components/BootstrapBlazor.Chart/wwwroot/js/chartjs-plugin-datalabels.js @@ -0,0 +1,7 @@ +/*! + * chartjs-plugin-datalabels v2.2.0 + * https://chartjs-plugin-datalabels.netlify.app + * (c) 2017-2022 chartjs-plugin-datalabels contributors + * Released under the MIT license + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("chart.js/helpers"),require("chart.js")):"function"==typeof define&&define.amd?define(["chart.js/helpers","chart.js"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).ChartDataLabels=e(t.Chart.helpers,t.Chart)}(this,(function(t,e){"use strict";var r=function(){if("undefined"!=typeof window){if(window.devicePixelRatio)return window.devicePixelRatio;var t=window.screen;if(t)return(t.deviceXDPI||1)/(t.logicalXDPI||1)}return 1}(),a=function(e){var r,a=[];for(e=[].concat(e);e.length;)"string"==typeof(r=e.pop())?a.unshift.apply(a,r.split("\n")):Array.isArray(r)?e.push.apply(e,r):t.isNullOrUndef(e)||a.unshift(""+r);return a},o=function(t,e,r){var a,o=[].concat(e),n=o.length,i=t.font,l=0;for(t.font=r.string,a=0;ar.right&&(a|=2),er.bottom&&(a|=4),a}function u(t,e){var r,a,o=e.anchor,n=t;return e.clamp&&(n=function(t,e){for(var r,a,o,n=t.x0,i=t.y0,l=t.x1,u=t.y1,d=s(n,i,e),c=s(l,u,e);d|c&&!(d&c);)8&(r=d||c)?(a=n+(l-n)*(e.top-i)/(u-i),o=e.top):4&r?(a=n+(l-n)*(e.bottom-i)/(u-i),o=e.bottom):2&r?(o=i+(u-i)*(e.right-n)/(l-n),a=e.right):1&r&&(o=i+(u-i)*(e.left-n)/(l-n),a=e.left),r===d?d=s(n=a,i=o,e):c=s(l=a,u=o,e);return{x0:n,x1:l,y0:i,y1:u}}(n,e.area)),"start"===o?(r=n.x0,a=n.y0):"end"===o?(r=n.x1,a=n.y1):(r=(n.x0+n.x1)/2,a=(n.y0+n.y1)/2),function(t,e,r,a,o){switch(o){case"center":r=a=0;break;case"bottom":r=0,a=1;break;case"right":r=1,a=0;break;case"left":r=-1,a=0;break;case"top":r=0,a=-1;break;case"start":r=-r,a=-a;break;case"end":break;default:o*=Math.PI/180,r=Math.cos(o),a=Math.sin(o)}return{x:t,y:e,vx:r,vy:a}}(r,a,t.vx,t.vy,e.align)}var d=function(t,e){var r=(t.startAngle+t.endAngle)/2,a=Math.cos(r),o=Math.sin(r),n=t.innerRadius,i=t.outerRadius;return u({x0:t.x+a*n,y0:t.y+o*n,x1:t.x+a*i,y1:t.y+o*i,vx:a,vy:o},e)},c=function(t,e){var r=l(t,e.origin),a=r.x*t.options.radius,o=r.y*t.options.radius;return u({x0:t.x-a,y0:t.y-o,x1:t.x+a,y1:t.y+o,vx:r.x,vy:r.y},e)},h=function(t,e){var r=l(t,e.origin),a=t.x,o=t.y,n=0,i=0;return t.horizontal?(a=Math.min(t.x,t.base),n=Math.abs(t.base-t.x)):(o=Math.min(t.y,t.base),i=Math.abs(t.base-t.y)),u({x0:a,y0:o+i,x1:a+n,y1:o,vx:r.x,vy:r.y},e)},f=function(t,e){var r=l(t,e.origin);return u({x0:t.x,y0:t.y,x1:t.x+(t.width||0),y1:t.y+(t.height||0),vx:r.x,vy:r.y},e)},x=function(t){return Math.round(t*r)/r};function y(t,e){var r=e.chart.getDatasetMeta(e.datasetIndex).vScale;if(!r)return null;if(void 0!==r.xCenter&&void 0!==r.yCenter)return{x:r.xCenter,y:r.yCenter};var a=r.getBasePixel();return t.horizontal?{x:a,y:null}:{x:null,y:a}}function v(t,e,r){var a=r.backgroundColor,o=r.borderColor,n=r.borderWidth;(a||o&&n)&&(t.beginPath(),function(t,e,r,a,o,n){var i=Math.PI/2;if(n){var l=Math.min(n,o/2,a/2),s=e+l,u=r+l,d=e+a-l,c=r+o-l;t.moveTo(e,u),sr.x+r.w+2||t.y>r.y+r.h+2)},intersects:function(t){var e,r,a,o=this._points(),n=t._points(),i=[M(o[0],o[1]),M(o[0],o[3])];for(this._rotation!==t._rotation&&i.push(M(n[0],n[1]),M(n[0],n[3])),e=0;et.getProps([e],!0)[e]}),n=a.geometry(),i=$(l,a.model(),n),o._box.update(i,n,a.rotation()));(function(t,e){var r,a,o,n;for(r=t.length-1;r>=0;--r)for(o=t[r].$layout,a=r-1;a>=0&&o._visible;--a)(n=t[a].$layout)._visible&&o._box.intersects(n._box)&&e(o,n)})(t,(function(t,e){var r=t._hidable,a=e._hidable;r&&a||a?e._visible=!1:r&&(t._visible=!1)}))}(t)},lookup:function(t,e){var r,a;for(r=t.length-1;r>=0;--r)if((a=t[r].$layout)&&a._visible&&a._box.contains(e))return t[r];return null},draw:function(t,e){var r,a,o,n,i,l;for(r=0,a=e.length;r