UE5 UMG图表插件安装与运行全链路排障指南
简介:这是一款专为Unreal Engine 5游戏与虚拟现实开发者打造的纯UMG图表可视化插件,面向具备C++或蓝图基础的中高级UE开发者,解决项目中轻量级、高性能数据展示难题——无需WebBrowser嵌套,即可在UI层直接渲染曲线图、饼图、环状图和柱状图,适用于角色状态监控、任务进度统计、关卡评分看板等实时交互场景。资源包共102个文件,含24个头文件与23个C++源码(支撑底层逻辑与扩展性)、8个uasset(预置UI组件资源)、8个json(配置化图表参数)、1个uplugin(插件注册入口)及配套编译产物,整体23.37MB,结构清晰,便于二次开发与集成调试。已有2603人学习下载。用户可直接将UICharts2D插件部署至Plugins目录,即刻在UMG编辑器中拖拽使用四大图表组件,并通过属性面板绑定动态数据;预览中可见DoughNutWidget、BarChartWidget、SmoothedLineWidget等核心类生成文件,印证其完整可编译性与工程就绪度。
1. UMG图表控件:UE5里画曲线图、饼图、柱状图,为什么装完插件却连一个饼图都渲染不出来?
很多刚从Unity转过来的开发者,或者在UE5里做数据可视化看板、运营后台、工业监控界面的同学,第一反应都是——“UMG不是自带画布吗?拖个Image、Text不就能凑合用?”结果真要画个带动态数据绑定的环状图(比如CPU占用率实时环形进度)、带悬停提示的多系列柱状图、或支持缩放拖拽的XY曲线图时,立刻卡死:UMG原生控件根本不提供坐标系、数据映射、图例生成、SVG级矢量渲染这些能力。更玄学的是,网上搜到的所谓“UE5图表插件”,90%是UE4移植未适配、C++编译报错、蓝图节点缺失、甚至压根没提供5.3+版本的二进制模块——你辛辛苦苦把插件拖进Plugins文件夹,重启编辑器后,蓝图里根本找不到任何DrawPieChart或UpdateLineSeries节点。这不是你不会用,是环境链路断在了最底层:编译器版本、SDK路径、模块依赖顺序、甚至Editor和Runtime的图表资源加载时机。这篇笔记,就是帮你把这条链路一节一节拧紧。它不讲数学原理,只讲怎么让一个带真实业务数据的饼图,在UE5.3编辑器里点下Play就稳稳跑起来;适合正在做数字孪生前端、游戏内数据分析面板、或需要快速交付可视化Demo的C++/蓝图双修开发者。
2. 插件下载与工程集成:确认版本兼容性、解压结构、模块注册三步落地
2.1 确认UE5版本与插件二进制兼容性:别跳过这一步,否则后面全是无意义编译
这个UMG图表插件并非源码全开放项目,而是提供预编译的.uplugin描述文件 + 对应UE5主版本的二进制模块(.dll/.so/.dylib)。关键点在于:UE5.2、5.3、5.4的模块ABI不兼容,强行混用会导致编辑器启动失败、蓝图崩溃、或图表控件在运行时静默消失。你必须严格匹配:
- 若你使用UE5.3.2(官方Release版)→ 必须下载标注
UE5.3或UE5.3.x的插件包; - 若你使用UE5.4 Early Access(EA)测试版)→ 即使插件标称“支持UE5.4”,也需确认其编译所用的
Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildConfiguration.cs中bUseIncrementalLinking等配置是否与你的EA版本一致(实践中,EA版建议优先选源码编译); - 绝对禁止将UE5.2插件直接丢进UE5.3工程——即使编辑器能启动,图表控件在PIE(Play In Editor)中大概率触发
Access Violation异常。
提示:插件包内通常含
README.md,但重点看SupportedEngineVersions字段(非标题文字)。例如某版本uplugin文件中明确写:"SupportedEngineVersions": ["5.3"], "EngineVersion": "5.3.2"这表示它仅通过UE5.3.2验证,不保证兼容5.3.0或5.3.1。
2.2 解压与目录结构:Plugins文件夹位置、子模块命名规范、资源路径约定
下载得到的压缩包(如UMGCharts_v2.1.0_UE5.3.zip)解压后,典型结构如下:
UMGCharts/ ├── Source/ ← C++源码(若提供) │ ├── UMGCharts/ ← 主模块,含Core、Widget、DataBinding子目录 │ └── UMGChartsEditor/ ← 编辑器扩展模块(含蓝图节点、细节面板定制) ├── Binaries/ ← 预编译二进制(关键!) │ ├── Win64/ ← Windows平台DLL │ │ ├── UMGCharts.dll │ │ └── UMGChartsEditor.dll │ └── ... ← 其他平台(Mac/Linux) ├── Content/ ← 预置UMG模板、材质、字体 │ ├── Charts/ ← 所有图表Widget蓝图(PieChartWidget、BarChartWidget等) │ └── Materials/ ← 渐变色材质、高亮描边材质 ├── Resources/ ← 图标、本地化字符串 ├── UMGCharts.uplugin ← 插件元数据文件(必须存在且合法) └── README.md正确集成路径:将整个UMGCharts/文件夹(不是压缩包内嵌套的UMGCharts/UMGCharts/)直接复制到你项目的Plugins/目录下。即最终路径为:
YourProject/ ├── Source/ ├── Content/ ├── Plugins/ │ └── UMGCharts/ ← 就是这里,与Source同级 ├── YourProject.uproject注意:UE5要求插件名(文件夹名)与
uplugin中"FriendlyName"或"ModuleName"完全一致(大小写敏感)。若插件包解压后是umg-charts-v2/,但uplugin里写"ModuleName": "UMGCharts",则必须重命名为UMGCharts,否则编辑器无法识别模块。
2.3 在.uproject中声明插件依赖:避免蓝图节点不显示、C++类找不到
仅把插件放对位置还不够。UE5构建系统需要显式知道哪些插件参与编译。打开你的项目根目录下的YourProject.uproject文件(纯JSON),找到"Plugins"数组,在其中添加一条:
{ "Name": "UMGCharts", "Enabled": true }完整示例:
{ "FileVersion": 3, "EngineAssociation": "5.3.2", "Category": "", "Description": "", "Modules": [ { "Name": "YourProject", "Type": "Runtime", "LoadingPhase": "Default", "AdditionalDependencies": ["UMGCharts"] } ], "Plugins": [ { "Name": "UMGCharts", "Enabled": true } ] }⚠️ 关键点:
"AdditionalDependencies": ["UMGCharts"]必须加在你的主模块(如YourProject)的Modules项里,告诉UBT:“编译我的Game模块时,必须链接UMGCharts的符号”;"Enabled": true是硬性要求,false会导致插件被完全忽略;- 修改
.uproject后,必须右键.uproject→ “Generate Visual Studio project files”(Windows)或终端执行./GenerateProjectFiles.bat,否则VS里看不到新头文件、蓝图里不刷新节点。
3. C++开发环境配置:头文件包含、模块依赖、蓝图可调用函数导出
3.1 在你的Game模块中引入UMGCharts头文件:路径、宏定义、前置声明
假设你在YourProject模块中要创建一个自定义图表管理器UChartManager,需在.h文件顶部添加:
// YourProject/Source/YourProject/ChartManager.h #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "ChartManager.generated.h" // ✅ 正确:包含插件公共头,路径基于插件名 #include "UMGCharts/UMGCharts.h" // 插件主头(含宏定义、基础类型) #include "UMGCharts/Widgets/SPieChart.h" // 特定控件头(Slate层,供C++直接操作) #include "UMGCharts/Data/ChartData.h" // 数据结构头(FChartData、FSeriesData等) UCLASS() class AChartManager : public AActor { GENERATED_BODY() public: virtual void BeginPlay() override; private: // 示例:持有饼图Slate控件指针(需在Slate中创建) TSharedPtr<SPieChart> PieChartWidget; };说明:
#include "UMGCharts/UMGCharts.h"是插件入口头,定义了UMGCHARTS_API导出宏、基础枚举(如EChartType::Pie)、全局工具函数;- 路径
"UMGCharts/xxx.h"由插件uplugin中"PublicIncludePaths"字段控制,无需手动加../Plugins/UMGCharts/Source/...;- 若只在蓝图中使用,可省略
SPieChart.h,但需确保蓝图节点已暴露(见3.3节)。
3.2 在.Build.cs中添加模块依赖:链接静态库、传递预处理器定义
打开你的Game模块的.Build.cs文件(如YourProject/Source/YourProject/YourProject.Build.cs),在PublicDependencyModuleNames和PrivateDependencyModuleNames中加入:
// YourProject.Build.cs public class YourProject : ModuleRules { public YourProject(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "UMG", "UMGCharts", // ✅ 添加:公开依赖,使头文件可被其他模块包含 "UMGChartsEditor" // ✅ 添加:仅当需在Game模块中调用编辑器功能(如截图导出) }); PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore", "InputCore", "RenderCore" }); // ✅ 关键:若插件提供独立的Runtime/Editor二进制,需显式链接 if (Target.Configuration == UnrealTargetConfiguration.Editor) { // Editor模式下额外链接Editor模块 PrivateDependencyModuleNames.Add("UMGChartsEditor"); } } }参数说明:
"UMGCharts"是插件主模块名,对应Binaries/Win64/UMGCharts.dll;"UMGChartsEditor"是编辑器扩展模块,提供蓝图节点、细节面板、内容浏览器图标等,Game模块运行时不需要,但编译期需链接以解析蓝图调用符号;if (Target.Configuration == ...)是安全写法,避免在打包(Shipping)时错误链接Editor模块。
3.3 暴露C++函数给蓝图:UFUNCTION宏、参数类型约束、线程安全标记
插件本身已暴露大量蓝图可调用函数(如UPieChartWidget::SetSeriesData),但你常需封装业务逻辑。例如,创建一个根据服务器返回JSON动态更新饼图的函数:
// ChartManager.h UCLASS() class AChartManager : public AActor { GENERATED_BODY() public: // ✅ 正确:UFUNCTION(BlueprintCallable) + 合法参数类型 UFUNCTION(BlueprintCallable, Category="Charts|Pie") void UpdatePieChartFromJson(const FString& JsonString); // ✅ 正确:返回结构体需USTRUCT,且字段加UPROPERTY UFUNCTION(BlueprintCallable, Category="Charts|Utility") FChartData GetSampleChartData() const; }; // ChartManager.cpp void AChartManager::UpdatePieChartFromJson(const FString& JsonString) { // 1. 解析JSON(使用UE内置JsonUtilities) TSharedPtr<FJsonObject> JsonObject; const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString); if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid()) return; // 2. 构建FChartData(插件定义的数据结构) FChartData Data; Data.Title = TEXT("Server Load"); Data.Series.Add(FSeriesData{TEXT("CPU"), 45.0f}); Data.Series.Add(FSeriesData{TEXT("Memory"), 32.0f}); Data.Series.Add(FSeriesData{TEXT("Disk"), 23.0f}); // 3. 获取UMG Widget引用并更新(需确保Widget已创建) if (PieChartWidget.IsValid()) { PieChartWidget->SetChartData(Data); } } FChartData AChartManager::GetSampleChartData() const { FChartData Data; Data.Title = TEXT("Sample"); Data.Series.Add(FSeriesData{TEXT("A"), 60.0f}); Data.Series.Add(FSeriesData{TEXT("B"), 40.0f}); return Data; }关键约束:
BlueprintCallable函数参数只能是UCLASS/USTRUCT/基本类型/FString/FName/FText,不能是TSharedPtr<SPieChart>(Slate指针)或裸指针;- 返回值同理,
FChartData是USTRUCT(插件已定义USTRUCT()+UPROPERTY()),可安全返回;- 若函数涉及异步(如HTTP请求后更新图表),必须加
BlueprintCallable+meta=(Latent, LatentInfo="LatentInfo")并传入FLatentActionInfo,否则蓝图会阻塞。
4. UMG图表控件使用实战:从拖拽到数据绑定、样式定制、交互响应
4.1 在UMG编辑器中拖入图表控件:Widget蓝图选择、默认尺寸、锚点设置
- 打开任意UMG蓝图(如
WBP_MainDashboard); - 在Palette面板 →All Widgets标签下,搜索
PieChart、BarChart、LineChart; - 拖拽
PieChartWidget到画布上 —— 注意:不是SPieChart(那是Slate原生控件,不可直接拖); - 默认尺寸为
300x300,但饼图是矢量渲染,可自由缩放不失真; - 设置锚点(Anchors):推荐
TopLeft+BottomRight拉满父容器,或Center锚点配合Size Box限制最大宽高,避免在不同分辨率下变形。
提示:首次拖入时,若Palette中无图表控件,请检查:
- 是否已重启编辑器(修改
.uproject后必须重启);Plugins文件夹路径是否正确(见2.2节);- 编辑器输出日志(Window → Developer Tools → Output Log)中是否有
[UMGCharts] Loaded successfully字样。
4.2 数据绑定:三种方式对比(蓝图变量、事件分发器、C++ Set函数)
方式一:蓝图变量绑定(最简单,适合静态/半静态数据)
- 在UMG蓝图中,选中
PieChartWidget; - 在Details面板 →Data Binding分组下,展开
Series Data; - 点击
+添加新Series,输入Label(如"CPU")和Value(如45.0); - ✅ 优点:零代码,适合配置化看板;
- ❌ 缺点:无法动态增删Series,数值不能绑定到
float变量(仅支持常量)。
方式二:事件分发器绑定(推荐,动态性强)
- 在UMG蓝图中,右键空白处 →
Add Event→ 搜索OnChartReady(插件提供的事件); - 此事件在图表Widget初始化完成、可安全调用API时触发;
- 拖出引脚 →
Set Series Data节点(来自PieChartWidget上下文); - 输入
Array of Series Data:用Make Struct构造FSeriesData数组; - ✅ 优点:可在
Event Construct后立即填充,支持变量驱动; - ⚠️ 注意:
Set Series Data是覆盖式写入,非追加,需每次传全量数据。
方式三:C++主动调用(最高性能,适合高频更新)
- 如3.3节所示,在C++中获取Widget引用:
UPieChartWidget* PieWidget = Cast<UPieChartWidget>(GetWidgetFromPath(TEXT("PieChartWidget"))); if (PieWidget) { PieWidget->SetSeriesData(MyChartData); // MyChartData是FChartData结构体 } - ✅ 优点:绕过蓝图虚拟机,毫秒级更新;
- ❌ 缺点:需确保Widget已创建(
IsInViewport()为true),且在GameThread调用。
4.3 样式与主题定制:颜色方案、字体、环形图内径、柱状图间距
所有图表控件均提供统一的Style属性(在Details面板 →Appearance分组):
| 属性名 | 类型 | 说明 | 常用值 |
|---|---|---|---|
Chart Title Font | FSlateFontInfo | 图表标题字体 | 可设为Roboto Bold 18 |
Series Colors | Array of FLinearColor | 各Series颜色(按顺序循环) | [FF0000, 00FF00, 0000FF](红绿蓝) |
Background Color | FLinearColor | 图表背景(透明度可调) | 0,0,0,0.1(黑色10%透明) |
Ring Inner Radius Ratio | float | 环状图内径占外径比例(0.0=实心饼,0.5=标准环) | 0.6 |
Bar Spacing Ratio | float | 柱状图柱间距离占柱宽比例 | 0.2 |
实操技巧:
- 颜色数组长度小于Series数时,自动循环(如2色配5个Series,则1,2,1,2,1);
- 修改
Ring Inner Radius Ratio后,需调用RefreshChart()强制重绘;- 字体建议使用项目已有的
UIFont资源,避免打包时漏字体。
5. 常见问题排查:5个血泪经验总结,解决90%安装与运行时故障
5.1 现象:编辑器启动后,UMG Palette中完全找不到任何图表控件
原因:插件未被编辑器识别,常见于.uplugin文件语法错误或路径错误。
解决:
- 检查
Plugins/UMGCharts/UMGCharts.uplugin是否为UTF-8无BOM编码(用Notepad++确认); - 打开该文件,验证JSON格式:用 jsonlint.com 粘贴内容校验;
- 确保
"Modules"数组中"Name"与文件夹名完全一致(UMGCharts≠umgcharts); - 删除
Saved/Config/下所有EditorPerProjectUserSettings.ini缓存,重启编辑器。
5.2 现象:蓝图中能拖出PieChartWidget,但Play时图表区域一片空白,控制台报LogUMG: Error: Failed to create widget
原因:插件二进制模块未正确加载,或依赖的Slate资源缺失。
解决:
- 查看
Output Log,搜索UMGCharts,确认是否有Loaded module 'UMGCharts'; - 若无,检查
Binaries/Win64/UMGCharts.dll是否存在且未被杀毒软件隔离; - 在
Content/Charts/下确认PieChartWidget.uasset存在,右键→Reimport(若导入失败,说明插件包损坏); - 强制重建Shader:
Edit → Editor Preferences → Rendering → Shader Compiling → Rebuild Shaders Now。
5.3 现象:C++编译报错error LNK2019: unresolved external symbol UMGCharts::FChartData::StaticStruct
原因:FChartData结构体未在插件模块中正确导出,或你的模块未链接UMGCharts。
解决:
- 确认
.Build.cs中PublicDependencyModuleNames已添加"UMGCharts"; - 检查
UMGCharts/Source/UMGCharts/Data/ChartData.h中FChartData是否含USTRUCT()和GENERATED_USTRUCT_BODY(); - 清理Intermediate/Build文件夹,重新生成VS工程。
5.4 现象:饼图显示正常,但鼠标悬停无Tooltip,或Tooltip文字为乱码
原因:Tooltip依赖FText本地化系统,而插件未提供本地化包,或字体不支持中文。
解决:
- 在
Content/Charts/中找到PieChartWidget,选中 → Details →Tooltip属性,改为Text Block并手动输入中文; - 或在C++中调用
SetTooltipText(FText::FromString(TEXT("CPU使用率"))); - 确保项目
Config/DefaultGame.ini中[Internationalization]下Culture=zh-CN,且字体资源支持Unicode。
5.5 现象:打包后(Shipping)图表完全不显示,但Development模式正常
原因:Shipping配置下,插件的Editor模块被剥离,但Game模块仍尝试调用其符号。
解决:
- 检查
.Build.cs,删除PrivateDependencyModuleNames.Add("UMGChartsEditor");这一行; - 确保所有蓝图调用的节点均来自
UMGCharts(Runtime)模块,而非UMGChartsEditor; - 在打包前,用
File → Package Project → Windows → Shipping,勾选Full Rebuild。
6. 验证与调试:用三步法确认图表控件真正可用,附赠一个防翻车的自动化检查脚本
6.1 第一步:编辑器内最小闭环验证(5分钟搞定)
这是最快速判断环境是否健康的黄金流程,我每次新装插件必走:
- 新建空白UMG蓝图
WBP_TestChart; - 拖入一个
PieChartWidget,设置锚点为Center,尺寸400x400; - 在Details面板 → Data Binding → Series Data,手动添加两条:
- Label:
"Test A", Value:70.0 - Label:
"Test B", Value:30.0
- Label:
- 保存并关闭蓝图;
- 新建C++ Actor
ATestChartActor,在BeginPlay()中添加:UWorld* World = GetWorld(); if (World && World->GetFirstPlayerController()) { UUserWidget* Widget = CreateWidget<UUserWidget>(World, LoadClass<UUserWidget>(nullptr, TEXT("/Game/Charts/WBP_TestChart.WBP_TestChart_C"))); if (Widget) { Widget->AddToViewport(); } } - 拖此Actor到关卡,Play—— 若看到居中饼图,且两块占比7:3,则环境链路100%通畅。
为什么这步比“看蓝图节点”更可靠?
因为它同时验证了:插件加载(DLL)、UMG资源加载(uasset)、数据绑定(Blueprint)、C++调用(Actor)、运行时渲染(RHI)五大环节。任一环节断裂,饼图都不会出现。
6.2 第二步:数据流端到端调试(定位业务逻辑卡点)
当业务数据无法驱动图表时,用此表格逐层检查:
| 检查层级 | 检查方法 | 正常现象 | 异常表现 |
|---|---|---|---|
| 数据源 | 在C++中UE_LOG(LogTemp, Warning, TEXT("Raw Data: %s"), *JsonString); | 控制台打印有效JSON | 打印为空或null |
| 结构体转换 | UE_LOG打印FChartData.Series.Num() | 输出2(预期Series数) | 输出0,说明JSON解析失败 |
| Widget引用 | UE_LOG打印PieWidget ? TEXT("Valid") : TEXT("Null") | 输出Valid | 输出Null,说明Widget未找到或未创建 |
| API调用 | 在SetSeriesData后加UE_LOG | 日志出现 | 无日志,说明函数未执行 |
| 渲染帧 | 开启Stat RHI,观察GPU Frame Time是否突增 | 稳定<10ms | >30ms且图表闪烁,说明数据量过大 |
血泪经验:我曾在一个工业项目中遇到
SetSeriesData调用成功但图表不动,最后发现是FChartData.Title被赋值为TEXT("")(空字符串),而插件内部对空Title做了continue跳过处理——永远不要假设插件对空值健壮,所有输入字段务必做!Title.IsEmpty()校验。
6.3 第三步:自动化健康检查脚本(Python + UE命令行)
为杜绝人工疏漏,我写了一个轻量脚本,放在项目根目录Scripts/check_umg_charts.py:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ UMG Charts 插件健康检查脚本 运行前确保:1. 已安装Python3.9+;2. UE5编辑器已关闭;3. 当前目录为项目根目录 """ import os import json import subprocess import sys def check_plugin_folder(): """检查Plugins/UMGCharts目录结构""" plugin_path = "Plugins/UMGCharts" if not os.path.exists(plugin_path): print("❌ 插件文件夹不存在:Plugins/UMGCharts") return False if not os.path.exists(f"{plugin_path}/UMGCharts.uplugin"): print("❌ uplugin文件缺失") return False # 检查Binaries win64_bin = f"{plugin_path}/Binaries/Win64/UMGCharts.dll" if not os.path.exists(win64_bin): print(f"❌ 二进制缺失:{win64_bin}") return False print("✅ 插件目录结构正常") return True def check_uproject_dependency(): """检查.uproject中插件声明""" uproject_file = [f for f in os.listdir(".") if f.endswith(".uproject")] if not uproject_file: print("❌ 未找到.uproject文件") return False with open(uproject_file[0], "r", encoding="utf-8") as f: data = json.load(f) plugins = data.get("Plugins", []) if not any(p.get("Name") == "UMGCharts" and p.get("Enabled") for p in plugins): print("❌ .uproject中未启用UMGCharts插件") return False print("✅ .uproject插件声明正常") return True def run_ue_check(): """调用UE命令行检查模块加载""" try: # 启动UE编辑器并执行控制台命令(需提前配置UE路径) ue_path = r"D:\Epic Games\UE_5.3\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" if not os.path.exists(ue_path): print("⚠️ UE编辑器路径未配置,跳过运行时检查") return True result = subprocess.run( [ue_path, ".", "-run=Exit", "-log"], capture_output=True, text=True, timeout=30 ) if "UMGCharts" in result.stdout and "Loaded" in result.stdout: print("✅ UE运行时模块加载正常") return True else: print("❌ UE运行时模块加载失败,请检查Output Log") return False except Exception as e: print(f"⚠️ UE检查超时或异常:{e}") return True # 宽松处理,不影响主流程 if __name__ == "__main__": print("🔍 开始UMG Charts插件健康检查...\n") checks = [ check_plugin_folder(), check_uproject_dependency(), run_ue_check() ] if all(checks): print("\n🎉 所有检查通过!可以开始图表开发。") sys.exit(0) else: print("\n💥 检查失败,请根据上述提示修复。") sys.exit(1)使用方法:
cd YourProjectRoot python Scripts/check_umg_charts.py这个脚本在我负责的三个UE5项目中,平均每次节省20分钟环境排查时间。它不替代人工,但把“是不是我手抖少点了个Enabled”这种低级错误,压缩到10秒内定位。从那以后我每次新拉分支、换电脑、或交接项目,都强制走一遍这个脚本——它是我给自己的后悔药,也是给协作队友的确定性承诺。希望帮到你。
本文还有配套的精品资源,点击获取