精华内容
下载资源
问答
  • QT 动态加载dll

    万次阅读 2020-04-28 09:03:26
    最近在调用卡尔的库函数时候,发现他们仅仅提供了一个dll,于是百度调研QT动态加载dll的方式,在调用成功后分析给大家。 动态调用。在使用的时候才会加载dll进来,使用QT本身自己的加载机制,利用QLibrary进行动态...

    最近在调用卡尔的库函数时候,发现他们仅仅提供了一个dll,于是百度调研QT动态加载dll的方式,在调用成功后分析给大家。

    动态调用。在使用的时候才会加载dll进来,使用QT本身自己的加载机制,利用QLibrary进行动态加载。
     

    typedef void (*CB_AnswerData)(const char *pBuf,int iLen);
    typedef int (*FunCardcallback)(CB_AnswerData );

    void cardInfoCallback(const char *pBuf,int iLen)
    {
    }

    void MainWindow::on_pushButton_3_clicked()
    {
        if(true)
        {
            QLibrary lib("KeUsbHid.dll");

            if(lib.load())
            {

                qDebug() << "load DLL succeed";

                //usb回调
                try
                {
                    FunUSBcallback usbBack=(FunUSBcallback)lib.resolve("_KERECEIVER_SetUSBStatusCallBack@4");
                   
                }
                catch (...)
                {
                    qDebug() << "usb   catch---------------------catch";
                }
            }
            else
            {

            }
        }
    }

    注意事项:

    (1)dll中的函数我们在调用过程中,需要在调用类中重新写一个带有形参个数和类型都一样的函数,就像本文的FunCardcallback,它在这里是作为回调函数使用的。

    (2)包含头文件#include "QLibrary"。

    (3)把调用的dll放在exe所在路径中。

    (4)分享一个小工具用于查看dll的函数原型,Stud_PE.exe。这个可以查看dll的导出表。

    展开全文
  • 访问控制第一层级:根据当前登录用户动态加载菜单。

    重要链接:
    「系列文章目录」

    「项目源码(GitHub)」

    前言

    果然不出大家所料,我又没能按时写完文章。时至今日我已经没有什么愧疚的感觉了,这大概就是成长吧。不过话又说回来了,曾经我也是一个月写 10 篇的男人啊。

    这篇文章的主要内容是实现按照用户角色动态加载后台管理页面的菜单,有如下几个重点:

    • 如何设计数据库以建立用户-角色-菜单之间的联系
    • 如何查询与处理树结构的数据
    • Vue 如何实现动态加载路由

    在正文之前,给大家讲讲这两周背后的故事:

    1、在官方活动 ——「CSDN 原力计划」 的加持下,我体验了一波飞速增长,读者数量翻了一倍。有那么几天我膨胀的不行,甚至一度想下定决心周更以早日达到破万的目标。后来我在房间里沉思良久,嚯完了一大杯快乐水,放弃了这个危险的念头。

    2、最近我发现咱们这个项目除了入门练习,还可以作为一些常见应用的 脚手架。比如图书查询与管理系统、个人博客(主页)、企业门户网站之类。上周我甚至在这个项目的基础上做了一个投票系统给公司内部举办活动用,虽然自己感觉稀碎,但忽悠不懂代码的同事足够了。

    为了让这个项目看起来正经一点,我对结构做了一些调整:

    • 把原来的开发的部分作为前台,并取消登录拦截
    • 登录后跳转到后台,在后台登出后跳转到前台首页
    • 去除图书馆页面增删改功能,之后会放在后台内容管理部分

    其实就是前端配置了下路由,修改了图书组件,后端取消了图书查询端口的拦截,相信大家自己能做到哈。之前有读者指出我一个地方漏掉了导入组件的代码,一开始我以为真漏了,后来想起来应该是特意没把整个组件复制上去,因为感觉没有太大必要,大家不要一复制代码就想没有错误地跑起来,不可能的,就是直接 clone 别人的仓库都会有各种各样的问题出现,出了问题百度就好啦。

    3、有读者给我发了十几封邮件,好几次贴了上几千行的错误信息,提问句式是:现在的问题是 XXXXX,然后就没了,连请我帮忙看看这样的话都没有,我好南啊(给我气笑了.jpg)

    不过会说话的读者也越来越多啦,我脸皮薄,很多时候还是不忍心拒绝你们的,这两周也为了回答问题花了不少时间。更有一些认真的读者发现了项目的 BUG 并指出了改正方法,我感到十分欣慰,希望老天爷多给我分配一些这样的读者。随着项目越来越复杂,难免会出现各种纰漏,欢迎大家从各个方面指出不足,也可以直接在 GitHub 上提 Issue 或 PR,咱们一起把这个东西做好。

    一、后端实现

    实现动态加载菜单功能的第一步,是完成根据当前用户查询出可访问菜单信息的接口。

    1.表设计

    基于之前讲过的 RBAC 原则,我们应该设计一张角色表,用角色去对应菜单。同时,为了建立用户与角色、角色与菜单之间的关系,又需要两张中间表。加上之前的用户表,一共需要五张表,各表字段如下图所示:
    表模型
    这里我为后台管理专属的表加上了 admin 前缀。起名是一件十分重要的事情,好的名字是保证代码质量的前提,你们不要学我起的这么随意,很多公司都有自己的起名原则,可以学习一下。(推荐 《阿里巴巴 Java 开发手册》)

    另外不同于之前 book 和 category 的做法,这里没有用到外键。一般决定用不用外键需要看系统对数据一致性和效率的要求哪个更突出,但是我觉得数据一致性问题都可以通过代码解决,用外键又麻烦又别扭。

    这里我简单介绍下 admin_menu 表的各个字段:

    字段 解释
    id 唯一标识
    path 与 Vue 路由中的 path 对应,即地址路径
    name 与 Vue 路由中的 name 属性对应
    name_zh 中文名称,用于渲染导航栏(菜单)界面
    icon_cls element 图标类名,用于渲染菜单名称前的小图标
    component 组件名,用于解析路由对应的组件
    parent_id 父节点 id,用于存储导航栏层级关系

    表中的数据你可以自己设计,为了方便测试,记得多注册一个账号以配置不同的角色,并为角色配置相应的菜单。嫌麻烦的话就直接执行我的 sql 文件:

    https://github.com/Antabot/White-Jotter/blob/master/wj/wj.sql

    里面有三个账号,admin、test 和 editor,密码都是 123,admin 的角色是系统管理员,editor 是内容管理员,test 是空的。

    2.pojo

    因为我们使用了 JPA 做 ORM,创建 POJO 时需要注意以下几点:

    P.S.其实过去我们创建的 POJO 应该再具体一点,称之为 PO(persistant object,持久对象)或者 Entity(实体),并使用 DTO(Data Transfer Object)与客户端进行交互。教程做了一些简化,源码添加了这些分类。

    • windows 下默认不区分 mysql 字段大小写,而 linux 区分,所以数据库字段不推荐大小写混用(最好都小写),而 Java 属性一般采用小驼峰法命名,JPA 会自动将小驼峰命名转换为下划线命名,比如 nameZh 自动转换为 name_zh
    • 数据库中不存在对应字段的属性,需要用 @Transient 注记标注出来

    我们需要创建 AdminUserRoleAdminRoleAdminRoleMenuAdminMenu 四个 PO,比较特殊的是 AdminMenu,这里我贴出来代码:

    package com.gm.wj.pojo;
    
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    
    import javax.persistence.*;
    import java.util.List;
    
    @Entity
    @Table(name = "admin_menu")
    @JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
    public class AdminMenu {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
        int id;
        String path;
        String name;
        String nameZh;
        String iconCls;
        String component;
        int parentId;
        @Transient
        List<AdminMenu> children;
    
    // getter and setter...
    
    

    与数据库中不同的是这个 Children 属性,用于存储子节点。

    3.菜单查询接口(树结构查询)

    根据用户查询出对应菜单的步骤是:

    • 利用 shiro 获取当前登录用户的 id
    • 根据用户 id 查询出该用户对应所有角色的 id
    • 根据这些角色的 id,查询出所有可访问的菜单项
    • 根据 parentId 把子菜单放进父菜单对象中,整理返回有正确层级关系的菜单数据

    为了实现这个接口,我们需要新增AdminUserRoleDAOAdminRoleMenuDAOAdminMenuDAO 三个数据库访问对象并编写 Service 对象,也可以顺道把 AdminRole 一套给写了,不过现在还用不上。

    AdminMenuService中需要实现一个根据当前用户查询出所有菜单项的方法:

    public List<AdminMenu> getMenusByCurrentUser() {
        // 从数据库中获取当前用户
        String username = SecurityUtils.getSubject().getPrincipal().toString();
        User user = userService.findByUsername(username);
    
        // 获得当前用户对应的所有角色的 id 列表
        List<Integer> rids = adminUserRoleService.listAllByUid(user.getId())
                .stream().map(AdminUserRole::getRid).collect(Collectors.toList());
    
        // 查询出这些角色对应的所有菜单项
        List<Integer> menuIds = adminRoleMenuService.findAllByRid(rids)
                .stream().map(AdminRoleMenu::getMid).collect(Collectors.toList());
        List<AdminMenu> menus = adminMenuDAO.findAllById(menuIds).stream().distinct().collect(Collectors.toList());
    
        // 处理菜单项的结构
        handleMenus(menus);
        return menus;
    }
    

    这个方法的主体部分对应上面的前三步。这里我们使用了 stream 来简化列表的处理,包括使用 map() 提取集合中的某一属性,通过 distinct() 对查询出的菜单项进行了去重操作,避免多角色情况下有冗余的菜单等。

    接下来需要把查询出来的菜单数据列表整合成具有层级关系的菜单树,也就是编写 handleMenus() 方法。

    这里我多说一嘴,由于导航菜单一般不会特别长,所以我们采用这种一次性取出的方式。上述过程中我们会在遍历列表的同时查询数据库,这样的多次交互在前台需要尽量避免,最好先一次性查询出全量数据以减轻服务器负担。但后台一般是给管理人员使用的,没有那么大的流量,所以不用担心。

    如果数据量特别大,那就应该考虑按节点动态加载。即通过监听节点的展开事件向后端发送节点 id 作为参数,查询出所有的子节点,并在前端动态渲染。这种方式的实现等以后用到了再具体讲。

    整合查询出的菜单数据的思路如下:

    • 遍历菜单项,根据每一项的 id 查询该项出所有的子项,并放进 children 属性
    • 剔除掉所有子项,只保留第一层的父项。比如 c 是 b 的子项,b 是 a 的子项,我们最后只要保留 a 就行,因为 a 包含了 b 和 c

    整合方法如下:

    public void handleMenus(List<AdminMenu> menus) {
    	for (AdminMenu menu : menus) {
    	    List<AdminMenu> children = getAllByParentId(menu.getId());
    	    menu.setChildren(children);
    	}
    	
    	Iterator<AdminMenu> iterator = menus.iterator();
    	while (iterator.hasNext()) {
    	    AdminMenu menu = iterator.next();
    	    if (menu.getParentId() != 0) {
    	        iterator.remove();
    	    }
    	}
    }
    

    先说明一下,查询树结构的方法有很多,我见过有按层级查询的,也有直接根据实际情况写死了的。我使用的这种方式好处就是无论几层都能正确地查询出来,虽然执行效率会低一些,但反正是后台用的,问题不大。

    有的同学可能对这个方法有疑问,似乎遍历一次应该只有两个层级才对。此外,menus 明明已经查询出来了,在遍历中每次仍然调用查询方法,是不是会频繁访问数据库,并创建额外的对象,增加不必要的开销?虽然是后台,这么写也太离谱了吧?

    (感谢 @m0_46435907 同学经过认真分析提供了这个问题的解答思路)

    这里可以放心,JPA 为我们提供了持久化上下文(Persistence Context,是一个实体的集合),用于确保相同的持久化对象只有一个实例,且在存在相应实例时不会再次访问数据库(详细内容见 「JPA Persistence Context」)。因此,我们查询到的 children 列表中的每一个 AdminMenu 对象实例都复用了 Menus 列表中的 AdminMenu 对象。

    同时,在 Java 里对象都是引用类型,假设我们把 b 放进了 a 的 children 里,又把 c 放进了 b 的 children 里,那么 c 就被放进了 a 的 children 的 children 里。因此,经过一次遍历,就能得到正确的层级关系。

    而下面的 remove 方法,实际上是把对象名指向了 null,而对象本身仍然存在。所以虽然我们无法再通过 b、c 获取到原来的对象,但 a 里面的信息是不会变的。

    为什么删除子项时用 iterator.remove() 而不用 List 的 remove 方法呢?是因为使用 List 遍历时,如果删除了某一个元素,后面的元素会补上来,也就是说后面元素的索引和列表长度都会发生改变。而循环仍然继续,循环的次数仍是最初的列表长度,这样既会漏掉一些元素,又会出现下标溢出,运行时表现就是会报 ConcurrentModificationException。而 iterator.remove() 进行了一些封装,会把当前索引和循环次数减 1,从而避免了这个问题。

    JDK 8 以上版本可以使用 lambda 表达式:

    public void handleMenus(List<AdminMenu> menus) {
        menus.forEach(m -> {
            List<AdminMenu> children = getAllByParentId(m.getId());
            m.setChildren(children);
        });
    
        menus.removeIf(m -> m.getParentId() != 0);
    }
    

    MenuController 中根据请求调用查询逻辑,代码如下:

     @GetMapping("/api/menu")
     public List<AdminMenu> menu() {
         return adminMenuService.getMenusByCurrentUser();
     }
    

    完成后可以测试下 menu 接口。在此之前要确保系统处于登录状态哈,要不可查询不到信息。

    menu 接口测试

    二、前端实现

    前端要做的事情就是处理后端传来的数据,并传递给路由和导航菜单,以实现动态渲染。

    1.后台页面设计

    我目前大概做了下面几个组件:
    后台组件
    主要是实现了页面基础设计,并方便测试动态加载,目前还没有任何功能。效果大概是这个样子:
    后台页面
    可以参考 GitHub 上的源码,也可以自己设计一下。开发完组件别忘了添加后台首页的路由哈,别的菜单对应的路由可以动态加载,这个不预先写好就进不去页面了。

    2.数据处理

    之前我们设计 AdminMenu 表,实际上包含了前端路由(router)与导航菜单需要的信息,从后台传来的数据,需要被整理成路由能够识别的格式。导航菜单倒是无所谓,赋给相应的属性就行。

    进行格式转换的方法如下:

    const formatRoutes = (routes) => {
      let fmtRoutes = []
      routes.forEach(route => {
        if (route.children) {
          route.children = formatRoutes(route.children)
        }
    
        let fmtRoute = {
          path: route.path,
          component: resolve => {
            require(['./components/admin/' + route.component + '.vue'], resolve)
          },
          name: route.name,
          nameZh: route.nameZh,
          iconCls: route.iconCls,
          children: route.children
        }
        fmtRoutes.push(fmtRoute)
      })
      return fmtRoutes
    }
    

    这里传入的参数 routes 代表我们从后端获取的菜单列表。遍历这个列表,首先判断一条菜单项是否含子项,如果含则进行递归处理。

    下面的语句就是把路由的属性与菜单项的属性对应起来,其它的都好说,主要是 component 这个属性是一个对象,因此需要根据名称做出解析(即获取对象引用)。同时我们需要把组件导入进来,因此可以利用 Vue 的异步组件加载机制(也叫懒加载),在解析的同时完成导入。

    我们数据库中存储的是组件相对 @components/admin 的路径,所以解析时要根据 js 文件的位置加上相应的前缀。

    3.添加路由与渲染菜单

    首先我们要思考一下什么时候需要去请求接口并渲染菜单。如果访问每个页面都加载一次,有点太浪费了。如果只在后台主页面渲染时加载一次,那么就不能在子页面中进行刷新操作。因此我们可以继续利用路由全局守卫,在用户已登录且访问以 /admin 开头的路径时请求菜单信息,完整的代码如下

    router.beforeEach((to, from, next) => {
        if (store.state.user.username && to.path.startsWith('/admin')) {
            initAdminMenu(router, store)
        }
        // 已登录状态下访问 login 页面直接跳转到后台首页
        if (store.state.username && to.path.startsWith('/login')) {
          next({
            path: 'admin/dashboard'
          })
        }
        if (to.meta.requireAuth) {
          if (store.state.user.username) {
            axios.get('/authentication').then(resp => {
              if (resp) next()
            })
          } else {
            next({
              path: 'login',
              query: {redirect: to.fullPath}
            })
          }
        } else {
          next()
        }
      }
    )
    

    为了保证用户确实登录,仍旧需要向后台发送一个验证请求。

    initAdminMenu 用于执行请求,调用格式化方法并向路由表中添加信息,代码如下:

    const initAdminMenu = (router, store) => {
      if (store.state.adminMenus.length > 0) {
        return
      }
      axios.get('/menu').then(resp => {
        if (resp && resp.status === 200) {
          var fmtRoutes = formatRoutes(resp.data)
          router.addRoutes(fmtRoutes)
          store.commit('initAdminMenu', fmtRoutes)
        }
      })
    }
    

    首先判断一下 store 里有没有菜单数据,如果有说明是正常跳转,无需重新加载。(第一次进入或进行刷新时需要重新加载)

    记得在 store.state 里添加变量 adminMenu: [],同时在 mutations 里添加如下方法:

     initAdminMenu (state, menus) {
       state.adminMenus = menus
     }
    

    这个 menus 就是上面的 fmtRoutes。当然也可以把数据放进 localStorage,记得登出时清空就好了。

    最后,我们来编写一下菜单组件 AdminMenu.vue

    <template>
      <div>
        <el-menu
          :default-active="'/admin/users'"
          class="el-menu-admin"
          router
          mode="vertical"
          background-color="#545c64"
          text-color="#fff"
          active-text-color="#ffd04b">
          <div style="height: 80px;"></div>
          <template v-for="(item,i) in adminMenus">
            <!--index 没有用但是必需字段且为 string -->
            <el-submenu :key="i" :index="i + ''" style="text-align: left">
              <span slot="title" style="font-size: 17px;">
                <i :class="item.iconCls"></i>
                {{item.nameZh}}
              </span>
              <el-menu-item v-for="child in item.children" :key="child.path" :index="child.path">
                <i :class="child.icon"></i>
                {{ child.nameZh }}
              </el-menu-item>
            </el-submenu>
          </template>
        </el-menu>
      </div>
    </template>
    
    <script>
        export default {
          name: 'AdminMenu',
          computed: {
            adminMenus () {
              return this.$store.state.adminMenus
            }
          }
        }
    </script>
    

    这里我们利用 element 的导航栏组件,进行两层循环,渲染出我们需要的菜单。<el-submenu> 代表一个有子菜单的菜单项,<el-menu-item> 则代表单独的菜单项。这么命名似乎有点毛病,又似乎没毛病。。。

    如果有三个层级,就是 <el-submenu><el-submenu> 再套 <el-menu-item> ,以此类推。

    终于大功告成了。我们来试试用 admin 账户登录,就是上面的效果,菜单是全的:
    Admin 菜单
    可以点击用户信息菜单,跳转到相应的路由并加载组件:
    组件测试

    使用 editor 账号登录,则只显示内容管理
    editor 菜单

    下一步

    这个页面做的比较着急,接下来计划按之前的设计完善各个模块,包括:

    • 开发用户角色、角色菜单分配组件
    • 迁移图书管理功能
    • 完成其它模块的基础界面
    • 实现功能级权限并开发分配组件

    下篇文章的重点是功能级权限的实现,其它方面会顺带提到,但不会说的太细,因为都是讲过的知识点。

    总算写完了,但感觉这篇文章还有些地方需要润色一下,大家有看不懂的地方可以尽情提,但像为什么粘了代码跑不起来这种问题我就直接忽略啦。

    另外这篇的内容参考了「 _江南一点雨 」,也就是松哥的实现思路,我最早就是跟着松哥的项目学的 Vue,咱们这个项目里前端很多的代码都是模仿松哥的「微人事」 项目写的,现在微人事已经 11.6k star 了,大家可以去学习一下,比我做的要完善许多。

    上一篇:Vue + Spring Boot 项目实战(十四):用户认证方案与完善的访问拦截

    下一篇:Vue + Spring Boot 项目实战(十六):功能级访问控制的实现

    展开全文
  • visual studio上C++库加载方式及方法:动态库静态加载、动态库动态加载、静态库加载 动态库静态加载 第一步:生成dll: 第二步:加载dll: 动态库动态加载 静态库加载 动态库静态加载 第一步:生成dll: 新建...

    visual studio上C++库加载方式及方法:动态库静态加载、动态库动态加载、静态库加载


    动态库静态加载

    第一步:生成dll:

    1. 新建vs项目
    2. 添加.h .cpp文件
    3. 设置vs项目属性
      1. 更改配置属性为动态态库,选择,项目->属性->常规->配置类型->动态库(dll)
      2. 添加编译宏,选择,“项目->属性->C/C++ ->预处理器->预处理器定义”中添加 APIEXPORT 宏
    4. 生成解决方案

    add.h文件内容

    #pragma once
    
    #ifdef APIEXPORT
    #define EXTERN_API extern "C" __declspec(dllexport)
    #else
    #define EXTERN_API extern "C" __declspec(dllimport)
    #endif
    
    EXTERN_API int add(int elm1, int elm2);
    

    add.cpp文件内容

    #include "add.h"
    
    int add(int elm1, int elm2)
    {
    	return elm1+ elm2;
    }
    

    第二步:加载dll:

    1. 新建vs项目
    2. 添加主程序入口函数.cpp文件(包含main函数的源文件)
    3. 添加第一步生成dll中的.h文件.dll文件.lib文件
      1. .h .lib可随意放置,使用时,指定正确的路径即可
      2. 放置.dll文件在和.exe文件同一目录下

    main.cpp文件内容

    //动态库静态加载
    #include <iostream>
    #include "add.h"//包含头文件
    #pragma comment(lib,"../Debug/libMathFunctions.lib")//指定.lib文件的位置
    int main()
    {
    	std::cout<<add(1, 2);
    	return 0;
    }
    

    动态库动态加载

    第一步:生成dll:

    此步骤参考动态库静态加载第一步:生成dll:

    第二步:加载dll:

    1. 新建vs项目
    2. 添加主程序入口函数.cpp文件(包含main函数的源文件)
      1. 使用Windows库函数加载dll
      2. LoadLibraryA 加载dll
      3. GetProcAddress 获取函数地址
      4. FreeLibrary 释放句柄
    3. 添加第一步生成dll中的.dll文件.lib文件
      1. .lib可随意放置,使用时,指定正确的路径即可
      2. 放置.dll文件在和.exe文件同一目录下

    main.cpp文件内容

    //动态库动态加载
    #include <iostream>
    #include <Windows.h>
    int main()
    {
    	HINSTANCE h = LoadLibraryA("libMathFunctions.dll");//第一步
    	typedef int(*FunPtr)(int a, int b);//定义函数指针
    	if (h == NULL)
    	{
    		FreeLibrary(h);
    		printf("load lib error\n");
    	}
    	else
    	{
    		FunPtr funPtr = (FunPtr)GetProcAddress(h, "add");//第二步
    		if (funPtr != NULL)
    		{
    			int result = funPtr(8, 3);
    			printf("8 + 3 =%d\n", result);
    		}
    		else
    		{
    			printf("get process error\n");
    			printf("%d", GetLastError());
    		}
    		FreeLibrary(h);//第三步
    	}
    	return 0;
    }
    

    静态库加载

    第一步:生成lib:

    1. 新建vs项目
    2. 添加.h .cpp文件
    3. 设置vs项目属性
      1. 更改配置属性为动态态库,选择,项目->属性->常规->配置类型->静态库(lib)
    4. 生成解决方案

    add.h文件

    #pragma once
    int add(int elm1, int elm2);
    

    add.cpp文件

    #include "add.h"
    int add(int elm1, int elm2)
    {
    	return elm1 + elm2;
    }
    

    第二步:加载dll:

    1. 新建vs项目
    2. 添加主程序入口函数.cpp文件(包含main函数的源文件)
    3. 添加第一步生成lib中的.lib文件.h文件
      1. .lib .h文件可随意放置,使用时,指定正确的路径即可

    main.cpp文件

    //静态库加载
    #include <iostream>
    #include "math/add.h"
    #pragma comment(lib,"../Debug/libMathFunctions.lib")
    int main()
    {
    	std::cout << add(1, 2);
    	return 0;
    }
    

    注意事项

    1. 动态库需要使用__declspec(dllexport)导出和 __declspec(dllimport)加载
    2. 静态库不需要__declspec(dllexport)和 __declspec(dllimport)

    展开全文
  • ECharts动态加载数据绘制折线图

    万次阅读 多人点赞 2019-02-19 15:28:08
    Echarts动态加载数据绘制折线图ECharts引入ECharts步骤连接数据接口,动态加载图表动态加载数据,整体代码 ECharts 纯Javascript的图表库,支持各种图表的绘制。 下载ECharts.js 引入ECharts 1.标签式单文件引入 &...

    ECharts

    纯Javascript的图表库,支持各种图表的绘制。
    下载ECharts.js

    引入ECharts

    1.标签式单文件引入

    <body>
        <div id="main" style="height:400px;"></div>
        //图表位置
        <script src="./js/echarts-all.js"></script>
        // 引入js文件
        <script>
            var myChart = echarts.init(document.getElementById('main'));
            var option = { }
            myChart.setOption(option);
        </script>
    </body>
    

    (这是较为简洁的使用方法)
    2.模块化引入文件

    <body>
        <div id="main" style="height:400px;"></div>
        ...
        <script src="./js/echarts.js"></script>
        <script type="text/javascript">
            require.config({
                paths: {
                    echarts: './js/dist'
                }
            });
            require(
                [
                    'echarts',
                    'echarts/chart/line',   // 按需加载所需图表,如需动态类型切换功能,别忘了同时加载相应图表
                    'echarts/chart/bar'
                ],
                function (ec) {
                    var myChart = ec.init(document.getElementById('main'));
                    var option = {
                        ...
                    }
                    myChart.setOption(option);
                }
            );
        </script>
    </body>
    

    步骤

    1.为 ECharts 准备一个具备大小(宽高)的 DOM 。
    定义一个待用的div,指定宽度、高度,设置id

     <div id="main" style="height:400px;"></div>
    

    2.引入echarts.js并加载

     <script src="./js/echarts.js"></script>
    

    3.基于准备好的dom,初始化echarts实例

     var myChart = echarts.init(document.getElementById('main'));
    

    4.指定图表的配置项和数据

     // 定义样式和数据
     
            var option = {
               
                  title: { //图表标题,可以通过show:true/false控制显示与否,subtext:'二级标题',
                    text: ''
                },
                backgroundColor: '#FFFFFF',
                
                tooltip : {//鼠标浮动时的工具条,显示鼠标所在区域的数据,trigger这个地方每种图有不同的设置
                    trigger: 'axis'
                },
                legend: {// 图例,每条折线或者项对应的示例
                    data:[]
                },
                calculable : true,
                xAxis : [
                    {
                        axisLabel:{
                            rotate: 30,
                            interval:0
                        },
                        axisLine:{
                            lineStyle :{
                                color: '#CCCCCC'
                            }
                        },
                        type : 'category',
                        boundaryGap : false,//从0刻度开始
                        // data:[]  X轴的定义
                        data : function (){
                            var list = [];
                            for (var i = 10; i <= 18; i++) {
                                if(i<= 12){
                                    list.push('2016-'+i + '-01');
                                }else{
                                    list.push('2017-'+(i-12) + '-01');
                                }
                            }
                            return list;
                        }()
                    }
                ],
                yAxis : [
                    {
    
                        type : 'value',
                        axisLine:{
                            lineStyle :{
                                color: '#CCCCCC'
                            }
                        }
                    }
                ],
                series : [
                    {
                        name:'新增用户',
                        type:'line',
                        // symbol:'none',//原点
                        smooth: 0.2,//弧度
                        color:['#66AEDE'],
                        // data:Y轴数据
                        data:[500,100,200,400,600,150,750,800,400,250,650,350]
                    },
                ]
            };
    

    5.使用刚指定的配置项和数据显示图表

    myChart.setOption(option);
    

    连接数据接口,动态加载图表

    使用ajax请求数据接口,获取图表数据,重新加载图表

    $.ajax({
                url:"user/userIncreaseList",
                type:'get',
                dataType:'json'
                success:function(jsons){
                    var Item = function(){
                        return {
                            name:'',
                            type:'line',
                            // itemStyle: {normal: {areaStyle: {type: 'default'}}},这为线条设置
                            label: {normal: {position: 'top'}},
                            markLine: {data: [{type: 'average', name: '平均值'}]},
                            data:[]
                        }
                    };// series中的每一项为一个item,所有的属性均可以在此处定义
                    var legends = [];// 准备存放图例数据
                    var Series = []; // 准备存放图表数据
                    var json = jsons.data;// 后台返回的json
                    for(var i=0;i < json.length;i++){
                        var it = new Item();
                        it.name = json[i].name;// 先将每一项填充数据
                        legends.push(json[i].name);// 将每一项的图例名称也放到图例的数组中
                        it.data = json[i].data;
                        Series.push(it);// 将item放在series中
                    }
    
                    // option.series.data=jsons.
                    option.xAxis[0].data = jsons.xcontent;// 设置X轴数据了
                    // 折线图可设置上下两个X轴,必须是option.xAxis[0].data = json.xcontent
                    option.legend.data = legends;// 设置图例
                    option.series = Series; // 设置图表
                    myChart.setOption(option);// 重新加载图表
                },
                error:function(){
                    alert("数据加载失败!请检查数据链接是否正确");
                }
            });
    

    动态加载数据,整体代码

    <body>
        <div id="main" style="height:400px;"></div>
        <script src="./js/echarts-all.js"></script>
        // 引入js文件
        <script>
            var myChart = echarts.init(document.getElementById('main'));
            
            //根据需求进行相应的设置
            var option = {
               
                  title: { //图表标题,可以通过show:true/false控制显示与否,subtext:'二级标题',
                    text: ''
                },
                backgroundColor: '#FFFFFF',
                tooltip : {//鼠标浮动时的工具条,显示鼠标所在区域的数据,trigger这个地方每种图有不同的设置
                    trigger: 'axis'
                },
                legend: {// 图例,每条折线或者项对应的示例
                    data:[]
                },
                calculable : true,
                xAxis : [ ],
                yAxis : [ ],
                series : [  ]
            };
    
    
    $.ajax({
                url:"user/userIncreaseList",
                type:'get',
                dataType:'json'
                success:function(jsons){
                    var Item = function(){
                        return {
                            name:'',
                            type:'line',
                            // itemStyle: {normal: {areaStyle: {type: 'default'}}},这为线条设置
                            label: {normal: {position: 'top'}},
                            markLine: {data: [{type: 'average', name: '平均值'}]},
                            data:[]
                        }
                    };// series中的每一项为一个item,所有的属性均可以在此处定义
                    var legends = [];// 准备存放图例数据
                    var Series = []; // 准备存放图表数据
                    var json = jsons.data;// 后台返回的json
                    for(var i=0;i < json.length;i++){
                        var it = new Item();
                        it.name = json[i].name;// 先将每一项填充数据
                        legends.push(json[i].name);// 将每一项的图例名称也放到图例的数组中
                        it.data = json[i].data;
                        Series.push(it);// 将item放在series中
                    }
    
                    // option.series.data=jsons.
                    option.xAxis[0].data = jsons.xcontent;// 设置X轴数据了
                    // 折线图可设置上下两个X轴,必须是option.xAxis[0].data = json.xcontent
                    option.legend.data = legends;// 设置图例
                    option.series = Series; // 设置图表
                    myChart.setOption(option);// 重新加载图表
                },
                error:function(){
                    alert("数据加载失败!请检查数据链接是否正确");
                }
            });
    
    
           // 初次加载图表
            myChart.setOption(option);
        </script>
    </body>
    

    折线图绘制

    在这里插入图片描述

    总结

    相关图表的属性设置,可以根据需求,查看官网上的相关API,进行设置。

    绘制多个图表的方法

    可以将绘制图表的方法单独写做一个方法,通过请求接口,获取数据之后,把数据格式调整为图表所需要的格式,再调用方法。

    echart官网
    https://echarts.baidu.com/index.html

    展开全文
  • 网上可以很轻松的找到动态加载资源、或者加载动态页面、或者加载静态资源的文章,但是找了好久都没找到将这几个功能整合起来的文章。于是决定自己写一个。 本文通过nodejs,实现真正像个服务器一样动态加载资源、...
  • 延迟加载和动态加载

    千次阅读 2015-11-04 13:33:50
    延迟加载是你在使用的使用加载,而动态加载是在程序运行时才知道类的名字,然后加载类。延迟加载可以应用在动态和非动态的加载上。 动态加载的例子 String className = ... // 例如:java.util.List List list = ...
  • 动态加载css

    万次阅读 2018-04-24 22:54:39
    动态加载CSS // 动态加载css var loadStyle = function(url) { var link = document.createElement('link'); link.rel = "stylesheet"; link.type = "text/css"; link.href = ...
  • 动态链接库之动态加载

    千次阅读 2019-01-21 14:22:51
    动态加载比静态加载难一些 这里是针对windows下的API来讨论动态加载   首先windows下的动态加载是靠三个函数来实现的 #include &lt;windows.h&gt; LoadLibrary(); GetProcAddress(); FreeLibrary();...
  • 85个web用动态加载

    千次下载 热门讨论 2013-09-22 16:37:45
    85个web用动态加载
  • java动态加载类和静态加载类

    千次阅读 2016-10-26 16:23:40
    什么是动态加载类 什么是静态加载类 Class.forName 不仅表示类的类类型,还代表了动态加载类。编译时加载是静态加载类, 运行时加载是动态加载类。 请大家区分编译 运行。 二.为何要使用动态加载类 我们写了一个程序...
  • Element-UI的表格动态加载数据

    万次阅读 2017-08-16 16:59:08
    Element-UI的表格动态加载数据
  • 动态库的动态加载和静态加载

    千次阅读 2016-07-19 09:18:21
    动态库的动态加载方式调用,里面定义的数据结构理论上只作为dll内部使用!在这种情况下,如果这个dll的导出函数参数有内部定义的数据类型,显然是违反低耦合性原则的,实际上这种情况下编写dll方应该提供.h、.lib和....
  • CocosCreator 动态加载与远程加载资源汇总 概述 所有需要通过 cc.loader.loadRes 动态加载的资源,都必须放置在 resources 文件夹或它的子文件夹下。如果一份资源仅仅是被 resources 中的其它资源所依赖,而不需要...
  • Python动态加载

    千次阅读 2016-07-28 13:12:09
    动态加载模块、类和方法
  • 关卡动态加载

    千次阅读 2016-12-05 10:36:31
    原文地址:入门(关卡动态加载)" style="text-decoration:none; color:rgb(62,115,160)">Unreal4 入门(关卡动态加载)作者:尹俊旭Siri  对于大型游戏MMORPG游戏或是3D街景(虚拟现实)还有大型无缝地图都需要...
  • Android动态加载入坑指南

    千次阅读 2016-11-13 11:55:33
    曾几何时,国内各大公司掀起了一股研究Android动态加载的技术,两年多过去了,动态加载技术俨然成了Android开发中必须掌握的技术。那么动态加载技术是什么呢,这里谈谈我的个人看法,如有雷同,纯属偶然。什么是动态...
  • 安卓动态加载布局

    千次阅读 2018-07-01 13:41:25
    但是把项目中动态加载布局这块学习了一下,把项目这块写了,然后还学了下如何动态添加item这块。 什么叫动态布局 这就是动态加载布局,根据我选择的卡片类型,创建不同的布局,加载到下面 动态布局就是动态加载...
  • Qt动态加载动态库

    千次阅读 2017-08-02 15:45:27
    Qt动态加载动态库 1、2017年8月2日15:44:16。 #include QLibrary lib("JutzeImageProc.dll"); if (lib.load()) { typedef int (*CalVolumeFunType)(const cv::Mat & , const cv::Mat & , double & , double...
  • QML之动态加载

    千次阅读 2017-11-19 22:05:43
    使用Loader动态加载组件QML中的Loader可用来动态加载QML组件,动态加载的作用: 1. 在需要使用该组件的时候才加载它(并不像visible属性会使控件一直存在); 2. 加载的组件可以销毁并释放资源。QML中控件的...
  • unity动态加载Resources.Load方法

    万次阅读 多人点赞 2016-04-08 09:46:25
    Resources.Load:使用这种方式加载资源,首先需要下Asset目录下创建一个名为Resources的文件夹,这个命名是U3D规定的方式,然后把资源文件放进去,当然也可以在Resources中再创建子文件夹,代码加载时需要添加相应的...
  • 1、先说明本文要实现的功能: (1)生成一个动态链接库; (2)隐式加载动态链接库,需要的文件:*.h(非必须文件,如果不使用*.h文件,那么调用程序所需要的函数或是类就需要...(3)使用QLibrary()动态加载,*....
  • DL动态加载框架技术文档

    万次阅读 多人点赞 2014-10-20 00:45:32
    DL动态加载框架技术文档DL技术交流群:29969245 1. Android apk动态加载机制的研究2. Android apk动态加载机制的研究(二):资源加载和activity生命周期管理3. APK动态加载框架DL解析4. Android 使用动态加载...
  • camel动态加载route

    千次阅读 2019-06-20 18:56:22
    camel的路由规则加载默认使用xml或java方法来创建,但这2个方法不能够动态加载路由,查看camel的源码发现,可以通过路由规则字符串来动态的加载。 假设需要加载的路由如下 <routes xmlns=...
  • APK动态加载框架(DL)解析

    万次阅读 多人点赞 2014-10-10 00:20:57
    好久没有发布新的文章,这次打算发表一下我这几个月的一个核心研究成果:APK动态加载框架(DL)。这段时间我致力于github的开源贡献,开源了2个比较有用且有意义的项目,一个是PinnedHeaderExpandableListView,另一...
  • 静态加载: 如果你有a.dll和a.lib,两个文件都有的话可以用静态加载的方式: message函数的声明你应该知道吧,把它的声明和下面的语句写到...如果dll没有对应的.lib文件,那么就只能使用动态加载的方式了。 动态调
  • Java动态加载

    万次阅读 2018-04-21 09:45:29
    在讲解动态加载类之前呢,我们先弄清楚为什么要动态加载类,静态加载不行吗?我们可以看下面的实例:我在文件夹里写了Office.java 类和 Word.java类,如下:Office.javaclass Office{ public static void main...
  • dll的动态加载,又称显式加载,特点有以下几点: 1:灵活,可以在需要的时候用LoadLibrary进行加载,在不需要的时候用FreeLibrary进行卸载,这样可以不必占用内存。 2:可以在没有dll时候发现,而不致程序报错。 ...
  • Android jni动态加载so

    千次阅读 2020-12-04 17:59:49
    说到动态加载so,优势就在于可以便捷的进行so的更新。下面说明下。 首先由于动态加载所以不能拷贝到外存储设备,所以最终是拷贝到**/data/data/PackageName/files**下面:(so是从assets文件下拷贝到/data/data/…)...
  • Unity 之 动态加载物体卡顿简析

    千次阅读 2019-07-12 21:04:29
    Unity 之 动态加载物体卡顿简析 Resources.Load Instantiate AddComponent 加载预制体,加载脚本卡顿
  • java 动态加载

    千次阅读 2017-02-23 18:42:35
    一、什么是动态加载动态加载:通过Class.forName的方式来得到一个Class类型的实例,然后通过这个Class类型的实例的newInstance来初始化,这种方法我们称之为动态加载程序在运行时调用相应方法,即使其他方法是错误...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 74,374
精华内容 29,749
关键字:

动态加载