【基于SpringBoot和Echarts的数据可视化工具设计17000字(论文)】_第1页
【基于SpringBoot和Echarts的数据可视化工具设计17000字(论文)】_第2页
【基于SpringBoot和Echarts的数据可视化工具设计17000字(论文)】_第3页
【基于SpringBoot和Echarts的数据可视化工具设计17000字(论文)】_第4页
【基于SpringBoot和Echarts的数据可视化工具设计17000字(论文)】_第5页
已阅读5页,还剩43页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

基于SpringBoot和Echarts的数据可视化工具设计摘要数据可视化已成为当前的一个必然发展趋势,越来越多的数据可视化工具也应运而生。但有些工具需要用户掌握一定的编程能力,或是操作繁琐且收费高。因此,本文拟开发一个操作简单并且免费的数据可视化工具,从而可以帮助普通用户简化生成图表流程,提高图表制作效率。本文首先分析了数据可视化工具的国内外研究现状,接着介绍本系统的开发意义,然后分析了系统主要开发平台和工具,对系统进行需求分析和总体设计,最终实现了数据可视化系统。本系统主要采用SpringBoot+Echarts等技术框架。普通用户可以上传Excel表格,系统读取文件进行数据预览,用户选择需要可视化的数据列、生成方式以及图表类型,系统自动生成图表。此外,用户还可以保存图表、查看图表、删除图表。管理员登录后可以对普通用户信息进行维护以及添加、修改、删除等操作。经过系统测试,系统的基本功能均能够正常实现。后续应添加更多类型的图表,并为用户提供图表数据修改功能。关键词:数据可视化;数据可视化工具;SpringBoot目录摘要 I第1章绪论 11.1开发背景 11.2数据可视化工具现状分析 21.3本文研究内容 4第2章相关技术介绍 52.1IDEA 52.2Java语言 52.3SpringBoot框架 52.4Echarts图表库 5第3章需求分析与总体设计 63.1需求分析 63.1.1系统功能需求 63.1.2系统用例描述 73.2系统功能设计 83.2.1系统功能图 83.2.2系统流程图 93.3界面设计 103.4数据库设计 113.4.1数据库E-R图 113.4.2数据库表设计 12第4章系统功能实现 144.1注册/登录模块 144.1.1注册功能 144.1.2登录功能 154.1.3修改密码 164.2新建图表模块 174.2.1读取文件 174.2.2数据预览 184.2.3可视化图表属性设置 194.3图表展示 224.3.1折线图 224.3.2面积图 224.3.3柱状图 244.3.4饼图 254.3.5散点图 264.3.6雷达图 264.3.7词云图 284.3.8日历热力图 294.3.9K线图 304.3.10漏斗图 314.3.11仪表盘 324.4图表管理 324.4.1保存图表 324.4.2删除图表 344.5用户管理 34第5章系统测试 355.1功能测试 355.1.1注册登录模块测试用例 355.1.2新增图表模块测试用例 365.1.3图表管理模块测试用例 385.2界面测试 395.3测试结论 39第6章总结 40参考文献 41第1章绪论1.1开发背景当今世界,互联网技术高速的发展着,而每个行业的发展都缺少不了数据。从表面上看来这些数据仅仅是纯数字,大量的纯数字不具有可读性,用户不能直接利用其来解决问题。往更深层次的方面考虑,将这些大量的数据运用起来应该可以创造出很大的价值。为了将数据尽量更加形象、客观,甚至将枯燥的数据进行可视化,可以给阅读者带来方便。我们需要便捷的工具帮助我们将数据进行整合[1],因此数据可视化工具应运而生。为了满足人们对数据可视化工具的需求,一款好的数据可视化工具应该具备以下特点:(1)数据量大。数据可视化工具应该要适应大数据爆发式增长的特点,能够处理数据量比较大的文件。(2)简单操作。数据可视化工具应该要能够让人们使用起来觉得方便,操作简单才能够让用户留存率变得更高。(3)丰富的展示。数据可视化工具应该能够支持丰富的图表类型,能充分满足数据展示的多维度要求,例如二维折线图堆叠、二维柱形图堆叠、雷达图等。科技的越发成熟也让更多的数据可视化工具出现在人们的生活中,这些数据可视化工具种类繁多,功能也越来越强大。从图1.1我们可以看到,现在市面上比较常见的数据可视化工具可以从软件、在线服务、JS图表库这三个层面上进行划分。这三类数据可视化工具都有自己的特点:软件类:这类数据可视化工具需要下载然后按照教程安装,操作界面复杂,很多数据分析师会选择这款软件进行业务学习交流。在线服务类:这类数据可视化工具提供在线服务,支持图表的在线制作并支持,免费版允许用基本的图表类型,基本图表类型无法满足则需要升级为付费用户。JS图表库类:这类数据可视化工具大多数以JS包的形式存在,对数据进行二次加工,但是需要用户掌握一定的编程能力才能够实现数据的可视化。不同行业对于数据的需求不同,因此会选择不同的数据可视化工具。但是对于普通用户来说,更需要一个操作简单、图表类型丰富、免费的数据可视化工具。

图1.1市面上常见的数据可视化工具图片来源:https://b23.tv/dx9skp2019年6月23日1.2数据可视化工具现状分析代表性产品(见表1-1):用于介绍代表性产品界面、名称、缺点。表1-1代表性产品产品界面产品名称缺点Excel交互性差、只能插入255个数据Tableau收费高Inforgram不支持数据导入、收费高ggplots需要编程能力、掌握数学函数Echarts需要编程能力百度图说图表类型少图表网图表类型少、不支持数据导入、界面简单大数据魔镜针对特定企业1.数据可视化工具现状现在,国内外数据可视化发展相对来说已经比较成熟了,数据可视化工具也很多。在国外,1990年微软开发的Excel是一款电子表格软件,是最常用的数据统计的工具,加上成功的市场营销,Excel现在已经成为最流行的个人计算机数据处理软件[2]。虽然它的图表类型简单、配置项易用,但是输出毫无交互性可言,只能支持到255个数据插入图表中,生成的图表比较密集。Tableau是基于斯坦福大学突破性技术研究出来的一款可以快速分析、可视化并分享信息的数据可视化工具。软件很容易上手,可以将大量数据拖放到数字“画布”上,转眼间就能创建好各种图表[3]。但是非免费版本价格昂贵(TableauCreator软件每月每用户$70),公开版本不允许将用户的数据分析保密。Inforgram是美国的一款网站库,它支持在线编辑信息图,用户选完主题后再往编辑器里面输入数据,然后就能够生成一幅好看的信息图[4]。但是Inforgram不支持Excel文件的数据上传,也不支持直接将Excel数据直接复制粘贴放入Inforgram的编辑区里面。ggplot2是基于R语言的图形语法绘图系统,通过定义底层组件来合成复杂的图形[5],利用简洁的函数来产生复杂的多层图形。这款数据可视化软件需要使用者掌握数学函数以及R语言,这对于普通用户来说不是很友好。在国内,Echarts是由百度前端开发的一款基于JavaScript的开源数据可视化图表库,它提供直观、生动、可交互、可高度个性化定制的数据可视化图表[6]。Echarts封装了JS,开发者只要引用JS包加以编程并能够生成想要的图表。Echarts作为入门级别的编程工具库,要求初学者掌握有一定的JavaScript基础,这对于普通用户来说,使用起来是有门槛的。百度图说[7]是基于Echarts的一款在线制作图表的网站。图表自定义的选项很丰富,但是数据导入后不可以进行选择,生成的图表有可能不正确。提供的图表类型太少,功能比较单一,用户体验并不是很好。图表网[8]可以在线生成统计图表、制作直方图折线图等图形。这款软件不用安装注册就可以在线编辑具有flash动画的图表。但是制作出来的图表太简陋,并且也不支持Excel文件的导入。大数据魔镜是由苏州国云数据科技有限公司研发的一款报表工具,这个数据可视化工具可以帮助企业处理海量数据,对数据分析师和中小型企业来说,基础功能免费,可以代替传统的报表工具和传统的BI,比较适用于企业级数据分析[9]。2.存在问题现阶段,尽管数据可视化的工具种类繁多、功能强大,但是对于一个普通用户来说,这些数据可视化工具仍然存在着一些问题。(1)传统的数据可视化工具生成图表比较单一且交互性差。单一的可视化图表给用户的视觉体验不好,交互性差的图表展示起来内容也比较的生硬。(2)用户需要掌握一定的编程能力。需要学习编程语言这对于普通用户来说使用门槛有点高,用户不会操作且难以理解,相应的用户留存率也会很低。(3)不支持数据导入。这在处理数据量大的文件时,还需要用户手动进行输入,这样的工具很浪费时间并且很不合理。(4)许多数据可视化软件是需要进行付费的。普通用户在基础上想使用其他的图表就需要进行付费,高昂的付费价格对普通用户来说是不友好的。未来的数据可视化趋势已经成为了必然性,国内外的数据可视化工具也越来越多,但这些数据可视化工具对于一个普通用户来说,存在着操作复杂、交互性差、收费高、提供图表类型不丰富等问题。因此本文旨在设计开发出一个操作简单、图表类型丰富、免费的数据可视化工具。1.3本文研究内容本文在对国内外数据可视化工具现状分析的基础上,针对目前已有对于普通用户的数据可视化工具存在的不足,设计并实现了基于SpringBoot和Echarts的数据可视化工具。普通用户可以上传Excel表格,系统读取文件进行数据预览,用户选择需要可视化的数据列、生成方式以及图表类型,系统自动为该Excel表格生成该类型图表。此外,用户还可以保存图表、查看图表、删除图表。管理员登录后可以对普通用户信息进行维护以及添加、修改、删除等操作。本文主要开展下面两个方面的探索:(1)研究如何对用户上传的Excel文件进行读取,使用户上传数据后能够在系统Excel数据显示区域进行数据预览,从而辅助用户选择相应的数据列来生成图表。(2)研究如何对数据以及数据之间的关系进行可视化展示,使用户能够直观的感受数据的呈现,又能够通过图表了解数据变化的规律。

第2章相关技术介绍本系统以IDEA为系统开发的工具,以Java语言为开发语言进行编写,结合现在的微服务框架SpringBoot框架、Echarts可视化JS图表库进行Web应用开发。2.1IDEAIDEA是Java语言的开发集成环境,现在被很多人公认是最好的Java开发工具之一。IDEA在智能代码助手、代码自动提示、代码审查还有创新的GUI设计等方面是很超常的[10]。2.2Java语言Java是一门面向对象编程的语言,Java语言具有功能强大和简单易用这两个特征,具有简单性、面向对象、分布式等特点。可以编写桌面应用程序,Web应用程序。Java既有C++语言的特点,又少了指针等复杂难以理解的概念,使得开发人员能更好的专注于面向对象编程[11]。2.3SpringBoot框架SpringBoot它的设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。SpringBoot去除了大量的xml配置文件,简化了复杂的依赖管理,配合各种starter使用,基本上可以做到自动化配置。Spring可以做的事情,现在用Springboot都可以实现[12]。2.4Echarts图表库Echarts是由百度前端开发的一款基于JavaScript的开源数据可视化图表库。它提供直观生动、可交互、可高度个性化定制的数据可视化图表。Echarts封装了JS,开发人员只要下载并导入JS包加以编程就能够使用里面精美的图表,在很大程度上增强了用户的可视化体验,给用户更加强的视觉效果[6]。

第3章需求分析与总体设计本章对普通用户使用的数据可视化工具的需求进行分析,结合数据可视化工具的特点,完成本系统的系统功能设计、界面设计以及数据库设计。3.1需求分析3.1.1系统功能需求由于现有的数据可视化工具对于普通用户的使用,存在着操作不方便、生成的图表类型单一、收费高等问题。因此开发出一个操作简单、图表类型丰富、免费的数据可视化工具对于普通用户来说是非常有意义的。该工具可使得用户生成图表的操作更加简单、生成的图表更好看且类型更多、同时还可以减少普通用户的开销成本。基于SpringBoot的数据可视化工具可以让普通用户和管理员进行使用。前端提供给普通用户使用,包含了数据导入、数据预览、图表展示、图表管理等功能;后端提供给管理员使用,包含了对普通用户信息进行维护以及添加、修改、删除等操作。开发数据可视化工具应该注意以下几点要求:(1)数据可视化工具的设计采用B/S浏览器/服务器结构,方便用户通过web浏览器进行访问。(2)通用性设计,满足大多数普通用户对于数据可视化工具的实际需求。(3)能够满足对于处理各种信息的基本要求,处理的能力要好,普通用户可以对自己的图表进行管理,不可以对其他用户的数据图表进行修改。(4)在设计开发这个数据可视化工具的时候,要对后续的模块保留相应的接口,以保证后续功能的添加和完善。根据大多数普通用户对于数据可视化工具的实际需求,基于SpringBoot的数据可视化系统建设完成后,应该能够满足下列功能需求:(1)注册登录:为了用户信息的安全性,首次使用这个数据可视化工具的用户必须先注册或者由管理员在后台录入用户信息方可登录进入操作平台。用户也可以对密码进行修改。(2)数据导入:开发的数据可视化工具能够让用户将Excel表里面编辑好的表格在系统中进行导入。用户导入数据后,系统能够读取文件里面的数据并进行数据预览。数据预览的区域能够准确地将Excel里面的数据展示出来,并且系统根据文件里面的内容,解析出每一个数据列以便用户进行选择。(3)图表展示:用户根据系统解析出的数据选择主数据列,可以根据各个图表的特点,按需选择可视化图表生成方式和其中一种图表类型,系统自动为该Excel表格生成该类型图并展示在图表展示区域。(4)图表管理:用户可以直接下载并保存系统生成的图表。用户也可以点击保存图表按钮,图表将以图片的形式保存在已经创建好的图表页面中,在这个页面中还可以对图片进行再查看、删除、下载操作。在数据可视化工具开发完成后,通过对数据可视化工具的主要功能测试,确保达到预期目标。3.1.2系统用例描述在确定了数据可视化工具的功能目标后,采用前后端分离的模式进行系统开发。系统共分为两个使用人群:普通用户和系统管理员。普通用户登录后可以导入数据,系统生成数据预览,用户从中选择需要可视化的数据列、图表生成方式,并从系统提供的多种图表中选择一种,系统自动为其生成相应的图表。此外,用户对生成的图表进行管理。管理登录之后可以对普通用户个人信息进行维护,并进行添加、删除、修改操作。普通用户和管理员的用例图如图3.1、图3.2所示。图3.1普通用户用例图图3.2管理员用例图3.2系统功能设计3.2.1系统功能图本系统设计完成后可以在Web浏览器上使用。该系统主要有两类用户:普通用户和管理员,二者登录后跳转的页面不同。普通用户在系统中导入数据,选择图表主要数据列、生成方式和图表类型后,系统自动生成图表。用户保存图表后可以对图表进行查看、下载、删除等管理。这些功能对应于注册/登录模块、新建图表模块、图表管理模块。管理员登录后可以对普通用户的个人信息进行维护,以及增加、删除、修改操作,对应于用户管理模块。图3.3简要地展示了系统主要功能模块:(1)注册登录模块:新用户要先进行注册才能够登录进入到主界面,或者也可以通过管理员进行添加。(2)新建图表模块:普通用户将Excel表格导入,系统读取文件后进行数据预览。用户选择需要进行可视化的图表的主数据列、图表生成方式,并从系统提供的多种图表类型中选择一种,系统自动为选中的数据列生成相应类型的图表,并在指定区域进行显示。(3)图表管理模块:用户点击保存图表后,系统就会将这个图表以图片的形式保存在已建图表列表中。用户在图表管理模块可以对已经生成的图表进行查看、删除和保存。(4)用户管理模块:管理员进行对用户的基本信息进行维护。图3.3系统功能模块图3.2.2系统流程图系统流程图能够清晰直观地呈现出系统所有业务的大致情况。系统流程图如图3.4所示:新用户先注册账号,之后输入登录需要的账号、密码、验证码。验证成功后可以进入系统主界面。普通用户在系统中导入数据,选择图表主要数据列、生成方式和图表类型后,系统自动生成图表。用户点击保存图表按钮后,系统将图表保存到已经创建的列表里面。用户在这个列表里面可以对图表进行查看、删除和保存。管理员登录后可以对所有的用户信息和图表信息并进行查看和维护。图3.4系统流程图3.3界面设计普通用户登录之后会看到系统由两个部分组成,分别是新建图表界面(如图3.5所示)和已建图表界面(如图3.6所示)。在新建图表界面能看到工具栏(包括导入数据按钮、可视化图表属性设置按钮、保存图表按钮)、Excel数据预览区域、图表显示区域。点击导入数据按钮选择好Excel表格后,数据在Excel数据展示区域预览;点击可视化图表属性设置按钮后有一个弹出框可以设置图表主数据列、生成方式和图表类型。在导航栏点击“已建图表”能看到用户保存后的图表,可以预览图表、删除图表、保存图表。图3.5新建图表界面图3.6已建图表界面3.4数据库设计数据库的设计主要体现在各种信息的输入、输出、增加、修改、删除以及信息的准确性与安全性上,并且设计的数据库表要容易进行建立与维护。本系统选用mysql8.0作为数据库开发的工具。3.4.1数据库E-R图E-R图即实体联系图,同时用来表示现实世界中的具体构建在抽象系统中的有效方法,同时提供了表示实体类型、属性、联系和方法,用来描述现实世界的概念模型[14]。开发人员可以通过E-R图对数据进行清晰的逻辑分析,为下一步的数据库逻辑设计提供数据的逻辑结构。本系统一共包含了四个实体:管理员、用户、图表、图片记录。其中,管理员可以添加多名用户,因此管理员和用户是一对多的关系;一个用户可以创建多个图表,因此用户与图表之间是一对多关系;一个保存图表的操作之后信息只存在于一条图片记录中,因此图表和记图片录之间是一对一的关系;一个管理员可以管理多条图片记录,因此管理员和图片记录之间是一对多的关系。本系统的E-R实体联系图如图3.7所示。图3.7系统E-R图3.4.2数据库表设计根据数据库表的概念设计与数据的逻辑设计创建本系统所需要的数据库。其中包含5张表:用户表、记录表、系统管理员表、角色表、角色关系表:(1)用户表信息(见表3-1):用于存储用户的个人信息,包括用户ID、用户名、用户手机号、密码、创建时间。(2)图片记录表(见表3-2):用于存储用户生成的图表,包括编号、名字、类型、数据、创建时间、创建人、状态。(3)系统管理员表(见表3-3):用于存储管理员信息,包括编号、名字、密码、邮箱、电话、邮箱、创建人Id、创建时间。(4)系统角色表(见表3-4):用于存储系统的角色,包括角色ID、角色名称、角色、创建者ID、备注、创建者时间。(5)角色关系表(见表3-5):用于存储系统的角色ID、角色名称、备注。表3-1用户信息表字段名称数据类型是否为空主键字段描述user_idBigintNOYES用户IDusernamevarchar(50)NONO用户名mobilevarchar(20)NONO手机号passwordvarchar(64)YESNO密码create_timeDatetimeYESNO创建时间表3-2图片记录表字段名称数据类型是否为空主键字段描述idbigintNOYES编号namevarchar(255)NONO名字typesvarchar(255)YESNO类型datasvarchar(300)YESNO数据create_timedatatimeYESNO创建时间create_uservarchar(255)YESNO创建人statusvarchar(255)YESNO状态

表3-3系统管理员表字段名称数据类型是否为空主键字段描述user_idbigintNOYES编号usernamevarchar(50)NONO用户名passwordvarchar(100)NONO密码emailVarchar(100)YESNO邮箱mobilevarchar(100)YESNO电话create_user_idbigintYESNO创建人idcreate_timedatetimeYESNO创建时间表3-4系统角色表字段名称数据类型是否为空主键字段描述role_idbigintNOYES角色IDrole_namevarchar(100)YESNO角色名称remarkvarchar(100)YESNO备注create_user_idbigintYESNO创建者IDcreate_timedatetimeYESNO创建时间表3-5角色关系表字段名称数据类型是否为空主键字段描述idbigintNOYES角色IDrole_idbigintYESNO角色名称user_idbigintYESNO备注

第4章系统功能实现4.1注册/登录模块4.1.1注册功能注册的程序流程图如图4.1所示。新用户首次登录前需要注册账号,或者让系统管理员对用户信息进行添加。下面首先介绍普通用户的注册方法:用户点击注册按钮,系统跳转到注册页面(如图4.2所示)。用户在注册页面依次输入用户名、密码、确认密码、邮箱、手机号。其中,输入的密码和确认密码要一致,手机号码输入和邮箱输入的格式要正确。图4.1注册流程图4.2注册界面当用户在注册界面中按照正确的格式输入自己的个人信息并点击确定按钮后,就会触发dataFormSubmint事件,v-model进行数据的双向绑定,验证得到了正确的form表单之后就会根据url:this.$http.adornUrl(`/sys/user/register'`)与后端/sys/user/register进行路径匹配,匹配成功就会调用注册register()方法实现注册功能。用户注册成功之后就会提示操作成功,则该用户就能够登录系统了。注册功能核心代码如下:@PostMapping("/register")@PostMapping("/register")

publicRregister(@RequestBodySysUserEntityuser){//得到user对象

ValidatorUtils.validateEntity(user,AddGroup.class);//得到用户注册时输入的账号、密码、电子邮箱、手机号码等个人信息.

List<Long>list=newArrayList<>();//创建一个列表

list.add(1L);//将一个对象放入这个列表中

user.setStatus(1);//状态为1表示能够正常登录系统的用户

user.setCreateUserId(1L);//给用户设置ID

sysUserService.saveUser(user);//保存这个对象

returnR.ok();//返回注册成功的信息}4.1.2登录功能登录流程图如图4.3所示。注册用户和管理员可以在登录界面(如图4.4所示)输入自己的用户名、密码、验证码,点击“登录”按钮后,数据通过验证后方可进入系统。图4.3登录流程图图4.4登录界面当用户输入账号、密码、验证码之后,点击登录按钮,会触发dataforsubmit事件,检查验证如果拿到了正确的form表单,就会通过路径url:this.$http.adornUrl('/sys/login')与/sys/login登录的代码进行匹配,匹配成功调用登录方法login(),对相应信息进行验证。如果验证不成功会给出提示信息,如果登录成功则进入主界面。核心代码如下: publicMap<String,Object>login(@RequestBodySysLoginFormform)throwsIOException{//得到用户登录输入的账号、密码、验证码等信息 booleancaptcha=sysCaptchaService.validate(form.getUuid(),form.getCaptcha()); if(!captcha){//对验证码进行验证 returnR.error("验证码不正确");} //根据用户的账号与数据库里的进行匹配 SysUserEntityuser=sysUserService.queryByUserName(form.getUsername()); if(user==null||!user.getPassword().equals(newSha256Hash(form.getPassword(),user.getSalt()).toHex())){//账号不存在、密码错误 returnR.error("账号或密码不正确"); } }4.1.3修改密码修改密码流程图如4.5所示。用户和管理员可以对密码进行修改。当用户点击修改密码之后,系统会弹出一个修改密码对话框(如图4.6所示),用户需要该对话框中输入原密码、新密码、确认密码,输入信息无误点击确定按钮后就能完成密码的修改。图4.5修改密码流程图图4.6修改密码界面

当用户在输入框输入修改密码的信息后点击确定按钮之后,就会触发dataforsumbit事件,系统验证成功后就会根据this.$http.adornUrl('/sys/user/password')进行路径的匹配,匹配成功后调用修改密码的方法updatePassword()实现修改密码的功能。修改密码功能核心代码如下:@PostMapping("/password")@PostMapping("/password")publicRupdatePassword(@RequestBodyPasswordFormform){//得到原密码与新密码Assert.isBlank(form.getNewPassword(),"新密码不为能空"); //输入的密码Stringpassword=newSha256Hash(form.getPassword(),getUser().getSalt()).toHex(); //输入的新密码StringnewPassword=newSha256Hash(form.getNewPassword(),getUser().getSalt()).toHex();//更新密码booleanflag=sysUserService.updatePassword(getUserId(),password,newPassword); if(!flag){ returnR.error("原密码不正确");}returnR.ok();}4.2新建图表模块4.2.1读取文件用户登录系统后,在新建图表界面点击“导入数据”按钮就能够将Excel表格导入系统,数据导入界面如图4.7所示。图4.7数据导入界面在用户导入Excel表格的过程中,系统对导入的数据文件有相应的格式限制。系统先对文件的后缀进行分析,如果文件格式错误,会给出错误的提示信息,并且让用户重新选择文件。在读取文件的方法file2Xce()

中,通过promise事件来进行管控,使用的时候先通过构造函数初始化一个FileReader对象,通过插件XLSX.read

()读取Excel表格里面的数据,onload事件在读取完成后进行触发。构造一个数组

result存放数据结果,然后根据每一个sheet表的名字,通过XLSX.utils.sheet_to_json()

方法将sheet里面数据通过调用封装成json数据存放在每一个sheet表的数据内容里面。核心代码如下:file2Xce(file){file2Xce(file){returnnewPromise(function(resolve,reject){//把文件读取,通过promise事件来进行管控constreader=newFileReader();//通过FileReader把文件读取出来reader.onload=function(e){//onload事件在读取文件完成后触发constdata=e.target.result;//得到文件中的数据this.wb=XLSX.read(data,{type:"binary"//通过插件读取Excel表格里面的数据});constresult=[];this.wb.SheetNames.forEach(sheetName=>{result.push({//每一个工作表里面的数据sheetName:sheetName,sheet:XLSX.utils.sheet_to_json(this.wb.Sheets[sheetName])//通过wb.sheet里面的信息得到sheet里面的数据,再把sheet里面数据通过调用封装成json数据})})resolve(result);}reader.readAsBinaryString(file.raw);//传统的input方法})}4.2.2数据预览用户点击“导入数据”按钮,选择好要导入的Excel表格之后,系统将用户导入的表格准确无误的展示在Excel数据预览区域。导入一个Excel表格后在系统中的数据预览界面如图4.8所示。

图4.8数据预览界面用户上传Excel表格以后,系统对文件进行读取,将上传的数据封装成JSON数据。通过JSON.parse(JSON.stringify(obj))方法对每一个数据在前端的显示,这个方法是利用JSON.stringify

将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象,然后将这些数据在前端页面中进行显示出来。数据预览的核心代码如下:<el-table<el-table

:data="xlsxList"

style="width:100%">

<el-table-columnv-for="iteminJSON.parse(JSON.stringify(Object.getOwnPropertyNames(xlsxList[0]||[]))):prop="item"

:label="item"

:key="item+newDate().getTime()">

</el-table-column></el-table>4.2.3可视化图表属性设置可视化图表属性设置需要完成三个步骤:选择需要显示的数据列、选择生成方式、选择图表类型。选择需要显示的数据列用户上传表格后,系统对数据进行解析,然后在前端显示该表格。点击“可视化图表属性设置”按钮就能够可以看到已经上传的图表的所有数据列。在定义formdata的时候,xOption表示X轴数据列,考虑到用户要生成二维图表,需要选择多个Y轴数据列,所以定义用一个数组yOption:[]来表示Y轴数据列。核心代码如下:<el-formref="form"v-model="form"label-width="80px"><el-formref="form"v-model="form"label-width="80px"><el-form-itemlabel="X轴"><el-selectv-model="form.xOption"placeholder="请选择"><el-option//v-model进行数据的绑定v-for="iteminform.options"//X轴数据列从多个列中选择一列,一般都是名称:label="item":value="item">//同时获取value和label的值</el-option></el-select></el-form-item><el-form-itemlabel="Y轴"v-show="form.mode!=2"><el-selectv-model="yOption"multipleplaceholder="请选择"><el-option//Y轴数据列是多选列,当选form.mode=2单列统计的时候数据列不显示v-for="iteminform.options":label="item":value="item"></el-option></el-select>2、选择生成方式有些图表是要进行统计的。用户可以选择单独的一列进行统计(例如用户要查看性别统计情况,那么他只选择“性别”这列进行统计)或选择两列进行统计(例如统计一年中每个书店的总销售额,那么就要选择书店名称和销量这两列进行统计)。用form.mode对图表生成方式进行标记,form.mode=0表示按照正常的顺序进行数据的封装传递、form.mode=1表示选择两列数据进行统计、form.mode=2表示选择单列数据进行统计。核心代码如下:if(this.form.mode==1){//两列统计if(this.form.mode==1){//两列统计letresult=[]data.forEach(item=>{letfind=result.find(el=>el[this.form.xOption]===item[this.form.xOption])//X相同this.yOption.forEach(option=>{if(find){find[option]+=item[option]//对Y轴的值进行累加}else{result.push(item)}})})data=result}elseif(this.form.mode==2){//单列统计letresult=[]this.yOption=['数量']data.forEach(item=>{//单列X相同letfind=result.find(el=>el[this.form.xOption]===item[this.form.xOption])if(find){找到对X里面的值用计数器处理find['数量']++}else{tem['数量']=1//找不到就计为1result.push(item)})data=result}3、选择图表类型在定义formdata的时候用form.type表示图表类型。在图表要求的数据格式正确的前提下,系统就能够为用户选中的数据列生成相应的图表。用户点击“可视化图表属性设置”按钮后会出现一个对话框,可以进行可视化图表属性的设置(如图4.9所示)。在该对话框里即可完成选择数据列、选择生成方式、选择图表类型这三个步骤的设置。鼠标滑过每一个图表类型上方,能看见这个图表的预览,方便用户进行选择。图4.9可视化图表属性设置界面

4.3图表展示4.3.1折线图折线图常用来分析数据随时间的变化趋势,也可用来分析多组数据随时间变化的相互作用和相互影响[15],可以清晰反应递增还是递减以及峰值情况。为系统设计了两种折线图:基础折线图(如图4.10所示)和基础平滑折线图(如图4.11所示)。这两种图主要通过series里面的smooth属性进行进行区分。通过map遍历每一个xOption里面的值并对xAxis.data进行赋值,另外通过map遍历对每一列的option里面的值对series.data进行赋值。核心代码如下:this.yOption.forEach(option=>{this.yOption.forEach(option=>{lettemp=JSON.parse(JSON.stringify(series))this.options[this.form.type].xAxis.data=data.map(item=>item[this.form.xOption])//X轴数据temp.data=data.map(item=>item[option])//每一列数据=optionthis.options[this.form.type].series.push(temp)})图4.10基础折线图图4.11平滑折线图4.3.2面积图面积图是在折线图的基础之上形成的,它将折线图中折线与自变量坐标轴之间的区域使用颜色或者纹理填充,颜色的填充可以更好的突出趋势信息。本系统提供堆叠面积图(如图4.12所示)和渐变堆叠面积图(如图4.13所示)两种面积图。

areaStyle:{

opacity:0.3,

color:newecharts.graphic.LinearGradient(0,0,0,1,[{//前边四个参数配置颜色渐变的起止位置:按照顺序依次为:右下左上四个方向的起止位置,1代表着渐变

offset:0,//第五个参数是一个数组,数组里边给渐变的起止点的颜色,offset:0代表渐变0方向的颜色;offset:1代表渐变1方向的颜色

color:'#45bdc5'

},{

areaStyle:{

opacity:0.3,

color:newecharts.graphic.LinearGradient(0,0,0,1,[{//前边四个参数配置颜色渐变的起止位置:按照顺序依次为:右下左上四个方向的起止位置,1代表着渐变

offset:0,//第五个参数是一个数组,数组里边给渐变的起止点的颜色,offset:0代表渐变0方向的颜色;offset:1代表渐变1方向的颜色

color:'#45bdc5'

},{

offset:1,

color:'rgba(113,237,255,0.1)'

}])

}图4.12堆叠面积图图4.13渐变堆叠面积图

4.3.3柱状图柱状图主要用于多个分类间的数据(大小、数值)的对比,二维柱状图中,根据每个分组中柱子的不同颜色区别各个分类。本系统提供了柱状图(如图4.14所示)和渐变缩放柱状图(如图4.15所示)。柱状图跟折线图的封装方法也很相似,主要区别在series里边的属性types上,折线图的series.type=line,柱状图的series.type=bar。柱状图和渐变缩放柱状图的主要区别在于datazoom组件,这个组件在数据过多的时候使用。渐变缩放柱状图的datazoom组件是内置数据区域缩放组件,添加了这个组件,用户可以通过鼠标滚轮来缩放坐标系。核心代码如下:dataZoom:{//dataZoom:{//数据过多时添加datazoom组件show:true,start:0,end:100//设置起始的百分比}//柱状图dataZoom:[{type:'inside'//type=inside,表示是内置型数据区域缩放组件}]//渐变缩放柱状图图4.14柱状图图4.15渐变缩放柱状图

4.3.4饼图饼图用于展示不同类比之前的占比问题,通过弧度的大小或者半径的大小来对比各种分类。本系统设计了基础饼图(如图4.16所示)、圆角环形图(如图4.17所示)、南丁格尔玫瑰图(如图4.18所示)这种三种类型的饼图。这三种饼图的主要区别在于series里面radius属性的设置,基础饼图radius:‘50%’,设置饼状图大小。圆角环形图radius:[‘30%’,‘60%’],第一个百分数设置内圈大小,第二个百分数设置外圈大小。南丁格尔玫瑰图radius:[20,140],roseType:'radius'。饼图对数据的处理是封装了一个对象,这个对象里面包含了名字name和value,用来处理一维数据,初始化数据的将form.xOption赋值给name,把多选列的第一个数据列yOption[0]赋值给value。核心代码如下:lettemp=JSON.parse(JSON.stringify(series))lettemp=JSON.parse(JSON.stringify(series))temp.data=data.map(item=>{return{name:item[this.form.xOption],//返回X轴数据列value:item[this.yOption[0]]//返回Y轴多选列的第一个数据列}})this.options[this.form.type].series[0]=temp//将拷贝得数据赋值给series图4.16基础饼图图4.17圆角环形图图4.18南丁格尔玫瑰图4.3.5散点图散点图通常用于显示和比较数值,不仅可以显示趋势,还能显示数据集群的形状以及在数据云团中各数据点的关系。通过观察散点图上数据点的分布情况,我们可以推断出变量间的相关性。本系统实现了基础散点图如图4.19所示,从图中我们可以看到X轴数据和Y轴数据之间存在着负相关的关系。散点图数据的封装主要是对一个坐标数组进行处理。所以每次处理的时候map遍历xOption和yOption[0]赋值给tempdata,再把temp这个对象push到option.seriesd的data属性里面完成散点图数据的封装。核心代码如下:lettemp=JSON.parse(JSON.stringify(series))lettemp=JSON.parse(JSON.stringify(series))

temp.data=data.map(item=>[item[this.form.xOption],item[this.yOption[0]]])

this.options[this.form.type].series.push(temp)图4.19散点图4.3.6雷达图雷达图将数据的多个维度映射到多个坐标轴上,这些坐标轴起始于同一个圆心点,通常结束于圆周边缘,将同一组的点使用线连接起来[16]。雷达图现在常常用来分析人的性格特点,例如用户导入了一个人物性格指标的表格(见表4-1),系统为该表格生成的基础雷达图如图4.20所示。

在生成这个人物性格雷达图(见图4.20)的过程中,X轴为指标,具有分为家务能力、工作能力、社会交际能力、厨艺等级、身体素质、赚钱能力6个类型。Y轴为具体类型性格的指标,分别有爱宝宝型、爱自己型、爱父母型、爱朋友型、爱工作型、爱享乐型等6个类型,雷达图分别将每一种性格类型的6个指标上的数据点连接起来,形成多边形。从图4.20可以看出,深绿色多边形为爱享乐类型的性格,而蓝色多边形则为爱工作类型的性格。表4-1人物性格指标指标爱宝宝型爱自己型爱父母型爱朋友型爱工作型爱享乐型张三李四家务能力0.340.830.30.650.9工作能力0.310.390.250.720.34社会交际能力0.30.30.480.90.37厨艺等级0.50.280.850.150.43身体素质0.90.220.450.30.38赚钱能力0.250.32图4.20雷达图雷达图数据的封装将x轴的xOption数据赋值给radar.indicator里面的name属性,从而形成X轴维度。将输入的雷达图最大值赋值给max,形成雷达图的边界。再用map遍历数据列option给Y轴数据列赋值,形成不同的多边形。核心代码如下:lettemp=JSON.parse(JSON.stringify(series))lettemp=JSON.parse(JSON.stringify(series))

this.yOption.forEach(option=>{

this.options[this.form.type].radar.indicator=data.map(item=>{

return{

name:item[this.form.xOption],//雷达图X轴的多个维度

max:this.form.maxLada//雷达图的边界数值}})

temp.data.push({

value:data.map(item=>item[option]),//Y轴的多个数据列

name:option})})

this.options[this.form.type].series[0]=temp4.3.7词云图词云图是对文本中出现频率较高的关键词进行可视化,本系统实现了如图4.21所示的词云图。词云图与饼图数据封装一样,都是针对一维数据,map遍历之后将form.xOption里面的值赋值给series.data的name属性,将yOption[0]里面的值赋值给series.data里面的value属性。主要区别在于series.type,series.type=wordCloud是词云图,series.type=Pie是饼图,并且词云图需要对产生的文本用随机数函数生成颜色,核心代码如下:type:'wordCloud',type:'wordCloud',sizeRange:[15,80],rotationRange:[0,0],rotationStep:45,gridSize:8,//单词之间的间隔大小shape:'pentagon',//词云图形状,五角形textStyle:{normal:{

color:function(){//随机生成每个单词的颜色

return'rgb('+[

Math.round(Math.random()*255),

Math.round(Math.random()*255),

Math.round(Math.random()*255)

].join(',')+')'}}}

图4.21词云图4.3.8日历热力图日历热力图可以生动地展示某一年每一天的数据,例如每一天的步数或消费情况等。本系统实现的日历热力图如图4.22所示。日历热力图上的每一个小块代表一天,小块上的颜色深浅代表了这一天数值的大小。日历热力图的数据封装将用户输入的年份form.year进行计算,然后利用Echarts常用的时间计算函数getVirtulData(form.year)对时间进行处理。用map遍历yOption[0]里面的每一个数值赋值给temp,再把temp里面的数据赋值给日历热力图的series.data,把年份转化为字符串作为属性calendar.range。核心代码如下:letyear=parseInt(this.form.year)//letyear=parseInt(this.form.year)//用户输入的年份letdate=+echarts.number.parseDate(year+'-01-01');//日期格式化letend=+echarts.number.parseDate((+year+1)+'-01-01');//结束日期格式化letdayTime=3600*24*1000;lettemp=[];//中间数组letindex=0//计数for(lettime=date;time<end;time+=dayTime){temp.push([echarts.format.formatTime('yyyy-MM-dd',time),data.map(item=>item[this.yOption[0]])[index]]);//读取出每一天的数据index++}series.data=temp//赋值给series.datathis.options[this.form.type].series=seriesthis.options[this.form.type].calendar.range=year.toString()//将年份转化为字符串作为日历热力图里面的calend.range属性图4.22日历热力图4.3.9K线图K线图主要应用于金融领域展示股票、期货等交易数据,展示的数据需要满足K线构成的四个要素:开盘价、收盘价、最高价、最低价。红色方块表示上涨,绿色方块表示下跌。本系统实现了如图4.23所示的K线图。K线图的数据封装将xOption赋值给Option.xAxis.data,Y轴数据列必须是四列,所以将yOption[0]、yOption[1]、yOption[2]、yOption[3]赋值给series.data。核心代码如下:this.yOption.forEach(option=>{this.yOption.forEach(option=>{

lettemp=JSON.parse(JSON.stringify(series))

this.options[this.form.type].xAxis.data=data.map(item=>item[this.form.xOption])//X轴数据遍历

temp.data=data.map(item=>[item[this.yOption[0]],item[this.yOption[1]],item[this.yOption[2]],item[this.yOption[3]]])//K线图必须满足Y轴是四列数据

this.options[this.form.type].series.push(temp)图4.23K线图4.3.10漏斗图漏斗图常常用于流程流量的分析。通过将各个流程中数量的信息画入漏斗图可以清晰的分析到哪个环节是当前业务流程中的薄弱环节,哪个环节是流量转化的瓶颈,进而帮助人们更加专注于薄弱环节提高整个流程的产出。本系统设计了如图4.24所示的漏斗图。漏斗图的数据封装跟饼图相似,map遍历之后将form.xOption里面的值赋值给series.data的name属性,将yOption[0]里面的值赋值给series.data里面的value属性。主要区别在于series.type,series.type=funnel是漏斗图,series.type=pie是饼图。除此之外,漏斗图需要在series属性里面定义数据最大、最小值、数据排序方式等。核心代码如下:series:[series:[

{

name:'漏斗图',

type:'funnel',//类型

min:0,//指定的数据最小值,不设置时为0

max:100,//指定的数据最大值,默认为100。

minSize:'0%',//数据最小值min映射的宽度,默认为0%

maxSize:'100%',//数据最大值max映射的宽度,默认为100%。

sort:'descending',//排序,下降

gap:2//数据图形间距}]图4.24漏斗图4.3.11仪表盘仪表盘是一种物化的图表,可以直观的表示出某个指标的进度或者实际情况。本系统实现了如图4.25所示的仪表盘。仪表盘数据的封装与饼图相似,主要区别在于仪表盘的数据是一行的,而饼图多行。初始化数据的时候series.type=pie是饼图,series.type=gauge是仪表盘。图4.25仪表盘4.4图表管理4.4.1保存图表如图4.25所示,用户可以直接点击图表右上方的下载图标将图表进行保存。用户也可以点击“保存图表”按钮,系统将图表以图片的形式保存在已经创建的图表列表中。用户在导航栏中点击“已建图表”(如图4.26所示)能看到自己创建的所有图表。点击“保存图表”按钮后,触发onsave()事件,先使用echartsgetConnectedDataURL()获得图表的base64编码,然后通过dataURLtoBlob()、toBuffer()两个方法将base64编码转换为buffer。路径url:this.$http.adornUrl(`/ec/info/save`)与后端路径进行匹配,匹配成功,调用save()方法将这个图片保存在用户创建的图片记录里面。核心代码如下:

onSave(){

onSave(){

letbase64=this.myCharts.getConnectedDataURL({//获得图表的base64编码

pixelRatio:5,//导出的图片分辨率比率,默认是1

backgroundColor:'#fff',//图表背景色

excludeComponents:[//保存图表时忽略的工具组件,默认忽略工具栏

'toolbox'

]type:'png'//图片类型支持png和jpeg

});constobjectKey=`${newDate().getTime()}.png`

constblob=this.dataURLtoBlob(base64);//blob转arrayBuffer

constreader=newFileReader();

reader.readAsArrayBuffer(blob);//读取blob

reader.onload=event=>{

constbuffer=this.toBuffer(event.target.result);//arrayBuffer转Buffer

client.put(objectKey,buffer).then(res=>{

this.$http({

url:this.$http.adornUrl(`/ec/info/save`),//路径进行匹配

method:'post',

data:this.$http.adornData({

'name':||'新建图表',types':'自定义图表',

'datas':res.url,'status':'发布',

})})图4.26已建图表4.4.2删除图表@RequestMapping("/delete")@RequiresPermissions("ec:info:delete")//得到删除操作的指令publicRdelete(@RequestBodyInteger[]ids){//得到删除的那一条图表的id infoService.removeByIds(Arrays.asList(ids));//调用remove方法,通过id删除的该id下的图表记录returnR.ok();}如图4.26所示,用户可以点击“删除”按钮来删除图表。点击删除按钮后,触发deleteHandle事件,根据这个图表记录的@RequestMapping("/delete")@RequiresPermissions("ec:info:delete")//得到删除操作的指令publicRdelete(@RequestBodyInteger[]ids){//得到删除的那一条图表的id infoService.removeByIds(Arrays.asList(ids));//调用remove方法,通过id删除的该id下的图表记录returnR.ok();}4.5用户管理系统管理员可以对用户信息进行维护以及添加、删除、修改操作。管理员添加用户的代码与注册代码相似。下面主要介绍删除用户的功能,管理员点击“删除”按钮之后,系统询问是否要对该用户删除(见图4.27),点击“确定”按钮就能将这一条用户记录删除。点击删除按钮,触发deleteHandle事件,根据url:this.$http.adornUrl('/sys/user/delete')与后端/sys/user/delete路径进行匹配,匹配成功调用delete()方法完成删除功能,核心代码如下:@PostMapping("/delete")@PostMapping("/delete") @RequiresPermissions("sys:user:delete")//得到删除操作的指令 publicRdelete(@RequestBodyLong[]userIds){//得到用户的Id if(ArrayUtils.contains(userIds,1L)){ returnR.error("系统管理员不能删除");//不能删除管理员 } sysUserService.deleteBatch(userIds);//通过ID删除 returnR.ok(); }图4.27删除用户第5章系统测试系统测试是系统开发阶段的最后一个步骤,软件系统的开发质量很大程度上依靠软件测试来保证。系统测试的用例应尽量覆盖全面,尽可能多的使系统达到用户的需求。5.1功能测试功能测试也叫黑盒测试,只需考虑需要测试的各个功能,不需要考虑整个软件的内部结构及代码.从软件产品的界面、架构出发,按照需求编写出来的测试用例,输入数据在预期结果和实际结果之间进行评测,进而提出更加使产品达到用户使用的要求[17]。5.1.1注册登录模块测试用例(1)注册功能的主要测试用例参见表5-1。表5-1注册功能测试用例表测试用例测试结果说明用户名为空注册失败注册的用户名即登录的账号不能为空两次输入的密码不一致注册失败密码和确认密码必须一致输入邮箱账号只输入数字注册失败必须按照邮箱格式输入输入的手机号码少于11位注册失败手机号码的正确格式是11位注册输入已存在用户名注册失败不能注册已有账号输入没注册过的用户名并且输入密码和确认密码一致,手机和邮箱格式输入正确注册成功(2)登录功能的测试用例参见表5-2。表5-2登录功能测试用例表测试用例测试结果说明登录用户名为空登录失败用户名不能为空用户名正确,登录不输入密码登录失败登录密码不能为空用户名正确,登录输入错误密码登录失败输入的密码要正确用户名正确,密码正确,登录输入错误验证码登录失败输入的验证码要正确未注册,随便输入用户名和密码登录失败要注册后才能够登录用户名、密码、验证码都正确登录成功(3)修改密码功能的测试用例参见表5-3。表5-3修改密码功能测试用例表测试用例测试结果说明原密码输入为空修改失败原密码不能空原密码输入不正确修改失败要输入正确原密码原密码正确、新密码确认密码不一致修改失败新密码和确认密码要一致原密码正确、新密码和确认密码一致修改成功5.1.2新增图表模块测试用例(1)导入数据功能的测试用例参见表5-4。表5-4导入数据功能测试用例表测试用例测试结果说明导入的数据不是xls/xlsx后缀上传失败只能上传xls/xlsx文件上传的Excel表是空文件上传失败上传文件内容为空,要求重新上传上传的文件不是空文件并且文件后缀是xls/xlsx上传数据成功,并且在数据预览区域显示数据新增图表必须按照特定格式输入(2)可视化图表属性设置功能的测试用例参见表5-5。表5-5可视化图表属性设置功能测试用例表测试用例测试结果说明X轴和Y轴数据列相同,图表生成方式选择“正常”不能生成,有错误提示“X轴和Y轴不能一样”图表生成方式为“基础”,X轴和Y轴数据列不能相同X轴数据列选择文本,Y轴数据列选择数值,图表生成方式选择“正常”能生成图表X轴数据列选择文本,图表生成方式选择“单列统计”相同的文本计数,生成图表单列统计的图表生成方式适用于统计同一组中的相同元素的个数X轴数据列选择文本,Y轴数据列选择数值,图表生成方式选择“两列统计”Y轴上的数据的值相加,生成图表两列统计用于统计同一组中具有相同的数据,对名称列进行聚合,数据列进行累加

表5-5可视化图表属性设置功能用例表测试用例测试结果说明导入一个二维数据的气温表,X轴选择星期、Y轴选择最高气温和最低气温,生成方式选择“正常”,图表类型选择“基础折线图”能够正确生成基础折线图图,鼠标在上面能进行简单交互导入一个二维数据的气温表,X轴选择星期、Y轴选择最高气温和最低气温,生成方式选择“正常”,图表类型

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论