支持多数据密集图表

This commit is contained in:
monitor1394
2019-04-02 00:24:57 +08:00
parent 3a48b06d34
commit 0b515a9afc
12 changed files with 5413 additions and 11143 deletions

View File

@@ -26,8 +26,8 @@ public class BarChartDemo : MonoBehaviour
rect.sizeDelta = new Vector2(wid, hig); rect.sizeDelta = new Vector2(wid, hig);
xchart.GetComponent<RectTransform>().sizeDelta = new Vector2(wid, hig); xchart.GetComponent<RectTransform>().sizeDelta = new Vector2(wid, hig);
bigdataChart = xchart.Find("barchart_bigdata").GetComponent<BarChart>(); bigdataChart = xchart.Find("barchart_multidata").GetComponent<BarChart>();
GenerateData(2000, bigdataChart); GenerateData(5000, bigdataChart);
} }
void Update() void Update()
@@ -66,20 +66,20 @@ public class BarChartDemo : MonoBehaviour
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
chart.AddXAxisCategory(time.ToString("yyyy-MM-dd hh:mm:ss")); chart.XAxis.AddMultiData(time.ToString("hh:mm:ss"));
smallBaseValue = i % 30 == 0 smallBaseValue = i % 30 == 0
? UnityEngine.Random.Range(0, 700) ? UnityEngine.Random.Range(0, 700)
: (smallBaseValue + UnityEngine.Random.Range(0, 500) - 250); : (smallBaseValue + UnityEngine.Random.Range(0, 500) - 250);
baseValue += UnityEngine.Random.Range(0, 20) - 10; baseValue += UnityEngine.Random.Range(0, 20) - 10;
//float value = Mathf.Max( float value = Mathf.Max(
// 0, 0,
// Mathf.Round(baseValue + smallBaseValue) + 3000 Mathf.Round(baseValue + smallBaseValue) + 3000
//); );
var index = i % 100; //var index = i % 100;
var value = (Mathf.Sin(index / 5) * (index / 5 - 10) + index / 6) * 5; //var value = (Mathf.Sin(index / 5) * (index / 5 - 10) + index / 6) * 5;
value = Mathf.Abs(value); value = Mathf.Abs(value);
chart.AddData(0, value); chart.AddMultiData(0, value);
time = time.AddSeconds(1); time = time.AddSeconds(1);
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -51,13 +51,13 @@ namespace xcharts
Series series = seriesList[j]; Series series = seriesList[j];
Color color = themeInfo.GetColor(j); Color color = themeInfo.GetColor(j);
int startIndex = 0; int startIndex = 0;
if (series.showDataNumber > 0 && series.dataList.Count > series.showDataNumber) if (series.showDataNumber > 0 && series.DataList.Count > series.showDataNumber)
{ {
startIndex = series.dataList.Count - series.showDataNumber; startIndex = series.DataList.Count - series.showDataNumber;
} }
for (int i = startIndex; i < series.dataList.Count; i++) for (int i = startIndex; i < series.DataList.Count; i++)
{ {
float data = series.dataList[i]; float data = series.DataList[i];
float pX = zeroX + coordinate.tickness; float pX = zeroX + coordinate.tickness;
float pY = zeroY + i * scaleWid; float pY = zeroY + i * scaleWid;
if (!yAxis.boundaryGap) pY -= scaleWid / 2; if (!yAxis.boundaryGap) pY -= scaleWid / 2;
@@ -80,12 +80,13 @@ namespace xcharts
float max = GetMaxValue(); float max = GetMaxValue();
if (tooltip.show && tooltip.DataIndex > 0) if (tooltip.show && tooltip.DataIndex > 0)
{ {
float tooltipSplitWid = scaleWid < 1 ? 1 : scaleWid;
float pX = zeroX + scaleWid * (tooltip.DataIndex - 1); float pX = zeroX + scaleWid * (tooltip.DataIndex - 1);
float pY = zeroY + coordinateHig; float pY = zeroY + coordinateHig;
Vector3 p1 = new Vector3(pX, zeroY); Vector3 p1 = new Vector3(pX, zeroY);
Vector3 p2 = new Vector3(pX, pY); Vector3 p2 = new Vector3(pX, pY);
Vector3 p3 = new Vector3(pX + scaleWid, pY); Vector3 p3 = new Vector3(pX + tooltipSplitWid, pY);
Vector3 p4 = new Vector3(pX + scaleWid, zeroY); Vector3 p4 = new Vector3(pX + tooltipSplitWid, zeroY);
ChartUtils.DrawPolygon(vh, p1, p2, p3, p4, themeInfo.tooltipFlagAreaColor); ChartUtils.DrawPolygon(vh, p1, p2, p3, p4, themeInfo.tooltipFlagAreaColor);
} }
for (int j = 0; j < seriesCount; j++) for (int j = 0; j < seriesCount; j++)
@@ -94,13 +95,13 @@ namespace xcharts
Series series = seriesList[j]; Series series = seriesList[j];
Color color = themeInfo.GetColor(j); Color color = themeInfo.GetColor(j);
int startIndex = 0; int startIndex = 0;
if (series.showDataNumber > 0 && series.dataList.Count > series.showDataNumber) if (series.showDataNumber > 0 && series.DataList.Count > series.showDataNumber)
{ {
startIndex = series.dataList.Count - series.showDataNumber; startIndex = series.DataList.Count - series.showDataNumber;
} }
for (int i = startIndex; i < series.dataList.Count; i++) for (int i = startIndex; i < series.DataList.Count; i++)
{ {
float data = series.dataList[i]; float data = series.DataList[i];
float pX = zeroX + i * scaleWid; float pX = zeroX + i * scaleWid;
if (!xAxis.boundaryGap) pX -= scaleWid / 2; if (!xAxis.boundaryGap) pX -= scaleWid / 2;
float pY = zeroY + coordinate.tickness; float pY = zeroY + coordinate.tickness;

View File

@@ -44,7 +44,18 @@ namespace xcharts
public bool showSplitLine = true; public bool showSplitLine = true;
public SplitLineType splitLineType = SplitLineType.dashed; public SplitLineType splitLineType = SplitLineType.dashed;
public bool boundaryGap = true; public bool boundaryGap = true;
public List<string> data; [SerializeField]
private List<string> data;
private List<string> multidata = new List<string>();
private List<string> Data
{
get
{
if (multidata.Count > 0) return multidata;
else return data;
}
}
public void AddData(string category) public void AddData(string category)
{ {
@@ -55,26 +66,26 @@ namespace xcharts
data.Add(category); data.Add(category);
} }
public string GetData(int index,float maxData = 0) public void AddMultiData(string category)
{ {
if(type == AxisType.value) if (multidata.Count >= maxSplitNumber && maxSplitNumber != 0)
{ {
return ((int)(maxData * index / GetSplitNumber())).ToString(); multidata.RemoveAt(0);
} }
int dataCount = data.Count; multidata.Add(category);
if (dataCount <= 0) return ""; }
float rate = dataCount / GetScaleNumber();
if (rate < 1) rate = 1; public string GetData(int index)
int newIndex = (int)(index * rate >= dataCount - 1 ? dataCount - 1 : index * rate); {
return data[newIndex]; return Data[index];
} }
public int GetSplitNumber() public int GetSplitNumber()
{ {
if (data.Count > 2 * splitNumber || data.Count <= 0) if (Data.Count > 2 * splitNumber || Data.Count <= 0)
return splitNumber; return splitNumber;
else else
return data.Count; return Data.Count;
} }
public float GetSplitWidth(float coordinateWidth) public float GetSplitWidth(float coordinateWidth)
@@ -84,20 +95,34 @@ namespace xcharts
public int GetDataNumber() public int GetDataNumber()
{ {
return data.Count; return Data.Count;
} }
public float GetDataWidth(float coordinateWidth) public float GetDataWidth(float coordinateWidth)
{ {
return coordinateWidth / (boundaryGap ? data.Count : data.Count - 1); return coordinateWidth / (boundaryGap ? Data.Count : Data.Count - 1);
}
public string GetScaleName(int index, float maxData = 0)
{
if (type == AxisType.value)
{
return ((int)(maxData * index / GetSplitNumber())).ToString();
}
int dataCount = Data.Count;
if (dataCount <= 0) return "";
float rate = dataCount / GetScaleNumber();
if (rate < 1) rate = 1;
int newIndex = (int)(index * rate >= dataCount - 1 ? dataCount - 1 : index * rate);
return Data[newIndex];
} }
public int GetScaleNumber() public int GetScaleNumber()
{ {
if (data.Count > 2 * splitNumber || data.Count <= 0) if (Data.Count > 2 * splitNumber || Data.Count <= 0)
return boundaryGap ? splitNumber + 1 : splitNumber; return boundaryGap ? splitNumber + 1 : splitNumber;
else else
return boundaryGap ? data.Count + 1 : data.Count; return boundaryGap ? Data.Count + 1 : Data.Count;
} }
public float GetScaleWidth(float coordinateWidth) public float GetScaleWidth(float coordinateWidth)
@@ -148,6 +173,9 @@ namespace xcharts
protected float coordinateWid { get { return chartWid - coordinate.left - coordinate.right; } } protected float coordinateWid { get { return chartWid - coordinate.left - coordinate.right; } }
protected float coordinateHig { get { return chartHig - coordinate.top - coordinate.bottom; } } protected float coordinateHig { get { return chartHig - coordinate.top - coordinate.bottom; } }
public Axis XAxis { get { return xAxis; } }
public Axis YAxis { get { return yAxis; } }
protected override void Awake() protected override void Awake()
{ {
base.Awake(); base.Awake();
@@ -251,10 +279,6 @@ namespace xcharts
base.RefreshTooltip(); base.RefreshTooltip();
int index = tooltip.DataIndex - 1; int index = tooltip.DataIndex - 1;
Axis tempAxis = xAxis.type == AxisType.value ? (Axis)yAxis : (Axis)xAxis; Axis tempAxis = xAxis.type == AxisType.value ? (Axis)yAxis : (Axis)xAxis;
if (index > tempAxis.data.Count - 1)
{
index = tempAxis.data.Count - 1;
}
if (index < 0) if (index < 0)
{ {
tooltip.SetActive(false); tooltip.SetActive(false);
@@ -263,17 +287,17 @@ namespace xcharts
tooltip.SetActive(true); tooltip.SetActive(true);
if (seriesList.Count == 1) if (seriesList.Count == 1)
{ {
string txt = tempAxis.GetData(index) + ": " + seriesList[0].dataList[index]; string txt = tempAxis.GetData(index) + ": " + seriesList[0].DataList[index];
tooltip.UpdateTooltipText(txt); tooltip.UpdateTooltipText(txt);
} }
else else
{ {
StringBuilder sb = new StringBuilder(tempAxis.data[index]); StringBuilder sb = new StringBuilder(tempAxis.GetData(index));
for (int i = 0; i < seriesList.Count; i++) for (int i = 0; i < seriesList.Count; i++)
{ {
string strColor = ColorUtility.ToHtmlStringRGBA(themeInfo.GetColor(i)); string strColor = ColorUtility.ToHtmlStringRGBA(themeInfo.GetColor(i));
string key = seriesList[i].name; string key = seriesList[i].name;
float value = seriesList[i].dataList[index]; float value = seriesList[i].DataList[index];
sb.Append("\n"); sb.Append("\n");
sb.AppendFormat("<color=#{0}>● </color>", strColor); sb.AppendFormat("<color=#{0}>● </color>", strColor);
sb.AppendFormat("{0}: {1}", key, value); sb.AppendFormat("{0}: {1}", key, value);
@@ -347,7 +371,7 @@ namespace xcharts
new Vector2(1, 0.5f), new Vector2(1, 0.5f),
new Vector2(coordinate.left, 20)); new Vector2(coordinate.left, 20));
txt.transform.localPosition = GetYScalePosition(scaleWid, i); txt.transform.localPosition = GetYScalePosition(scaleWid, i);
txt.text = yAxis.GetData(i, max); txt.text = yAxis.GetScaleName(i, max);
txt.gameObject.SetActive(coordinate.show); txt.gameObject.SetActive(coordinate.show);
yScaleTextList.Add(txt); yScaleTextList.Add(txt);
} }
@@ -366,7 +390,7 @@ namespace xcharts
new Vector2(scaleWid, 20)); new Vector2(scaleWid, 20));
txt.transform.localPosition = GetXScalePosition(scaleWid, i); txt.transform.localPosition = GetXScalePosition(scaleWid, i);
txt.text = xAxis.GetData(i, max); txt.text = xAxis.GetScaleName(i, max);
txt.gameObject.SetActive(coordinate.show); txt.gameObject.SetActive(coordinate.show);
xScaleTextList.Add(txt); xScaleTextList.Add(txt);
} }

View File

@@ -16,6 +16,7 @@ namespace xcharts
{ {
public bool show = true; public bool show = true;
public string text = "Chart Title"; public string text = "Chart Title";
public string subText = "";
public Align align = Align.center; public Align align = Align.center;
public float left; public float left;
public float right; public float right;
@@ -171,14 +172,21 @@ namespace xcharts
{ {
public string name; public string name;
public int showDataNumber = 0; public int showDataNumber = 0;
public List<float> dataList = new List<float>(); [SerializeField]
private List<float> dataList = new List<float>();
private List<float> multiDataList = new List<float>();
public List<float> DataList
{
get { return multiDataList.Count > 0 ? multiDataList : dataList; }
}
public float Max public float Max
{ {
get get
{ {
float max = 0; float max = 0;
foreach (var data in dataList) foreach (var data in DataList)
{ {
if (data > max) if (data > max)
{ {
@@ -194,7 +202,7 @@ namespace xcharts
get get
{ {
float total = 0; float total = 0;
foreach (var data in dataList) foreach (var data in DataList)
{ {
total += data; total += data;
} }
@@ -211,27 +219,45 @@ namespace xcharts
dataList.Add(value); dataList.Add(value);
} }
public void AddMultiData(float value)
{
if (multiDataList.Count >= showDataNumber && showDataNumber != 0)
{
multiDataList.RemoveAt(0);
}
multiDataList.Add(value);
}
public float GetData(int index) public float GetData(int index)
{ {
if (index >= 0 && index <= dataList.Count - 1) if (index >= 0 && index <= DataList.Count - 1)
{ {
return dataList[index]; return DataList[index];
} }
return 0; return 0;
} }
public void UpdataData(int index, float value) public void UpdateData(int index, float value)
{ {
if (index >= 0 && index <= dataList.Count - 1) if (index >= 0 && index <= dataList.Count - 1)
{ {
dataList[index] = value; dataList[index] = value;
} }
} }
public void UpdateMultiData(int index, float value)
{
if (index >= 0 && index <= multiDataList.Count - 1)
{
multiDataList[index] = value;
}
}
} }
public class BaseChart : MaskableGraphic public class BaseChart : MaskableGraphic
{ {
private const string TILTE_TEXT = "title"; private const string TILTE_TEXT = "title";
private const string SUB_TILTE_TEXT = "sub_title";
private const string LEGEND_TEXT = "legend"; private const string LEGEND_TEXT = "legend";
[SerializeField] [SerializeField]
protected Theme theme = Theme.Dark; protected Theme theme = Theme.Dark;
@@ -252,7 +278,6 @@ namespace xcharts
private float checkWid = 0; private float checkWid = 0;
private float checkHig = 0; private float checkHig = 0;
protected Text titleText;
protected List<Text> legendTextList = new List<Text>(); protected List<Text> legendTextList = new List<Text>();
protected float chartWid { get { return rectTransform.sizeDelta.x; } } protected float chartWid { get { return rectTransform.sizeDelta.x; } }
protected float chartHig { get { return rectTransform.sizeDelta.y; } } protected float chartHig { get { return rectTransform.sizeDelta.y; } }
@@ -301,18 +326,36 @@ namespace xcharts
RefreshChart(); RefreshChart();
} }
public void AddMultiData(string legend, float value)
{
for (int i = 0; i < seriesList.Count; i++)
{
if (seriesList[i].name.Equals(legend))
{
seriesList[i].AddMultiData(value);
break;
}
}
RefreshChart();
}
public void AddData(int legend,float value) public void AddData(int legend,float value)
{ {
seriesList[legend].AddData(value); seriesList[legend].AddData(value);
} }
public void AddMultiData(int legend, float value)
{
seriesList[legend].AddMultiData(value);
}
public void UpdateData(string legend, float value, int dataIndex = 0) public void UpdateData(string legend, float value, int dataIndex = 0)
{ {
for (int i = 0; i < seriesList.Count; i++) for (int i = 0; i < seriesList.Count; i++)
{ {
if (seriesList[i].name.Equals(legend)) if (seriesList[i].name.Equals(legend))
{ {
seriesList[i].UpdataData(dataIndex, value); seriesList[i].UpdateData(dataIndex, value);
break; break;
} }
} }
@@ -325,7 +368,33 @@ namespace xcharts
{ {
if (i == legendIndex) if (i == legendIndex)
{ {
seriesList[i].UpdataData(dataIndex, value); seriesList[i].UpdateData(dataIndex, value);
break;
}
}
RefreshChart();
}
public void UpdateMultiData(string legend, float value, int dataIndex = 0)
{
for (int i = 0; i < seriesList.Count; i++)
{
if (seriesList[i].name.Equals(legend))
{
seriesList[i].UpdateMultiData(dataIndex, value);
break;
}
}
RefreshChart();
}
public void UpdateMultiData(int legendIndex, float value, int dataIndex = 0)
{
for (int i = 0; i < seriesList.Count; i++)
{
if (i == legendIndex)
{
seriesList[i].UpdateMultiData(dataIndex, value);
break; break;
} }
} }
@@ -384,13 +453,21 @@ namespace xcharts
titlePosition = new Vector3(0, -title.top, 0); titlePosition = new Vector3(0, -title.top, 0);
break; break;
} }
titleText = ChartUtils.AddTextObject(TILTE_TEXT, transform, themeInfo.font, Text titleText = ChartUtils.AddTextObject(TILTE_TEXT, transform, themeInfo.font,
themeInfo.textColor, anchor, anchorMin, anchorMax, new Vector2(0, 1), themeInfo.textColor, anchor, anchorMin, anchorMax, new Vector2(0, 1),
new Vector2(titleWid, titleHig), 16); new Vector2(titleWid, titleHig), 16);
titleText.alignment = anchor; titleText.alignment = anchor;
titleText.gameObject.SetActive(title.show); titleText.gameObject.SetActive(title.show);
titleText.transform.localPosition = titlePosition; titleText.transform.localPosition = titlePosition;
titleText.text = title.text; titleText.text = title.text;
Text subText = ChartUtils.AddTextObject(SUB_TILTE_TEXT, transform, themeInfo.font,
themeInfo.textColor, anchor, anchorMin, anchorMax, new Vector2(0, 1),
new Vector2(titleWid, titleHig), 14);
subText.alignment = anchor;
subText.gameObject.SetActive(title.show && !string.IsNullOrEmpty(title.subText));
subText.transform.localPosition = titlePosition - new Vector3(0,15,0);
subText.text = title.subText;
} }
private void InitLegend() private void InitLegend()

View File

@@ -54,13 +54,13 @@ namespace xcharts
float startX = zeroX + (xAxis.boundaryGap ? scaleWid / 2 : 0); float startX = zeroX + (xAxis.boundaryGap ? scaleWid / 2 : 0);
int showDataNumber = series.showDataNumber; int showDataNumber = series.showDataNumber;
int startIndex = 0; int startIndex = 0;
if (series.showDataNumber > 0 && series.dataList.Count > series.showDataNumber) if (series.showDataNumber > 0 && series.DataList.Count > series.showDataNumber)
{ {
startIndex = series.dataList.Count - series.showDataNumber; startIndex = series.DataList.Count - series.showDataNumber;
} }
for (int i = startIndex; i < series.dataList.Count; i++) for (int i = startIndex; i < series.DataList.Count; i++)
{ {
float value = series.dataList[i]; float value = series.DataList[i];
np = new Vector3(startX + i * scaleWid, zeroY + value * coordinateHig / max); np = new Vector3(startX + i * scaleWid, zeroY + value * coordinateHig / max);
if (i > 0) if (i > 0)
@@ -93,9 +93,9 @@ namespace xcharts
// draw point // draw point
if (lineInfo.showPoint) if (lineInfo.showPoint)
{ {
for (int i = 0; i < series.dataList.Count; i++) for (int i = 0; i < series.DataList.Count; i++)
{ {
float value = series.dataList[i]; float value = series.DataList[i];
Vector3 p = new Vector3(startX + i * scaleWid, Vector3 p = new Vector3(startX + i * scaleWid,
zeroY + value * coordinateHig / max); zeroY + value * coordinateHig / max);

View File

@@ -57,7 +57,7 @@ namespace xcharts
angleList.Add(0); angleList.Add(0);
continue; continue;
} }
float value = seriesList[i].dataList[0]; float value = seriesList[i].DataList[0];
float degree = totalDegree * value / dataTotal; float degree = totalDegree * value / dataTotal;
float toDegree = startDegree + degree; float toDegree = startDegree + degree;
@@ -194,7 +194,7 @@ namespace xcharts
tooltip.SetActive(true); tooltip.SetActive(true);
string strColor = ColorUtility.ToHtmlStringRGBA(themeInfo.GetColor(index)); string strColor = ColorUtility.ToHtmlStringRGBA(themeInfo.GetColor(index));
string key = legend.dataList[index]; string key = legend.dataList[index];
float value = seriesList[index].dataList[0]; float value = seriesList[index].DataList[0];
string txt = ""; string txt = "";
if (!string.IsNullOrEmpty(pieInfo.name)) if (!string.IsNullOrEmpty(pieInfo.name))
{ {

View File

@@ -193,7 +193,7 @@ namespace xcharts
dataPosList.Add(new List<Vector3>()); dataPosList.Add(new List<Vector3>());
continue; continue;
} }
var dataList = seriesList[i].dataList; var dataList = seriesList[i].DataList;
var color = themeInfo.GetColor(i); var color = themeInfo.GetColor(i);
var areaColor = new Color(color.r, color.g, color.b, color.a * 0.7f); var areaColor = new Color(color.r, color.g, color.b, color.a * 0.7f);
var max = radarInfo.indicatorList[i].max > 0 ? var max = radarInfo.indicatorList[i].max > 0 ?
@@ -369,7 +369,7 @@ namespace xcharts
for (int i = 0; i < radarInfo.indicatorList.Count; i++) for (int i = 0; i < radarInfo.indicatorList.Count; i++)
{ {
string key = radarInfo.indicatorList[i].name; string key = radarInfo.indicatorList[i].name;
float value = seriesList[index].dataList[i]; float value = seriesList[index].DataList[i];
sb.Append("\n"); sb.Append("\n");
sb.AppendFormat("{0}: {1}", key, value); sb.AppendFormat("{0}: {1}", key, value);
} }

BIN
Doc/multidata.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -2,14 +2,16 @@
A Simple UGUI Charting Library for Unity A Simple UGUI Charting Library for Unity
# 特性 # 特性
1. 支持折线图(`LineChart`)、柱状图(`BarChart`)、饼图(`PieChart`)、雷达图(`RadarChart`)等 1. 支持折线图(`LineChart`)、柱状图(`BarChart`)、饼图(`PieChart`)、雷达图(`RadarChart`)等常用图表
2. 支持`Default``Light``Dark`三种主题切换 2. 支持`Default``Light``Dark`三种主题切换
3. 参数可视化配置,效果实时预览 3. 参数可视化配置,效果实时预览,纯源码绘制
4. 折线图通过参数可配置出:折线图、曲线图、区域图等 4. 折线图通过参数可配置出:折线图、曲线图、区域图等
5. 饼图通过参数可配置出:饼图、环形图、南丁格尔玫瑰图等 5. 饼图通过参数可配置出:饼图、环形图、南丁格尔玫瑰图等
6. 支持多数据密集图表
# TODO # TODO
1. ~~`tooltip`~~2019.3.21完成) 1. ~~`tooltip`~~2019.3.21完成)
2. ~~多数据支持~~2019.4.1完成)
2. 旭日图`sunburst` 2. 旭日图`sunburst`
3. 动画效果 3. 动画效果
@@ -20,7 +22,8 @@ A Simple UGUI Charting Library for Unity
![Light](Doc/light.png) ![Light](Doc/light.png)
3.`Dark`主题 3.`Dark`主题
![Dark](Doc/dark.png) ![Dark](Doc/dark.png)
4.多数据支持
![Multidata](Doc/multidata.png)
# 配置项手册 # 配置项手册
* `Theme` 主题 * `Theme` 主题
+ `theme`:主题,`Default``Light``Dark`三种可选主题 + `theme`:主题,`Default``Light``Dark`三种可选主题