news 2026/9/29 14:05:29

WinCC报表实现全攻略:从打印作业到VBS脚本导出Excel

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WinCC报表实现全攻略:从打印作业到VBS脚本导出Excel

接手项目的时候,对方提了一句"画面都挺满意,就是每天要手工导数据做报表,太痛苦了"。这句话让我意识到,很多WinCC项目做到最后,实时画面只是及格线,真正决定运营方用着顺不顺手的,往往是一张张交班报表、日报表和报警统计表。WinCC里攒了大量过程数据,如果只是停留在"能看",没有输出成"能交差"的表格,系统价值就打了对折。这篇文章就把WinCC报表这件事从头到尾捋一遍,包括实现方式、选型逻辑、实操步骤和我自己踩过的坑,给正在跟报表较劲的同行一个参考。

1. 报表在工业监控里到底解决什么问题

1.1 从"能实时看"到"能离线交差"的跨越

做工业监控这么多年,我越来越觉得,报表不是数据功能的锦上添花,而是整个系统的刚需。实时画面解决的是"现在怎么样",报表解决的是"刚才怎么样、今天怎么样、这个月怎么样"。车间主任不会一直盯着屏幕看曲线,但他一定会问:夜班产量多少、哪台设备停了多久、几个报警没处理。这些问题全部要落到历史数据上,落到一张结构清晰、时间范围明确、数值可靠的表上。

WinCC在实时性这块做得很好,趋势曲线、报警视图、变量监视都很成熟,但历史数据的对外输出一直是很多工程师觉得别扭的地方。原因在于,WinCC的数据默认存储在后台SQL Server数据库里,它不是一个直观的"表格软件",要让数据变成一份像样的Excel或PDF,中间要自己搭桥。也正是因为这个门槛,市面上才有了各种"土办法"和"专业办法",接下来我会详细讲。

1.2 WinCC报表的典型应用场景

我接触到的WinCC报表需求,大致逃不出下面几类:

  • 交接班报表:每班产量、运行时间、停机时间、班内报警次数,通常按8小时或12小时切分。
  • 日/周/月统计报表:均值、峰值、累计量,用于生产分析和绩效核算。
  • 报警统计报表:报警发生时间、确认时间、恢复时间,用来评估设备故障频率和处理及时性。
  • 能耗报表:水、电、气、蒸汽的瞬时流量和累计流量,这部分往往还要对接能源管理。
  • 质量追溯报表:某个批次产品对应的关键工艺参数曲线和数据点,出了问题要能倒查。

这些场景的共同特点是:数据量大、时间范围明确、输出格式有约定。搞清楚了需求,再选实现方式,才不会白忙一场。我见过太多人上来就写复杂脚本,结果发现用WinCC自带的打印作业就能满足,白白浪费半天时间。

2. 盘点WinCC出报表的四条路线,别一上来就选最难的那条

2.1 打印作业:只花十分钟的轻量输出

WinCC自带的打印作业(Print Job)是最容易被忽视的报表方案。它在WinCC项目管理器里就能组态,支持行打印和页面打印两种形式。行打印适合把报警消息一条条打出来,像流水账;页面打印适合把变量记录某个时间段的数据以表格形式输出到打印机或PDF虚拟打印机。

这个方案的优点是真省事,不需要写一行代码。在"打印作业"编辑器里新建一个作业,选择数据源——报警记录或者变量记录,设定时间范围和打印周期,再选一台打印机,一个周期性报表就出来了。很多车间每天早上自动打印一份前一天的报警清单,用这个功能就能实现。

缺点也很明显:版式固定、不能按需求做汇总和统计,比如平均值、最大值、合格率这些计算它做不了。另外它面向的是"直接打印"场景,如果要生成Excel文件再加工,它就无能为力了。所以我的判断是:如果需求只是"要一份数据流水",打印作业是最快路径;一旦涉及统计计算、格式定制、电子化存档,就要考虑下面的路线。

2.2 用户归档:把报表需求做成工艺数据

用户归档(User Archive)是WinCC里一个很有意思的组件。它本质上是一组可以由组态自定义结构的数据库表,运行时会话里通过脚本或画面操作往里写记录。很多人用它做配方管理,但其实它也可以用来做报表数据源。

举个例子,你可以在用户归档里建立一个"班次产量记录"结构,字段包括班次、日期、产量、设备状态、操作员。然后写一段脚本,在每次交接班时把当前统计值写入一条新记录。这样,报表要的数据不是从成千上万条原始归档点里实时算出来的,而是事先算好存好的。出报表的时候只需要把这些记录读出来排版就行,速度快、逻辑清晰。

这个方案的思路是"把报表数据变成生产数据来管理"。它适合业务逻辑相对固定的场景,比如班次记录、批次记录、检测结果登记。但如果你要的是一张灵活的、随时可以改时间范围和数据项的报表,用户归档就不太合适,因为它存储的是"预先定义好的结果",而不是原始的底层过程数据。

2.3 VBS脚本导出Excel:自由度最高的"万能方案"

如果说上面两个是WinCC"正统"功能,那VBS脚本导出Excel绝对是广大工程师用脚投票选出来的方案。思路很简单:用WinCC全局脚本里的VBS写一段程序,通过ADO连接WinCC的归档数据库,查询出需要的数据,然后调用Excel的COM对象把数据逐行写入工作表,再按模板美化格式,最后保存成.xlsx文件。

这条路线的核心价值在于"自由"。你可以完全控制报表的样式、统计逻辑、文件命名规则和存放目录。今天要一张带logo的表头,明天要加一列合格率判断,后天要把多个变量放在一个工作表里对比,全都能改。对于交付工程师来说,这种可定制性意味着再奇葩的需求都有办法兜底。

代价是学习曲线和调试成本。VBS的调试不像高级语言那么舒服,WinCC脚本运行时报错信息又很隐晦,加上Excel COM对象的版本兼容问题,新手起步时容易受挫。但从效果回报来看,这是目前WinCC报表项目里性价比最高的方案,也是后面我要重点实操的一条路线。

2.4 数据库直连加专业报表工具:面向海量数据的终局路线

如果你面对的报表需求特别重,比如全厂几百个变量、每分钟一批数据、要求秒级出统计结果,VBS逐行写Excel就会力不从心。这时候可以让专业的报表工具直接连WinCC的SQL Server数据库,把查询和展现交给报表工具来处理。

常见的组合是Crystal Reports、帆软报表(FineReport)、甚至直接用Excel的Power Query连数据库。思路是一样的:WinCC只是数据源,报表工具负责写SQL、做聚合计算、设计交互式报表页面。我见过有企业用帆软做了一套Web端生产报表,班组长在手机上就能看当班产量,底层数据就是直连WinCC数据库取出来的。

这个路线的门槛在于要懂数据库查询,还要理解WinCC的归档表结构。另外要特别注意权限和性能问题——生产系统的数据库不能随便让人跑全表查询,否则会影响WinCC自身的读写性能。这条路线更适合已经有IT开发能力或者专业报表团队的企业,对于单个项目的交付来说,可能有杀鸡用牛刀的感觉。

2.5 选型判断表

实现路线灵活度上手难度适合场景典型局限
打印作业低极低报警流水、周期数据打印不能做汇总统计,格式固定
用户归档中中班次记录、批次结果、配方记录只能输出预先定义的数据
VBS导出Excel高中高定制报表、多种统计、交付出报告调试麻烦,大数据量偏慢
数据库直连加报表工具极高高海量数据、Web报表、多部门共用涉及数据库权限,维护复杂

判断原则就一条:够用就好,别炫技。先看需求能不能用最简单的办法满足,满足不了再往上走。在我做过的项目里,VBS路线占了六成以上,不是说另外三种不好,而是大多数交付场景的报表需求,用VBS已经足够灵活地覆盖了。

3. 实操:用VBS脚本从WinCC归档数据库生成一张班次报表

3.1 开工前的组态检查:归档才是报表的数据源头

开始写脚本之前,有一件事必须确认:你要查询的变量到底有没有加到变量记录(Tag Logging)里做归档。很多人报表查出来是空的,第一反应是脚本写错了,但实际上百分之六十的情况是变量根本没有归档,或者归档周期设得太大,导致某个时间段里一条数据都没有。

在WinCC变量管理里,右键需要归档的变量,打开"变量记录"属性,确认"归档"选项勾上了,并且在变量记录编辑器里把采集周期设置合理。比如工艺参数要求秒级分析,采集周期就不能用一分钟;能耗累计量要算班累计,原始归档的时间间隔也不能太大,否则累计精度会受影响。

另外要注意,WinCC的归档数据是按时间分段存储的,如果你查询的时间范围跨了归档段,脚本里最好做分段查询再合并,否则可能只查到某一段的数据。我在项目里通常会把查询范围限制在单个班次或者单日,规避分段问题。

3.2 连接WinCC数据库:连接串与权限

WinCC运行时的数据都放在本机或服务器的SQL Server里。要用VBS查数据,第一步是建立ADO连接。标准的连接字符串是这样的:

Dim conn Set conn = CreateObject("ADODB.Connection") conn.ConnectionString = "Provider=SQLOLEDB.1;Data Source=.\WINCC;Initial Catalog=WinCC_Project001;Integrated Security=SSPI" conn.Open

这里有几个坑要提前说明。Data Source里的.\WINCC指的是本机SQL Server的命名实例WINCC,这是WinCC默认安装实例名。如果WinCC装在服务器上,客户端脚本要从网络访问,就得改成服务器名\WINCC,并且要确保SQL Server允许远程连接、防火墙端口放行。Initial Catalog是项目数据库名,一般和项目名一致,具体可以在WinCC项目的数据库属性里查到。

权限方面,连接用的是Windows集成认证,运行WinCC画面的用户必须对该数据库有读取权限。现场经常遇到的情况是:工程师自己登录有权限,但交接班操作员登录的Windows账户权限不够,导致报表脚本报错。解决办法是给相关用户组在SQL Server里授予db_datareader角色,或者在连接串里使用专门的只读账号。

3.3 核心SQL查询与脚本实现

WinCC归档数据存储在一张张以LGT_开头的表里,其中数字部分是归档段编号。查询某段时间、某个变量的值,典型的SQL是这样:

SELECT DateTime, Value FROM LGT_#01 WHERE DateTime >= '2025-01-01 06:00:00' AND DateTime < '2025-01-01 14:00:00' AND Value IS NOT NULL ORDER BY DateTime

需要注意的是,表名里的#在SQL中需要处理,动态拼接SQL时表名要加方括号。变量和表的对应关系可以通过系统表查到,但实操中我更推荐在WinCC变量记录编辑器里直接看分配关系,省得绕弯。

下面是一段完整的VBS脚本框架,把查询结果写入Excel:

Dim conn, rs, sql Dim excelApp, wb, ws, rowIndex ' 1. 连接WinCC数据库 Set conn = CreateObject("ADODB.Connection") conn.ConnectionString = "Provider=SQLOLEDB.1;Data Source=.\WINCC;Initial Catalog=WinCC_Project001;Integrated Security=SSPI" conn.Open ' 2. 查询班次数据 sql = "SELECT DateTime, Value FROM [LGT_#01] " & _ "WHERE DateTime >= '2025-01-01 06:00:00' " & _ "AND DateTime < '2025-01-01 14:00:00' ORDER BY DateTime" Set rs = conn.Execute(sql) ' 3. 创建Excel对象 Set excelApp = CreateObject("Excel.Application") excelApp.Visible = False Set wb = excelApp.Workbooks.Add() Set ws = wb.Worksheets(1) ' 4. 写入表头和数据 ws.Cells(1, 1).Value = "时间" ws.Cells(1, 2).Value = "过程值" rowIndex = 2 Do While Not rs.EOF ws.Cells(rowIndex, 1).Value = rs.Fields("DateTime").Value ws.Cells(rowIndex, 2).Value = rs.Fields("Value").Value rowIndex = rowIndex + 1 rs.MoveNext Loop ' 5. 保存文件 wb.SaveAs "D:\Reports\ShiftReport_20250101.xlsx" wb.Close False excelApp.Quit ' 6. 释放对象 Set rs = Nothing Set conn = Nothing Set ws = Nothing Set wb = Nothing Set excelApp = Nothing

这段脚本覆盖了"连接、查询、写表、保存、释放"完整链路。实际项目里还要加错误处理,比如数据库连接失败时要弹出提示而不是让脚本默默崩溃。另外,Excel对象的释放一定要做干净,否则多次运行后会有一堆Excel进程残留在服务器上,时间长了内存越占越多,这个坑下面专门说。

3.4 触发方式和报表文件组织

脚本写完之后,要考虑什么时候运行。WinCC里触发VBS脚本的方式主要有三种:画面按钮点击、全局脚本周期触发、报警或变量事件触发。

  • 画面按钮:操作员交接班时自己点一下"生成报表",最直观。
  • 周期触发:用全局脚本的周期执行功能,每天凌晨自动生成昨日报表。注意周期触发有最小时间限制,通常用于每天一次或每小时一次的场景。
  • 事件触发:比如某个变量变为1时生成报表,适合"批次结束自动出报告"的场景。

文件组织上,我习惯把报表按日期分目录存放,比如D:\Reports\2025\01\,文件名带班次标记,例如20250101_DayShift_Line1.xlsx。这样后续查找历史报表非常方便。还要定期清理过期报表,不然硬盘会被塞满,这看起来是小事,但在无人值守的服务器上真会出问题。

3.5 为什么要这样做:设计逻辑说明

可能有同行会问:既然WinCC自带了趋势控件和表格控件,为什么不直接在画面上显示报表,非要导成Excel?原因有两点。

第一,报表的使用者往往不在操作站前面。车间主任、生产调度、质量管理,他们需要的是能在自己电脑上打开、能打印、能转发给别人的文件,而不是跑去现场看某个操作画面。

第二,Excel是各岗位都认的通用格式。生产数据一旦落到Excel里,可以继续做二次分析、做PPT、传系统,这是WinCC自带控件给不了的开放性。所以"数据库查询加Excel输出"这套设计,本质上是把WinCC从封闭的监控系统变成了开放的数据服务,报表只是这个服务的第一层应用。

4. 报表背后必须搞懂的WinCC数据组织方式

4.1 归档数据是怎么落进SQL Server的

WinCC的变量记录组件会按照你设定的采集周期,周期性读取PLC里的变量值,然后写入SQL Server数据库。这个过程是WinCC运行时自动完成的,不需要你干预。但底下有几个细节直接影响报表准确性。

第一点是采集周期和存储周期的区别。采集周期决定CPU多久去读一次值,存储周期决定这些值多久写一次数据库。两者可以不同,例如采集周期1秒,存储周期30秒,数据库里实际记录的是每30秒一个快照。报表查到的数据粒度是存储周期决定的,不是采集周期。

第二点是归档段。为了避免单张表无限增长,WinCC会把数据按时间分成多个段,每个段对应一张LGT_#xx表。段的切换时机和大小可以在变量记录属性里配置。查询时要意识到你查的是"当前激活的段"还是"历史段",如果跨段查询,建议用系统提供的视图或联合查询方式。

第三点是压缩数据。WinCC可以提供平均值、瞬时值、最大值、最小值等压缩归档,用来减少磁盘占用并加快查询。如果报表只需要看趋势平均值,直接查压缩数据段比查原始数据快一个数量级。这个优化手段在数据量大时特别好用。

4.2 关键数据表结构认知

虽然WinCC版本不同,表结构细节会有差异,但核心逻辑是相通的。

变量归档数据表通常包含时间戳、数值、质量代码这几个核心列。时间戳记录该条数据的采集时刻,数值列存储过程值,质量代码表示该值的可靠性状态。报警记录表则是每一条报警消息一行,包含报警编号、触发时间、确认时间、恢复时间、报警文本等字段。

在写查询脚本时,最常用的是按时间范围过滤数据。SQL层面要特别注意时间的格式和时区。WinCC里显示的时间通常是你组态时的本地时间,但数据库存储时涉及UTC转换,如果服务器时区设置不对,查询结果会发现所有时间都偏移了几个小时。这个坑我至少遇到三次,每次排查到最后都是时区问题。

4.3 时间戳、采集周期和质量代码是三个最容易翻车的地方

报表数据不对,多半不是脚本语法问题,而是数据本身的三个属性出了问题。

时间戳的问题,除了时区偏移,还有时间不同步。很多现场的工控机和PLC之间没有做NTP时间同步,PLC记录的报警时间可能和WinCC服务器时间差几分钟甚至更多。报表里如果混用了不同时间源的数据,排序和统计都会乱。

采集周期的问题,主要是采集间隔太疏导致的数据盲区。比如一台设备瞬时温度波动很快,但你一分钟才采一次,报表里看到的峰值就永远小于实际峰值。这种问题在"为什么报表显示最高温度只有80度,实际都超90度了"的质疑里非常典型。

质量代码的问题常常被忽略。PLC通讯闪断、设备断电、变量被手动置位,都会产生带质量问题的数据。如果报表不区分好坏值,直接把坏值统计进平均值或累计量,结果会有明显偏差。查询时加一层质量过滤,或者至少在报表里把坏值单独标记出来,是一个值得养成的习惯。

5. 报表实施中的故障排查与性能优化

5.1 报表空白或数据对不上:完整排查链路

这是报表交付里最常被问的问题。一张表跑出来是空的,或者和现场实际值对不上,大多数人第一反应是改脚本,但脚本往往是无辜的。按下面这条链路排查,能省很多时间。

先确认时间范围。把报表里设置的开始和结束时间原样拿出来,在SQL Server Management Studio里手动执行一遍,看有没有数据。如果手动查有数据、报表没数据,那是传参问题;如果手动查也没数据,说明查询本身针对的时间段没有归档记录,要么变量当时没归档,要么归档被停止了。

再确认表名和变量对应关系。WinCC项目的归档表不止一张,而且归档段切换后数据可能分散在不同表里。用错表名查不到数据是常见低级错误,但也确实会发生。我习惯在脚本初期加一段诊断输出,把当前正在查的表名、时间范围、记录数都写到日志文件里,跑一次就清楚了。

接着确认权限。数据库连接串、Windows用户权限,这些在前面说过,就不再展开了。最后才是检查脚本本身的逻辑,比如循环写入时的行号计算、字段名拼写、类型转换。按这个顺序排查,大部分问题都能在十分钟内定位。

5.2 Excel导出偶发失败与进程残留

VBS调用Excel看着简单,实际运行中最大的敌人是Excel进程残留和权限隔离。

先说进程残留。脚本每次创建一个Excel.Application实例,如果脚本中途出错退出,或者没有执行到Quit那一步,这个Excel进程就不会销毁。反复运行几十次,服务器上会积累大量看不见的Excel进程,每个都占用几十上百兆内存,最终导致系统卡顿甚至新Excel无法正常启动。

我的习惯是:脚本开头先清理一次残留进程,用WMI查询并结束所有EXCEL.EXE进程。虽然暴力,但对报表服务器这种专用环境来说,效果比优雅释放更好。另外,确保脚本每一步都加了On Error Resume Next和状态判断,尽量保证能走到对象释放那一段。

再说权限隔离。WinCC画面运行在用户会话里,如果这个用户没有权限创建Excel对象(比如受限的域账户),脚本会直接报错。解决办法是给运行用户赋予本机普通用户权限,或者把报表服务独立出来用专门账户运行。曾经有人试图在Service账户下操作Excel,会遇到桌面交互问题,这也是一个要避开的坑。

5.3 数据量大时怎么让报表跑得快

报表慢到没法用的场景,一般发生在数据量达到百万级以上。这时候再去优化逐行写入Excel已经没意义了,要从查询效率上动手。

第一,避免全表扫描。永远用时间范围作为第一过滤条件,并且尽量让查询落在索引能够命中的范围。WinCC归档表上通常有时间索引,只要SQL写法不导致隐式类型转换,查询速度就不会太差。

第二,能用聚合查询就不要把原始数据拉到本地算。求一班平均值,直接在SQL里用AVG函数,把每秒一条的原始数据压缩成一条结果,再写入Excel。这样网络传输量和Excel行数都大幅减少,报表生成时间可以从几分钟降到几秒。

第三,考虑查压缩数据段。如果业务只需要看分钟级、小时级趋势,就不要查秒级原始数据。用WinCC的压缩归档作为数据源,数据量直接缩小几十倍,对双方都是减负。

第四,如果是周期性自动报表,可以把报表生成安排在凌晨数据量少的时候,错开操作高峰。同时注意WinCC自身的归档进程和报表查询同时进行时,磁盘IO会互相影响,生产系统上尤其要留意。

6. 做报表这几年,我的几点体会

每次做完一套WinCC报表,我都会问自己一个问题:如果明天操作员不用这个报表,是因为它不好用,还是因为根本不需要?答案可以帮我判断需求理解是否到位。

有个体会是真实的:报表的需求永远会变。今天要班报,下个月就要周报,再过几个月要把合格率加进去。所以我在设计脚本时从不把数据源写死在代码里,而是尽量通过配置文件或WinCC变量来设置查询对象和时间范围。这样后续调整需求时,改配置比改脚本安全得多,也不容易引入新问题。

另一个体会是:报表不只是技术活,更是沟通活。好的报表要让看的人三秒内找到他想看的数据,而不是扔给他一张密密麻麻的原始数据表。字段命名要贴近业务语言,比如"产量"而不是"Var001_Avg",单位要标清楚,时间范围要写明白,异常数据要有标记。这些东西,比脚本写得漂亮更能决定项目能不能顺利验收。

如果你正在做一个WinCC报表的需求,我建议先把使用者和使用场景问清楚,再选实现路线。大部分人用VBS导出Excel已经能解决九成的问题,少数重场景再考虑专业报表工具。先把第一步跑通,后面都好说。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 14:02:34

TensorFlow安装与生产部署的底层原理与避坑指南

1. 这不是“装个库”那么简单&#xff1a;TensorFlow到底在解决什么问题&#xff1f;你搜“tensorflow”&#xff0c;首页跳出来的不是技术文档&#xff0c;而是“TensorFlow安装失败”“ImportError: No module named tensorflow”“pip install tensorflow超时”——这说明什…

作者头像 李华
网站建设 2026/9/29 14:02:05

从TMS320F28335到国产DSP:选型对比与迁移实战指南

做了几年的嵌入式软件开发&#xff0c;手头好几个项目都在处理TI DSP的国产替代工作。最开始大家只是把国产芯片当作备选方案&#xff0c;没想到这几年越做越深入&#xff0c;从电源、电机驱动到并网逆变器&#xff0c;甚至高端音频设备&#xff0c;都在问同一个问题&#xff1…

作者头像 李华
网站建设 2026/9/29 14:00:32

IIPDS 2026视角下的图像与数据科学交叉:超分、修复与遥感医学实战

1. 从IIPDS 2026的征稿方向看图像与数据科学的交叉地带第一次看到“2026年图像、信息处理与数据科学国际会议&#xff08;IIPDS 2026&#xff09;”这个标题&#xff0c;我脑子里蹦出来的第一个念头是&#xff1a;这三个词终于被放到同一张桌子上了。过去几年&#xff0c;图像处…

作者头像 李华
网站建设 2026/9/29 14:00:08

Win10运行bash的三种方案:Git Bash、WSL1与WSL2选型指南

1. 先厘清一个根本误区&#xff1a;Win10里根本没有原生“bash批处理命令”很多人搜“Win10如何使用bash批处理命令”&#xff0c;一上来就卡在概念上——这个说法本身就不成立。Windows 10 的原生命令行环境是cmd.exe和后来升级的PowerShell&#xff0c;它们用的是 Windows 自…

作者头像 李华
网站建设 2026/9/29 13:55:57

阿里Qwen-Image LoRA训练指南:从零跑通风格定制模型

简介&#xff1a;这份资源是面向多模态模型开发者与AI训练爱好者的Qwen-Image LoRA训练实战代码包&#xff0c;聚焦阿里开源20B模型的三层融合架构解析与LoRA适配落地&#xff0c;帮助读者解决中文场景下微调效率低、手脚生成异常等实际问题。压缩包共5个文件&#xff0c;约12K…

作者头像 李华
网站建设 2026/9/29 13:52:34

arm64离线部署Harbor 2.13.1:从踩坑到跑通全指南

简介&#xff1a;这份资源是面向 ARM 架构服务器环境的 Harbor 2.13.1 离线安装包&#xff0c;适合需要在国产化平台或 ARM 服务器上私有化部署容器镜像仓库的运维与 DevOps 人员。包内共 6 个文件&#xff0c;以 shell 脚本、配置模板、压缩镜像包和许可证文件为主&#xff0c…

作者头像 李华