精华内容
下载资源
问答
  • 如何识别图片表格数据

    万次阅读 2018-12-24 10:34:44
      很多时候,我们的数据来源形式是多种多样的,有时候数据(或表格)也会呈现图片。那么,我们如何来获取图片的有用数据呢?当一张图片含有表格数据的时候,我们可以用OpenCV识别表格中的直线,然后再用...

      在很多时候,我们的数据来源形式是多种多样的,有时候数据(或表格)也会呈现在图片中。那么,我们如何来获取图片中的有用数据呢?当一张图片中含有表格数据的时候,我们可以用OpenCV识别表格中的直线,然后再用OCR技术识别其中的文字。
      本文仅作为如何识别图片中的表格的一个例子,希望能给读者一些启示。笔者用到的工具如下:

    • opencv
    • pyteressact
    • numpy

    我们用opencv来识别表格中的直线,用pyteressact来识别单元格文字,用numpy做数值处理。我们要识别的示例图片(AI.png)如下:

    示例图片 AI.png

    我们分以下几步进行识别:

    1. 识别表格中的横线,即分割记录(每一行)的横线;
    2. 识别表格中的竖线,即每个列的分割线;
    3. 找到数据所在的单元格;
    4. 利用pyteressact识别单元格的文字。

    识别表格中的横线

      识别横线之前,我们先创建一个图片表格识别类(ImageTableOCR),如下:

    # -*- coding: utf-8 -*-
    import cv2
    import pytesseract
    import numpy as np
    
    class ImageTableOCR(object):
    
        # 初始化
        def __init__(self, ImagePath):
            # 读取图片
            self.image = cv2.imread(ImagePath, 1)
            # 把图片转换为灰度模式
            self.gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
    

    其中self.image为RGB模块的图片,self.gray为灰度模式的图片。
      接下来,我们识别图片中的分割两条记录的横线。注意到,相邻两条记录之间的颜色是不一致的,因此,我们利用图片灰度化后,每一行像素的平均值的差的绝对值来作为相邻两条记录的分割线,这样就能检测出分割两条记录的横线了。具体的识别横线的函数的Python代码如下:(接以上代码)

        # 横向直线检测
        def HorizontalLineDetect(self):
    
            # 图像二值化
            ret, thresh1 = cv2.threshold(self.gray, 240, 255, cv2.THRESH_BINARY)
            # 进行两次中值滤波
            blur = cv2.medianBlur(thresh1, 3)  # 模板大小3*3
            blur = cv2.medianBlur(blur, 3)  # 模板大小3*3
    
            h, w = self.gray.shape
    
            # 横向直线列表
            horizontal_lines = []
            for i in range(h - 1):
                # 找到两条记录的分隔线段,以相邻两行的平均像素差大于120为标准
                if abs(np.mean(blur[i, :]) - np.mean(blur[i + 1, :])) > 120:
                    # 在图像上绘制线段
                    horizontal_lines.append([0, i, w, i])
                    cv2.line(self.image, (0, i), (w, i), (0, 255, 0), 2)
    
            horizontal_lines = horizontal_lines[1:]
            # print(horizontal_lines)
            return horizontal_lines
    

    首先对图片进行二值化处理,再进行两次中值滤波,这样是为了使相邻两条记录之间的像素区别尽可能大。然后对该图片中的每一行的像素进行检测, 以相邻两行的平均像素差大于120为标准, 识别出分割两条记录的横线。识别后的横线如下:(图片中的绿色线段)

    识别横线后的图片

    识别表格中的竖线

      在这一步中,我们利用opencv中的Hough直线检测方法来检测图片中的竖线。完整的Python代码如下:(接以上代码)

        #  纵向直线检测
        def VerticalLineDetect(self):
            # Canny边缘检测
            edges = cv2.Canny(self.gray, 30, 240)
    
            # Hough直线检测
            minLineLength = 500
            maxLineGap = 30
            lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength, maxLineGap).tolist()
            lines.append([[13, 937, 13, 102]])
            lines.append([[756, 937, 756, 102]])
            sorted_lines = sorted(lines, key=lambda x: x[0])
    
            # 纵向直线列表
            vertical_lines = []
            for line in sorted_lines:
                for x1, y1, x2, y2 in line:
                    # 在图片上绘制纵向直线
                    if x1 == x2:
                        print(line)
                        vertical_lines.append((x1, y1, x2, y2))
                        cv2.line(self.image, (x1, y1), (x2, y2), (0, 0, 255), 2)
    
            return vertical_lines
    

    首先我们对灰度图片进行Canny边缘检测,在此基础上再利用Hough直线检测方法识别图片中的直线,要求识别的最大间距为30,线段长度最小为500,并且为竖直直线(x1 == x2),当然,也有一些人为的因素,那就是笔者自己添加了两条竖直直线([[13, 937, 13, 102]],[[756, 937, 756, 102]])。运行上述方法,输出的结果如下:

    [[13, 937, 13, 102]]
    [[75, 937, 75, 102]]
    [[77, 937, 77, 102]]
    [[270, 937, 270, 104]]
    [[272, 937, 272, 102]]
    [[756, 937, 756, 102]]

    识别竖直直线后的图片如下:(图片中的红色线段)

    识别竖线后的图片

    可以看到,图片六条竖直的线段都已经完整标记出来了。

    识别图片中的单元格

      在识别图片中的单元格之前,我们先来识别每个单元格所在的顶点,也就是上述识别后的横线与竖线的交点。完整的Python代码如下:(接以上代码)

        # 顶点检测
        def VertexDetect(self):
            vertical_lines = self.VerticalLineDetect()
            horizontal_lines = self.HorizontalLineDetect()
    
            # 顶点列表
            vertex = []
            for v_line in vertical_lines:
                for h_line in horizontal_lines:
                    vertex.append((v_line[0], h_line[1]))
    
            #print(vertex)
    
            # 绘制顶点
            for point in vertex:
                cv2.circle(self.image, point, 1, (255, 0, 0), 2)
    
            return vertex
    

    顶点检测后的图片如下:(图片中的蓝色点即为每个单元格的顶点)

    顶点检测后的图片

    由此可见,我们识别出来的单元格的顶点是正确的。接着,我们把这些单元格取出来,代码如下:(接以上代码)

    # 寻找单元格区域
        def CellDetect(self):
            vertical_lines = self.VerticalLineDetect()
            horizontal_lines = self.HorizontalLineDetect()
    
            # 顶点列表
            rects = []
            for i in range(0, len(vertical_lines) - 1, 2):
                for j in range(len(horizontal_lines) - 1):
                    rects.append((vertical_lines[i][0], horizontal_lines[j][1], \
                                  vertical_lines[i + 1][0], horizontal_lines[j + 1][1]))
    
            # print(rects)
            return rects
    

    以第一个单元格为例,其图像如下:

    第一个单元格的图片

    识别单元格的文字

      在识别出图片中表格的单元格后,我们可以对该单元格图片进行文字识别,我们使用的OCR工具为Teressact, 其Python的接口为pyteressact 。具体的Python代码如下:(接以上代码)

    # 识别单元格中的文字
        def OCR(self):
            rects = self.CellDetect()
            thresh = self.gray
    
            # 特殊字符列表
            special_char_list = ' `~!@#$%^&*()-_=+[]{}|\\;:‘’,。《》/?ˇ'
            for i in range(20):
                rect1 = rects[i]
                DetectImage1 = thresh[rect1[1]:rect1[3], rect1[0]:rect1[2]]
    
                # Tesseract所在的路径
                pytesseract.pytesseract.tesseract_cmd = 'C://Program Files (x86)/Tesseract-OCR/tesseract.exe'
                # 识别数字(每行第一列)
                text1 = pytesseract.image_to_string(DetectImage1, config="--psm 10")
                print(text1, end='-->')
    
                # 识别汉字(每行第二列)
                rect2 = rects[i+20]
                DetectImage2 = thresh[rect2[1]:rect2[3], rect2[0]:rect2[2]]
                text2 = pytesseract.image_to_string(DetectImage2, config='--psm 7', lang='chi_sim')
                text2 = ''.join([char for char in text2 if char not in special_char_list])
                print(text2, end='-->')
    
                # 识别汉字(每行第三列)
                rect3 = rects[i+40]
                DetectImage3 = thresh[rect3[1]:rect3[3], rect3[0]:rect3[2]]
                text3 = pytesseract.image_to_string(DetectImage3, config='--psm 7', lang='chi_sim')
                text3 = ''.join([char for char in text3 if char not in special_char_list])
                print(text3)
    

    识别后的结果如下:

    I–>度一–>开放的人一智能服务平台
    2–>肌讯–>互联网综合服务
    3–>标为–>人一智能自动化业务、智能屹片
    4–>阿里巴巴–>互联网综合服务
    5–>平安集口–>人T智能金融研发平仄
    6–>华大基因–>精准检测、医疗数据运营服务
    d–>搜狗–>综合人T智能解决方案平台
    8–>一科大讯飞–>智能语音技术
    9–>一中利创汤–>智能终端平台技术
    10–>珍山集团–>SaaS级智能营销云平台
    i–>商汤科技–>人工智能视觉深度学习平台
    12–>神州泰岳–>综合类软件产品及服务
    13–>寒武红科技–>深度学对专用的智能盂片
    14–>汉王科技–>文字识别技术与智能交工
    15–>全志刑技–>智能芯片设计
    16–>face旷视科技–>人T智能产品和行业解夷方案
    17–>创略科技–>智能客户数据平台
    18–>海云数据–>企业级大数据整体运营与分析服务
    19–>影渭科技–>视觉技术、智能影像生产企业
    20–>智蹈智能–>智能机器人技术提供和平台运萧

    下面,我们来统计一下识别的准确率。在原来的表格中,一共是20个数字加上280个汉字(包括标点)加上10个英语字母(包括标点),对于识别的结果,其中数字类识别正确17个,汉字正确256个,英语字母8个,因此,总的识别的准确率为90.6% ,识别的结果还是可以的。

    总结

      本文仅作为如何识别图片中的表格的一个例子,希望能给读者一些启示。对于不同的图片表格,需要具体问题具体分析。
      虽然笔者尽可能把整个过程写得简单明了,但其中的探索过程却是很复杂的。而且值得注意的是,在本文中的检测横线的方法仅适用于相邻两条记录有颜色差别的表格图片。
      完整的Python代码如下,希望能给大家一些思考。

    # -*- coding: utf-8 -*-
    import cv2
    import pytesseract
    import numpy as np
    
    class ImageTableOCR(object):
    
        # 初始化
        def __init__(self, ImagePath):
            # 读取图片
            self.image = cv2.imread(ImagePath, 1)
            # 把图片转换为灰度模式
            self.gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
    
        # 横向直线检测
        def HorizontalLineDetect(self):
    
            # 图像二值化
            ret, thresh1 = cv2.threshold(self.gray, 240, 255, cv2.THRESH_BINARY)
            # 进行两次中值滤波
            blur = cv2.medianBlur(thresh1, 3)  # 模板大小3*3
            blur = cv2.medianBlur(blur, 3)  # 模板大小3*3
    
            h, w = self.gray.shape
    
            # 横向直线列表
            horizontal_lines = []
            for i in range(h - 1):
                # 找到两条记录的分隔线段,以相邻两行的平均像素差大于120为标准
                if abs(np.mean(blur[i, :]) - np.mean(blur[i + 1, :])) > 120:
                    # 在图像上绘制线段
                    horizontal_lines.append([0, i, w, i])
                    # cv2.line(self.image, (0, i), (w, i), (0, 255, 0), 2)
    
            horizontal_lines = horizontal_lines[1:]
            # print(horizontal_lines)
            return horizontal_lines
    
        #  纵向直线检测
        def VerticalLineDetect(self):
            # Canny边缘检测
            edges = cv2.Canny(self.gray, 30, 240)
    
            # Hough直线检测
            minLineLength = 500
            maxLineGap = 30
            lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength, maxLineGap).tolist()
            lines.append([[13, 937, 13, 102]])
            lines.append([[756, 937, 756, 102]])
            sorted_lines = sorted(lines, key=lambda x: x[0])
    
            # 纵向直线列表
            vertical_lines = []
            for line in sorted_lines:
                for x1, y1, x2, y2 in line:
                    # 在图片上绘制纵向直线
                    if x1 == x2:
                        # print(line)
                        vertical_lines.append((x1, y1, x2, y2))
                        # cv2.line(self.image, (x1, y1), (x2, y2), (0, 0, 255), 2)
    
            return vertical_lines
    
        # 顶点检测
        def VertexDetect(self):
            vertical_lines = self.VerticalLineDetect()
            horizontal_lines = self.HorizontalLineDetect()
    
            # 顶点列表
            vertex = []
            for v_line in vertical_lines:
                for h_line in horizontal_lines:
                    vertex.append((v_line[0], h_line[1]))
    
            #print(vertex)
    
            # 绘制顶点
            for point in vertex:
                cv2.circle(self.image, point, 1, (255, 0, 0), 2)
    
            return vertex
    
        # 寻找单元格区域
        def CellDetect(self):
            vertical_lines = self.VerticalLineDetect()
            horizontal_lines = self.HorizontalLineDetect()
    
            # 顶点列表
            rects = []
            for i in range(0, len(vertical_lines) - 1, 2):
                for j in range(len(horizontal_lines) - 1):
                    rects.append((vertical_lines[i][0], horizontal_lines[j][1], \
                                  vertical_lines[i + 1][0], horizontal_lines[j + 1][1]))
    
            # print(rects)
            return rects
    
        # 识别单元格中的文字
        def OCR(self):
            rects = self.CellDetect()
            thresh = self.gray
    
            # 特殊字符列表
            special_char_list = ' `~!@#$%^&*()-_=+[]{}|\\;:‘’,。《》/?ˇ'
            for i in range(20):
                rect1 = rects[i]
                DetectImage1 = thresh[rect1[1]:rect1[3], rect1[0]:rect1[2]]
    
                # Tesseract所在的路径
                pytesseract.pytesseract.tesseract_cmd = 'C://Program Files (x86)/Tesseract-OCR/tesseract.exe'
                # 识别数字(每行第一列)
                text1 = pytesseract.image_to_string(DetectImage1, config="--psm 10")
                print(text1, end='-->')
    
                # 识别汉字(每行第二列)
                rect2 = rects[i+20]
                DetectImage2 = thresh[rect2[1]:rect2[3], rect2[0]:rect2[2]]
                text2 = pytesseract.image_to_string(DetectImage2, config='--psm 7', lang='chi_sim')
                text2 = ''.join([char for char in text2 if char not in special_char_list])
                print(text2, end='-->')
    
                # 识别汉字(每行第三列)
                rect3 = rects[i+40]
                DetectImage3 = thresh[rect3[1]:rect3[3], rect3[0]:rect3[2]]
                text3 = pytesseract.image_to_string(DetectImage3, config='--psm 7', lang='chi_sim')
                text3 = ''.join([char for char in text3 if char not in special_char_list])
                print(text3)
    
        # 显示图像
        def ShowImage(self):
            cv2.imshow('AI', self.image)
            cv2.waitKey(0)
            # cv2.imwrite('E://Horizontal.png', self.image)
    
    ImagePath = 'E://AI.png'
    imageOCR = ImageTableOCR(ImagePath)
    imageOCR.OCR()
    

    注意:本人现已开通微信公众号: Python爬虫与算法(微信号为:easy_web_scrape), 欢迎大家关注哦~~

    展开全文
  • 表格数据变更,一般包括几个内容:新增、修改、删除、移动,开发经常会面临的一个问题就是变更之后如何将数据同步到节点上,一直以来个人的建议还是利用表格重载,不管是url模式的还是data模式的实际都是需要重载...

    参考链接

    https://www.html.cn/framework/layui/15886.html
    

    表格数据变更,一般包括几个内容:新增、修改、删除、移动,开发中经常会面临的一个问题就是变更之后如何将数据同步到节点上,一直以来个人的建议还是利用表格重载,不管是url模式的还是data模式的实际都是需要重载,url重载自然会重新请求后台得到最新的数据,data模式一般就是对data的操作,之后重新以新的data去渲染出来。

    同时会考虑的是如何尽量减少请求,可能感受最深的就是update操作,为了要更新这一条记录而重载整个表格,请求一遍数据感觉划不来,那么一般来说就可以利用表格的tool事件中的obj.update这个方法去更新。

    不过具体使用中就会发现其诸多的不足的地方,本文就是主要针对这些不足进行一个处理给出一个tablePlug.update的方法,然后进而衍生出add和remove和move,同时新增了更新统计行数据的方法。

    测试页面: 综合测试页面 流加载表格测试页面 定时刷新表格测试页面

    一、update

    正如上面说的obj.update(data)有诸多限制,优点上来说就是用最小的修改代价,实现了数据的更新,他就更新参数中的data中的键的数据,不会整个行更不会整个table的节点更新;缺陷是底层的实现逻辑有点问题:

    1、是通过遍历data,更新缓存cache中对应的记录的key的value,然后根据cols的配置信息更新td的内容,但是如果是想要更新toolbar列的话就没戏,目前解析的只有templet的,所以如果想要更新toolbar的话基本就只能设置成templet,而且要给这个列添加一个field,才有理论上的可能。

    2、toolbar列即使加了field改成templet也未必能更新过来,因为内部的实现逻辑是先判断原始的data是否有这个key,所以如果field命名是原始的data里面没有的,后面用obj.update也更新不进去的,这个是一个比较大的限制。

    因为拿我们项目来说,后台给我们的数据如果原始的记录里面没有这个key的值他不会给一个key: ''的,那么后面要想利用obj.update这个key就变得不可能,除非利用parseData在渲染之前对后台给的数据做一个人工的初始化把对应的key添加上,但是可想而知有多麻烦。

    3、数据他是一个一个更新进去,然后更新一个值就更新对应的td,但是这个就存在另外一个风险了,就是遍历对象他是无序的,比如update{a: 1, b:2},如果a字段的cols中会用到b字段的值做一个处理再显示出来。

    那么如果遍历顺序是先更新a的值,然后就开始更新a的td的内容,这个时候cache中b的值还是旧的不是你要更新进去的2,等到更新了b字段了他又不能说检测到其他字段有使用了这个字段会去再次更新对方的内容,这就导致了a出来的结果还是错的。

    4、更新了统计列的某一个值统计行的对应数据没有重新计算。

    总结的来说就是,obj.update实现的还是太过理想化太过简单,一条记录从数据上来说每个key是独立的这个没什么问题,但是到页面显示就不然了,因为页面的内容它不一直是单个字段的简单值显示,还会进行一些特殊处理。

    所以需要一个templet来转化,来自定义,所以有可能一个td里面会用到多个字段这个很正常,工具列的按钮也会根据数据的状态去决定部分按钮是否显示等等。

    所以个人认为要更新这个数据不能是一个独立的小单元的更新,而是先update这一行的数据然后在update这一行,而不是遍历被update的key一个个更新,再往大了看,实际这个表的记录也是一个整体。

    也是不能说你改了这条记录其他的记录必定是不变的,不排除某个字段的td他会根据当前页面的同一个field做了什么处理现实,比如统计行,所以目前的思路就是直接将值先update到cache中,然后再调用table内部的渲染tr td的内容。

    大致的代码:

    1579059007896296.png

    前面是针对参数做了一些处理让参数更加灵活,最关键的是后半部分的更新cache的部分,还要一个最关键的renderData的方法:

    1579059017111371.png

    他的作用就是将cache中的数据重新解析渲染一遍,同时针对是否是移动数据还有默认点击那一条记录的处理,但是核心是渲染cache,调用table.js内部的renderData。

    使用场景:

    1、知道当前编辑修改的是那一条记录,可以看看一个最常用的场景就是点击编辑弹出一个form然后修改提交,完成之后希望尽量不要重新请求接口更新到data和页面中去,

    gif很不好录,自己使用测试的例子里面的编辑按钮测试效果即可

    调用的更新数据的形式是:

    tablePlug.update(表格实例的id, 当前tr的index, newData)
    

    1579059027272235.png

    2、不知道当前的trIndex的情况下update某一条记录的话,必须有一个限制就是必须是有主键的表格,并且更新的数据中必须包含主键的字段,不然你也不知道更新的到底是哪条记录。

    tablePlug.update('demo', {id: 10002, username: '贤心'});
    

    3、一次性更新多条记录,这个参数trIndex就没有意义了,加了也没用,因为是更新多条记录,所以可以这么写

    tablePlug.update('demo', [{id: 10002, username: '贤心'}, {id: 10006, username: '小偷', age: 18, sign: '大叔'}]);
    

    这个测试页面可以看看头部toolbar中的“积分清零”还有“女性积分加100”这两个测试按钮以及背后的事件执行的方法

    4.png

    4、更加任性的,只要传入一个tableId,update会将当前按照cache中的数据给渲染一次,这个是非常实用的,比如如果你觉得我update中的逻辑针参数对cache的修改的逻辑不满意可以自己用自己觉得更好的方法去处理cache,最后执行一下tablePlug.update(‘demo’)就好了,提供更高的自由度,和拓展的可能性。

    二、addData

    addData添加的记录是已经请求接口完成返回的数据记录,本质上来说就是不一样的,所以不要混淆。

    具体addData的代码:

    1579059045223457.png

    data模式的话,实际也是往data里面添加一些记录,然后也是再reload一下。

    // 添加单条记录:

    tablePlug.addData ('demo', {id: newId, username: '甲'});
    

    // 添加多条记录

    tablePlug.addData ('demo', [{id: newId1, username: '乙'},{id: newId2, username: '丙'}]);
    

    关于addData的有一个比较综合的例子可以看看利用table的data模式怎么跟流加载配合使用,弄成一个流加载的表格

    https://sun_zoro.gitee.io/layuitableplug/testTableFlow
    

    三、del

    新增和删除实际个人建议还是reload比较稳妥,不管是url还是data模式都是,所以删除对应的处理方式也跟新增实际差不多,只不过删除麻烦一点的就是data模式要在原始的记录里面去删除指定的记录。

    而且有可能开启了复选的状态记忆删除了就要将关于他的状态给调整一下;还是为了使用更方便,参数同样做了处理,

    1、删除指定的下标的数据,可以查看表格行的toolbar中的删除按钮的监听处理,但是注意,如果表格是url的模式,目前测试页面写的都是json文件,所以reload也不会有效果的。

    所以要测试请在data模式的测试,不用纠结这个,url的如果是实际的服务接口的话是后台返回数据,一般删除成功了后面查询是不会再出来的,除非后台接口有问题。

    6.png

    2,删除指定的一些记录,这个一般有两种形式,但是要求一样就是必须是有主键的表格

    // id集合

    tablePlug.del('demo', [1,2,3,4]);
    

    // 对象数组

    tablePlug.del('demo', [{id: 1, name: 'name1'}, {id:2}, {id:4}]);
    

    根据得到哪种数据比较方便就用哪种形式,可以参考测试页面的批量删除的处理方式

    1579059068662686.png

    四、move

    这个处理基本跟update差不多,将数据在cache中调整位置,然后调用一下组件内部的renderData的方法让他重新渲染出来就好

    8.png

    然后为了使用方便衍生出来一个上移跟下移的方法

    9.png

    效果

    10.gif

    理论上利用一些拖拽事件或者其他的插件在事件中调用一下tablePlug.move(‘demo’, form, to);就能够实现顺序的任意改变了

    限制:注意!这个只是针对数据移动,不会有单条数据记录的变动,如果原始的数据里面有点击了排序,那么移动之后默认是会去掉这个排序的状态了的,因为移动之后很可能就不能满足当前的排序规则了,所以建议在使用移动的时候不要跟sort搭配,如果有sort而且所谓的移动是会发起请求改变数据的,那么这个建议还是使用请求接口得到两个新的数据然后用update去更新他们的位置。

    五、renderTotal

    在记录更新之后,如果存在统计行有需要统计的列,那么值一般也要跟着变,另外一个更加重要的作用就是可以自定义统计规则,而不是自带的求和,可以自定一定计算的函数,或者可以直接类似templet一样的去自定义返回的内容,包括异步的去读取想要显示的数据。

    代码大概如下:

    11.png

    从实现代码可以看出就是给cols的字段配置新增一个totalFormat的设置,可以设置一个规则如果不设置的话就是sum(目前也只是添加了sum,其他的规则后面会加入或者自己加入,比如平均。

    最大最小不过个人觉得主要意义是可以自定义方法,这个才是实用常用的),也可以设置一个方法,如果不是异步的可以直接把结果返回,如果是需要异步的那么也可以在得到最终想要的结果的时候执行:

    tablePlug.renderTotal(tableId, field, res);
    

    比如下面的:

    12.png

    平时实用的话不是都要自己去调用的,在插件内部已经在renderDone回调里面会去执行他了:

    13.png

    参数也是比较自由,不同的组合会有不同的效果,

    // 触发更新某个表格的所有列的统计数据

    renderTotal(tableId);
    

    // 触发更新某个表格的某个字段的数据

    renderTotal(tableId, fieldName);
    

    // 更新某个表格的某个字段的统计数据为value

    renderTotal(tableId, fieldName, totalValue);
    

    六、refresh

    之前做过一个智能reload的修改,即在执行table.reload的时候会根据传过去的option来判断只是重新请求数据还是需要重载一下,个人觉得效果可以了。

    不过对于有强迫症(有追求)的小伙伴来说,在一些场景下还是不够好,就是那些定时刷新的,表现就是一方面滚动条会回到top:0,left:0,还有其他的比如鼠标在操作分页组件的时候会觉得失去焦点,新增一个tablePlug.refresh来试一试能否满足要求。

    先看效果:

    14.gif

    事件背后做的事情:

    15.png

    表格config:

    16.png

    背后的实现思路

    17.png

    修改table的Class.prototype.pullData支持refresh模式

    renderData的时候根据是否refresh去做一些细节的处理,还有一个限定就是返回的数据中关于总数应该是不变的,如果发生了改变,那么还是会renderData,会重新渲染page组件。

    另外一个限制就是这种refresh的表格不建议再加什么按钮呀edit呀,因为它一直会在变,基本主要就是用来做一个单纯用来显示用的表格,比如一些经常变化的数据,访问人次,股票动态之类的。

    使用:

    // 启动表格demo的自动刷新功能,500毫秒一次
    
    tablePlug.refresh('demo', 500);
    
    // 取消表格demo的自动刷新
    
    tablePlug.refresh('demo', false);
    
    // 停止所有已开启自动刷新的表格的自动刷新
    
    tablePlug.refresh(false);
    
    展开全文
  • 后台分页使用pagehelper-可github上找到,dao使用mybatis,并结合spring和springmvc一起使用。详细代码见这里 简单介绍下后台代码 //**1.mybatis mapper.xml文件,一个简单查询**<?xml version="...

    说明:整个项目使用了maven管理,服务器使用jetty.后台分页使用pagehelper-可在github上找到,dao使用mybatis,并结合spring和springmvc一起使用。

    1. 简单介绍下后台代码
    //**1.mybatis mapper.xml文件,一个简单查询**
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.jel.tech.dao.DeptDao">
    
        <resultMap type="Dept" id="deptMap">
            <id column="dept_id"  property="deptId"/>
            <result property="deptName" column="dept_name" />
            <result property="parentId" column="parent_id" />
            <result property="icon" column="icon" />
            <result property="rank" column="rank" />
        </resultMap> 
    
        <select id="queryDeptByName" resultMap="deptMap" parameterType="java.util.Map">
            select * from t_dept
            <where>
                <if test="deptName != null">
                    dept_name LIKE CONCAT('%',#{deptName},'%')
                </if>
            </where>
        </select>
    </mapper> 
    
    //**2.DeptDao.java代码,service层代码省略,你懂的!**
    
    package com.jel.tech.dao;
    
    import java.util.List;
    import java.util.Map;
    import com.jel.tech.model.Dept;
    
    public interface DeptDao {
        public List<Dept> queryDeptByName(Map<String, Object> map);
    }
    
    
    //**3.mybatis-config.xml,注意pagehelper配置**
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!-- 别名 -->
        <typeAliases>
            <package name="com.jel.tech.model"/>
        </typeAliases>
    
        <plugins>
          <plugin interceptor="com.github.pagehelper.PageHelper">
            <!-- 4.0.0以后版本可以不设置该参数 -->
            <property name="dialect" value="mysql"/>
            <!-- 
                    当该参数设置为`true`时,如果`pageSize=0`或者`RowBounds.limit = 0`
                    就会查询出全部的结果(相当于没有执行分页查询,
                    但是返回结果仍然是`Page`类型)
                 -->
            <property name="pageSizeZero" value="true"/>
            <property name="reasonable" value="true"/>
          </plugin>
        </plugins>
    </configuration>
    
    //**4.controller层代码**
    
    package com.jel.tech.controller;
    
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.lang3.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import com.github.pagehelper.PageHelper;
    import com.github.pagehelper.PageInfo;
    import com.jel.tech.common.json.JsonUtils;
    import com.jel.tech.model.Dept;
    import com.jel.tech.model.datatables.DatatableOrder;
    import com.jel.tech.model.datatables.DatatableRequest;
    import com.jel.tech.model.datatables.DatatableResponse;
    import com.jel.tech.service.DeptService;
    
    @RequestMapping("/datatables")
    @Controller
    public class DatatableHandler {
        private static final Logger logger = LoggerFactory.getLogger(DatatableHandler.class);
        @Autowired
        private DeptService deptService;
    
        /*
        *需要produces,不然页面可能中文乱码,
        *callback是DataTables框架请求内部自带过来的,返回数据的时需要它
        前端的请求以json形式发送到后台,把其中的数据封装到Model中,
        代码中具体实现原理请查看封装的Datatable Model.
        *数据一般是以json形式返回.
        */
        @ResponseBody
        @RequestMapping(value = "/getJsonData4.do", method = RequestMethod.POST,produces="application/json; charset=UTF-8")
        public String getDataTables(String callback,@RequestBody DatatableRequest request) {
    
            DatatableResponse<Dept> response = new DatatableResponse<Dept>();
            response.setDraw(request.getDraw());
            //分页
            Integer start = request.getStart();
            Integer length = request.getLength();
            PageHelper.startPage(start, length);
            //对应数据库中的列名称
            String [] columnNames = {"dept_id","dept_name","parent_id","icon","rank"};
            //排序
            /*
             * request.getOrder()中的数据可能如下:
             * [DatatableOrder [column=0, dir=asc], DatatableOrder [column=2, dir=desc]]
             * 经过for循环处理后:
             * [DatatableOrder [column=0, dir=dept_id asc], DatatableOrder [column=2, dir=parent_id desc]]
             * 此时,orderBy = dept_id asc, parent_id desc
             * 至此组成完整的sql语句:
             * select * from tableName 
             * where condition 
             * limit start, length 
             * order by dept_id asc, parent_id desc
             */
            for(DatatableOrder order : request.getOrder()) {
                        order.setDir(StringUtils.join(Arrays.asList(columnNames[order.getColumn()], order.getDir()), " "));
            }
            String orderBy = StringUtils.join(request.getOrder().stream().map(DatatableOrder::getDir).toArray(), ",");
            PageHelper.orderBy(orderBy);
    
            List<Dept> depts = deptService.queryDeptByName(request.getParamMap());
            PageInfo<Dept> pageInfo = new PageInfo<Dept>(depts);
    
            response.setRecordsTotal((int)pageInfo.getTotal());                      response.setRecordsFiltered((int)pageInfo.getTotal());
            response.setData(pageInfo.getList());
            String json = JsonUtils.toJson(response);
            logger.info(json);
            /*
            *返回数据类似如下格式:
            jQuery111305123673508038207_1482069843425(
            {
        "draw":1,
        "recordsTotal":12,
        "recordsFiltered":12,
        "data":[
            {
                "deptId":1,
                "deptName":"大学",
                "parentId":-1,
                "icon":"http://localhost:8080/tech-web/images/cart.gif",
                "rank":1
            },
            {
                "deptId":10,
                "deptName":"综合大学",
                "parentId":1,
                "icon":"http://localhost:8080/tech-web/images/cart.gif",
                "rank":10
            },
            {
                "deptId":11,
                "deptName":"专业大学",
                "parentId":1,
                "icon":"http://localhost:8080/tech-web/images/cart.gif",
                "rank":11
            },
            {
                "deptId":101,
                "deptName":"清华大学",
                "parentId":10,
                "icon":"http://localhost:8080/tech-web/images/cart.gif",
                "rank":101
            },
            {
                "deptId":102,
                "deptName":"北京大学",
                "parentId":10,
                "icon":"http://localhost:8080/tech-web/images/cart.gif",
                "rank":102
            },
            {
                "deptId":103,
                "deptName":"华北电力大学",
                "parentId":11,
                "icon":"http://localhost:8080/tech-web/images/cart.gif",
                "rank":103
            },
            {
                "deptId":104,
                "deptName":"河海大学",
                "parentId":10,
                "icon":"http://localhost:8080/tech-web/images/cart.gif",
                "rank":104
            },
            {
                "deptId":105,
                "deptName":"野鸡大学",
                "parentId":11,
                "icon":"http://localhost:8080/tech-web/images/cart.gif",
                "rank":105
            },
            {
                "deptId":110,
                "deptName":"耶鲁大学",
                "parentId":11,
                "icon":"/tech-web/images/cart.gif",
                "rank":17
            },
            {
                "deptId":111,
                "deptName":"剑桥大学",
                "parentId":11,
                "icon":"/tech-web/images/cart.gif",
                "rank":24
            }
        ]
    })*/
            return callback.concat("(").concat(json).concat(")");
        }
    }
    
    
    //**4.DataTables Model代码**
    
    package com.jel.tech.model.datatables;
    
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 
     * @author Jelex.xu DataTables' server-side processing:Request Model
     *
     */
    public class DatatableRequest {
        /**
         * Draw counter. This is used by DataTables to ensure that the Ajax returns
         * from server-side processing requests are drawn in sequence by DataTables
         */
        private Integer draw;
        /**
         * Paging first record indicator. This is the start point in the current
         * data set (0 index based, i.e. 0 is the first record)
         */
        private Integer start;
        /**
         * Number of records that the table can display in the current draw. It is
         * expected that the number of records returned will be equal to this
         * number, unless the server has fewer records to return. Note that this can
         * be -1 to indicate that all records should be returned(although that
         * negates any benefits of server-side processing!)
         */
        private Integer length;
        /**
         * Means global search function model here.
         */
        DatatableSearch search;
        /**
         * an array defining all columns in the table
         */
        private List<DatatableColumn> columns = Collections.<DatatableColumn>emptyList();
        /**
         * an array defining how many columns are being ordered upon - i.e. if the
         * array length is 1, then a single column sort is being performed,
         * otherwise a multi-column sort is being performed.
         */
        private List<DatatableOrder> order = Collections.<DatatableOrder>emptyList();
        /**
         * request params(search conditions) a user input to search.
         *  key:param name,
         *  value:param value.
         */
        private Map<String, Object> paramMap = Collections.emptyMap();
    
        // getter/setter 省略
    }
    
    
    package com.jel.tech.model.datatables;
    
    import java.util.Collections;
    import java.util.List;
    
    /**
     * 
     * @author Jelex.xu DataTables Response Model
     * 
     *  The actually response data might be like this:
     {
        "draw": 1,
        "recordsTotal": 57,
        "recordsFiltered": 57,
        "data": [
            [
                "Angelica",
                "Ramos",
                "System Architect",
                "London",
                "9th Oct 09",
                "$2,875"
            ],
            [
                "Ashton",
                "Cox",
                "Technical Author",
                "San Francisco",
                "12th Jan 09",
                "$4,800"
            ],
            ...
        ]
    }
     */
    public class DatatableResponse<T> {
    
        /**
         * The draw counter that this object is a response to - from the draw
         * parameter sent as part of the data request. Note that it is strongly
         * recommended for security reasons that you cast this parameter to an
         * integer, rather than simply echoing back to the client what it sent in
         * the draw parameter, in order to prevent Cross Site Scripting (XSS)
         * attacks.
         */
        private Integer draw;
        /**
         * Total records, before filtering (i.e. the total number of records in the
         * database)
         */
        private Integer recordsTotal;
        /**
         * Total records, after filtering (i.e. the total number of records after
         * filtering has been applied - not just the number of records being
         * returned for this page of data).
         */
        private Integer recordsFiltered;
        /**
         * The data to be displayed in the table. This is an array of data source
         * objects, one for each row, which will be used by DataTables. Note that
         * this parameter's name can be changed using the ajax option's dataSrc
         * property.
         */
        private List<T> data = Collections.<T>emptyList();
        /**
         * Optional: If an error occurs during the running of the server-side
         * processing script, you can inform the user of this error by passing back
         * the error message to be displayed using this parameter. Do not include if
         * there is no error.
         */
    //  private String error;
    
        // getter/setter 省略
    
        /*public String getError() {
            return error;
        }
        public void setError(String error) {
            this.error = error;
        }*/
    
    }
    
    
    package com.jel.tech.model.datatables;
    
    /**
     * 
     * @author Jelex.xu 
     *  DataTables Ordering Model
     *
     */
    public class DatatableOrder {
    
        /**
         * Column to which ordering should be applied. This is an index reference to
         * the columns array of information that is also submitted to the server.
         */
        private Integer column;
        /**
         * Ordering direction for this column. It will be asc or desc to indicate
         * ascending ordering or descending ordering, respectively. You may want to
         * get the value through the use of OrderDirection class.
         */
        private String dir;
    
        // getter/setter 省略 
    }
    
    
    package com.jel.tech.model.datatables;
    
    /**
     * 
     * @author Jelex.xu
     *  Column Model in the table.
     *  a request example at the front-end might be like this:
        var t = $('#example').DataTable( {
            "columnDefs": [ {
                "searchable": false,
                "orderable": false,
                "targets": 0
            } ],
            "order": [[ 1, 'asc' ]]
        } );
    
     */
    public class DatatableColumn {
    
        /**
         * Column's data source,as defined by columns.data. Which can be used to
         * read and write data to and from any data source property, including
         * deeply nested objects / properties.
         */
        private String data;
        /**
         * Column's name, as defined by columns.name. Since: DataTables 1.10 Set a
         * descriptive name for a column.
         */
        private String name;
        /**
         * Flag to indicate if this column is searchable (true) or not (false). This
         * is controlled by columns.searchable.
         */
        private boolean searchable;
        /**
         * Flag to indicate if this column is orderable (true) or not (false). This
         * is controlled by columns.orderable :Enable or disable ordering on this
         * column
         */
        private boolean orderable;
        /**
         * Here used as a model to search specific column, which is
         * different to the usage situation in DatatableReq class,
         * and I don't think you would like to use it for performance reason.
         */
        private DatatableSearch search;
        // getter/setter 省略
    }
    
    
    package com.jel.tech.model.datatables;
    
    /**
     * 
     * @author Jelex.xu
     *DataTables Search function model
     */
    public class DatatableSearch {
    
        /**
         * Search value to apply to this specific column
         */
        private String value;
        /**
         * Flag to indicate if the search term for this column
         * should be treated as regular expression (true) or not (false).
         * As with global search, normally server-side processing scripts
         * will not perform regular expression searching for performance reasons
         * on large data sets, but it is technically possible
         * and at the discretion of your script.
         */
        private boolean regex;
        // getter/setter省略
    }
    
    
    package com.jel.tech.model.datatables;
    
    /**
     * 列排序枚举类
     */
    public enum OrderDirection {
    
        Direction_ASC("asc","升序"),
        Direction_DESC("desc","降序");
        /**
         * ascending ordering OR descending ordering
         */
        private String order;
        /**
         * ordering description
         */
        private String description;
    
        private OrderDirection(String order, String description) {
            this.order = order;
            this.description = description;
        }
    
        public String getOrder() {
            return order;
        }
        public String getDescription() {
            return description;
        }
    }
    
    
    package com.jel.tech.common.json;
    
    import java.lang.reflect.Type;
    
    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
    
    /**
     * JSON 工具类
     * @author Jelex
     *
     */
    public class JsonUtils {
    
        private static final Gson gsonWithPrettyPrinting = new GsonBuilder().serializeNulls().setPrettyPrinting().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        private static final Gson gson = new GsonBuilder().serializeNulls().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
    
        private JsonUtils() {}
    
        public static <E> String toJson(E obj) {
            return gson.toJson(obj);
        }
    
        public static <E> String toJsonWithPrettyPrint(E obj) {
            return gsonWithPrettyPrinting.toJson(obj);
        }
    
        /**
         * note:you can build the typeOfSrc in this way:
         *  Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
         *  where class Bar means the generic class type.
         * Actually,the toJson with one param is sufficient.
         * 
         * @param obj:the generic class
         * @param typeOfSrc
         * @return
         */
        public static <E> String toJson(E obj,Type typeOfSrc) {
                return gson.toJson(obj,typeOfSrc);
        }
    
        public static <E> E fromJson(String jsonStr,Class<E> clazz) {
            return gson.fromJson(jsonStr, clazz);
        }
    
        /**
         * 可从json格式转换为javabean,Map,List,for example:
         * @param jsonStr
         * @param typeOfSrc
         * @return
         * 
         *An sexample:
         public class Point {
            private double x;
            private double y;
         }
         Map<String, Point> map = new HashMap<String,Point>();
            map.put("start", new Point(1.0,5.0));
            map.put("end", new Point(3.0,9.0));
            map.put("circle", new Point(2.0,6.0));
    
            String json = JsonUtils.toJson(map);
            System.out.println(json);
    
            Type typeOfSrc = new TypeToken<Map<String,Point>>(){}.getType();
            Map<String,Point> retMap = JsonUtils.fromJson(json, typeOfSrc );
    
            System.out.println(retMap);
            System.out.println(MapUtils.getObject(retMap, "circle"));
            System.out.println(retMap.size());
    
            and the total result would be like this:
    
                {"start":{"x":1.0,"y":5.0},"end":{"x":3.0,"y":9.0},"circle":{"x":2.0,"y":6.0}}
                {start=Point [x=1.0, y=5.0], end=Point [x=3.0, y=9.0], circle=Point [x=2.0, y=6.0]}
                Point [x=2.0, y=6.0]
                3
         */
        public static <E> E fromJson(String jsonStr,Type typeOfSrc) {
            return gson.fromJson(jsonStr, typeOfSrc);
        }
    
    }
    
    //**至此,后台代码结束!**

    2.前端部分:

    • js文件:除了DataTables官网所要求的js文件外,还需要引入我封装的一个js文件,以及一个语言json文件-language.json

    • 截图如下:
      自己封装的js和语言json文件

    • css文件截图:

      这里写图片描述

    • **页面代码
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>主页</title>
    <link rel="stylesheet" href="css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff" />
    <link rel="stylesheet" href="css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2" />
    <link rel="stylesheet" href="css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf" />
    
    <link rel="stylesheet" href="css/datatables.min.css" />
    <link rel="stylesheet" href="css/bootstrap.min.css" />
    <link rel="stylesheet" href="css/bootstrap-theme.min.css" />
    <script type="text/javascript" src="js/datatables.min.js"></script>
    <script type="text/javascript" src="js/ajax-datatables.js"></script>
    
    <script type="text/javascript">
    
        function callback() {
            alert('yes!');
        }
    
        $(function() {
            $("#datatables").click(function() {
                 var paramMap = {
                          /* deptName : '华北电力大学' */
                      };
                var dataColumns = [
                    {'data':'deptId'},
                    {'data':'deptName'},
                    {'data':'parentId'},
                    {'data':'icon'},
                    {'data':'rank'},
                    {'data':"deptId","render": function (data, type, row) {
                         return '<div class="dropdown text-center">'
                         +'  <span class="glyphicon glyphicon-cog" data-toggle="dropdown"'
                         +' aria-haspopup="true" aria-expanded="false" style="cursor:pointer;"></span>'
                         +'  <ul class="dropdown-menu" aria-labelledby="dLabel">'
                         +'  <li><a href="#" data-toggle="modal" data-target="#deptModal" onclick="deptMain.edit(\''+row.deptId+'\')">修改</a></li> '
                         +'  <li><a href="#" onclick="deptMain.deleteInfo(\''+row.deptId+'\')">删除</a></li> '
                         +'  </ul>'
                         +'</div>';
                         }
                     }
                ];
    
                var dataColumnDefs = [
                    { targets: [0, 1], visible: true},
                    { targets: 3, "searchable": false },
                    { targets: 4, visible: false},
                ]
                console.log(dataTable);
                 dataTable.initTable(
                         'example',
                         '/tech-web/datatables/getJsonData4.do',
                        paramMap,
                        dataColumns,
                        /* dataColumnDefs,  */
                        null,
                        callback);
    
            });
        });
    </script>
    </head>
    <body>
        <div class="container">
            <button id="datatables" class="btn btn-success" type="button">datatables</button>
            <form action="" id="form" method="POST">
                <table id="example" class="table table-bordered table-hover">
                    <thead>
                        <tr>
                            <th>部门编号</th>
                            <th>部门名称</th>
                            <th>上级部门</th>
                            <th>校徽</th>
                            <th>排名</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <tbody></tbody>
                </table>
            </form>
        </div>
    </body>
    </html>
    
    
    [ajax-datatables.js]
    
        /*
     two ways to add your params to request the data you need:
    
     1.Add data to the request, returnng an object by extending the default data:
    
         $('#example').dataTable( {
         "ajax": {
             "url": "data.json",
             "data": function ( d ) {
             return $.extend( {}, d, {
                 "extra_search": $('#extra').val()
                 } );
             }
          }
         } );
    
    2.Add data to the request by manipulating the data object:
         $('#example').dataTable( {
         "ajax": {
             "url": "data.json",
             "data": function ( d ) {
                 d.extra_search = $('#extra').val();
             }
           }
         } );
    */
    
    /*note:
        the js is used to make it convenient to fire an ajax request to get data so as to
        show it in the table in a jQuery-DataTables way.
    
        tableId: the document id element name;
            for example:
                <table id="exampleTableId" class="table table-bordered table-hover">
                   <thead>
                     <tr>
                         <th>Property1</th>
                         <th>Property2</th>
                         <th>Property3</th>
                         <th>Property4</th>
                         <th>Property5</th>
                         <th>Operation</th>
                     </tr>
                   </thead>
                     <tbody></tbody>
                </table>
            That's it,a typical table, and the id attribute value 'exampleTableId' is what we need.
    
        url: the url to which you post the request;
    
        paramMap: request params you pass:
            think a situation like this,
            if you want to query your physical exercise(running) plan at day 2016-12-18,and you can compose
            you request param in this way:
                var paramMap = {taskName:'running',taskDate:'2016-12-18'};
            in which, taskName and taskDate are the input query condition,
            with 'running' and '2016-12-18' be their values respectively,
            by the way,the taskDate can be a datetimepicker selected value and that's the sugguested way;
    
        dataColumns: the columns you would like to show in the DataTable,actually,they are corresponding to the position
            at the table->thread->tr>th element and it's an array filled with js object. below is an example:
    
                 columns = [
                     {'data':'deptId'},
                     {'data':'deptName'},
                     {'data':'parentId'},
                     {'data':'icon'},
                     {'data':'rank'},
                     {'data':"deptId","render": function (data, type, row) {
                         return '<div class="dropdown text-center">'
                         +'  <span class="glyphicon glyphicon-cog" data-toggle="dropdown"'
                         +' aria-haspopup="true" aria-expanded="false" style="cursor:pointer;"></span>'
                         +'  <ul class="dropdown-menu" aria-labelledby="dLabel">'
                         +'  <li><a href="#" data-toggle="modal" data-target="#deptModal" onclick="deptMain.edit(\''+row.deptId+'\')">修改</a></li> '
                         +'  <li><a href="#" onclick="deptMain.deleteInfo(\''+row.deptId+'\')">删除</a></li> '
                         +'  </ul>'
                         +'</div>';
                         }
                     }
                 ];
    
        dataColumnDefs: user's own columns' settings can be set here,for example:
    
         var columnDefs = [
             {
                 "targets": 2,
                 "bSortable": false,
                 "searchable": false,
                 "render": function ( data, type, row ) {
                        //you extra process goes here.
                 }
             },
             {
                targets: [3,5],
                visible: false
             }
         ];
    
        callback: as the name says,it's a callback function.
            If you want to run some javascript on the contents of the table after its initial draw,
            and after every redraw / page, try using draw Event or drawCallback options.
                draw Event:
                     var table = $('#example').DataTable();
    
                     table.on( 'draw.dt', function () {
                     console.log( 'Table draw event' );
                     })
    
                drawCallback:
                     $('#example').dataTable( {
                     "drawCallback": function( settings ) {
                     var api = this.api();
    
                     // Output the data for the visible rows to the browser's console
                     console.log( api.rows( {page:'current'} ).data() );
                     }
                     } );
    
        finally,important:
            for it to be working,the language.json comes with this js,you should bind them together,put them in the same position.
    
     */
    var dataTable = {
        initTable : function(tableId,url,paramMap,dataColumns,dataColumnDefs,callback) {
            //get the DataTable object
            var otable =$("#"+tableId).dataTable();
            //destroy the DataTable if exists so as to reinitialize it.
            if(otable) {
                otable.fnDestroy();
            }
            /*$("#"+tableId).on('order.dt', callback())
             .on('page.dt', callback())*/
    
            $("#"+tableId).dataTable({
                "processing": true,
                'serverSide': true,
                "pagingType": "full_numbers",
                "searching":false,
                "lengthMenu": [ [10,15,20,50,100], [10,15,20,50,100] ],
                "language": {
                    "url": "js/luaguage.json"
                },
                'ajax' : {
                    'url' : url,
                    'dataType': 'jsonp',
                    'type': 'POST',
                    'contentType': 'application/json; charset=utf-8',
                    'data': function (d) {
                        console.log(d);
                        d.paramMap = paramMap;
                        return JSON.stringify(d);
                    }
                },
                columns:dataColumns,
                columnDefs:dataColumnDefs,
                order: [[ 0, 'asc' ]],
                "drawCallback": function( settings ) {
                    callback();
                }
            });
            /*.on( 'draw.dt', callback());*/
        }
    };
    
    
    [luaguage.json]
    
        {
          "decimal":        "",
          "emptyTable":     "表中无数据!",
          "info":           "显示_START_-_END_/_TOTAL_条",
          "infoEmpty":      "显示0-0/0条",
          "infoFiltered":   "(从_MAX_条记录中选出)",
          "infoPostFix":    "",
          "thousands":      ",",
          "lengthMenu":     "显示 _MENU_ 条",
          "loadingRecords": "拼命加载中...",
          "processing":     "处理中...",
          "search":         "搜索:",
          "zeroRecords":    "没找到匹配记录",
          "paginate": {
            "first":      "首页",
            "last":       "尾页",
            "next":       "下页",
            "previous":   "上页"
          },
          "aria": {
            "sortAscending":  ": activate to sort column ascending",
            "sortDescending": ": activate to sort column descending"
          }
        }

    //前端代码结束!

    运行截图:
    这里写图片描述

    注:支持分页、排序,和bootstrap样式完美结合。

    展开全文
  • ASP.NET的三种表格数据方法

    千次阅读 2006-01-06 20:22:00
    开发ASP.NET站点项目,经常用表格化的方式显示数据。最常用的可能就是DataGrid绑定DataSet数据的方式。做过的软件项目,有3种表格化数据的典型处理方式。 1、DataGrid绑定数据源。这种方式大家用的最多,...
            在开发ASP.NET站点项目中,经常用表格化的方式显示数据。最常用的可能就是DataGrid绑定DataSet数据的方式。在做过的软件项目中,有3种表格化数据的典型处理方式。

      1、DataGrid绑定数据源。这种方式大家用的最多,但是DataGrid与ADO.NET完美的绑定方式,还是让人不爽。清一色的DataGrid风格很难适应不同项目的特殊风格,而且在DataGrid上做出的个性化处理也会非常的麻烦。

      2、用XML(数据)+XSL(样式单)。大家能理解,DataSet绑定到DataGrid的实现机理不过如此。如图所示,实现这样的一个表格,开发人员可以尽情的设计XSL的风格样式。



        3、直接将数据绘制到HTML。这个方式有些土,但有些时候却非常有效,先看实现的代码。下述代码是实现上图所示的表格。

     1<table style="WIDTH: 100%; BORDER-COLLAPSE: collapse; HEIGHT: 10px">
     2                    <tr>
     3                        <td align="center">
     4                            <TABLE id="tblContainer" class="MsoNormalTable" style="WIDTH: 380px; BORDER-COLLAPSE: collapse; HEIGHT: 10px"
     5                                cellSpacing="0" cellPadding="6" border="1" runat="server" bordercolor="#99cccc">
     6                                <tr>
     7                                    <td colspan="2" align="center">
     8                                        <P><FONT size="3"><STRONG><FONT face="宋体">综合管理部人员职务</FONT></STRONG></FONT></P>
     9                                    </td>
    10                                </tr>
    11                                <tr>
    12                                    <td align="center" bgcolor="#003399"><FONT size="2" color="#ffffff"><STRONG>人员姓名</STRONG></FONT></td>
    13                                    <td align="center" bgcolor="#003399"><FONT size="2" color="#ffffff"><STRONG>部门职务</STRONG></FONT></td>
    14                                </tr>
    15                            </TABLE>
    16                        </td>
    17                    </tr>
    18                </table>

      直接用ASP.NET WebControls的Add方法,将Label添加到HTML的Cell中。

      1public class WebForm2 : System.Web.UI.Page
      2    {
      3    
      4        struct PersonRole
      5        {
      6            public string name;
      7            public string role;
      8        }

      9
     10        protected System.Web.UI.HtmlControls.HtmlTable tblContainer;
     11        public string strAuditItemID = "A899B637-AC47-42EB-9B61-A61C9C880DDC";
     12        private void Page_Load(object sender, System.EventArgs e)
     13        {
     14            // 在此处放置用户代码以初始化页面
     15            if(Request.QueryString["AuditItemID"!= null)
     16            {
     17                strAuditItemID =  Request.QueryString["AuditItemID"].ToString();
     18            }

     19
     20            GetTeamMember(strAuditItemID);
     21        }

     22
     23        Web 窗体设计器生成的代码
     43
     44        private void GetTeamMember(string AuditItemID)
     45        {
     46            string strMaster, strTeamLeader, strPM;
     47            ArrayList al = GetTeamMemberName(AuditItemID, out strMaster, out strTeamLeader, out strPM);
     48
     49            foreach(PersonRole pr in al)
     50            {
     51                HtmlTableCell cell=new HtmlTableCell();    
     52                cell.Align = "Center";
     53                Label lbl = new Label();
     54                lbl.Text = pr.name;
     55                lbl.Font.Size = 9;
     56
     57                cell.Controls.Add(lbl); 
     58                HtmlTableRow row=new HtmlTableRow();
     59                row.Cells.Add(cell);
     60
     61                HtmlTableCell cellRole = new HtmlTableCell();
     62                cellRole.Align = "Center";
     63                Label lblRole = new Label();
     64                lblRole.Text = pr.role;
     65                lblRole.Font.Size = 9;
     66
     67                cellRole.Controls.Add(lblRole);
     68                row.Cells.Add(cellRole);
     69
     70                tblContainer.Rows.Add(row);                        
     71            }

     72        }

     73
     74        private ArrayList GetTeamMemberName(string AuditItemID, out string strMasterName, out string strTeamLeader,out string strPM)
     75        {
     76            ArrayList al = new ArrayList();
     77            strMasterName = "";
     78            strTeamLeader = "";
     79            strPM = "";
     80            
     81            PersonRole pr;
     82            pr.name = "张三";
     83            pr.role = "总经理";
     84            al.Add(pr);
     85            
     86            pr.name = "李四";
     87            pr.role = "副总经理";
     88            al.Add(pr);
     89
     90            pr.name = "王五";
     91            pr.role = "科员";
     92            al.Add(pr);
     93
     94            pr.name = "赵六";
     95            pr.role = "科员";
     96            al.Add(pr);
     97
     98            return al;
     99        }

    100    }


      在绘制页面的时候,用哪种方式应该是仁者见仁、智者见智。在站点开发中,这3种方式都有典型的应用,特别是第三种,我发现在解决部分页面处理的性能问题中应用的非常有效。况且可以自动化的Layout页面控件,个性化处理比DataGrid中重写Render更加容易。

    文章

    展开全文
  • html中表格table的内容居中显示

    万次阅读 2018-11-06 18:16:17
    html中表格table的内容居中显示
  •   很多时候,我们的数据来源形式是多种多样的,有时候数据(或表格)也会呈现图片。那么,我们如何来获取图片的有用数据呢?当一张图片含有表格数据的时候,我们可以用OpenCV识别表格中的直线,然后再用...
  • 业务层首先创建两个集合以方便存取数据方法闲设置表格样式,再设置自己需要的表格宽度,我这里用的是四个宽度的表格,再将查询的数据插入到表格中就可以直接导出自己需要的表格数据 业务层代码: private ...
  • 用OpenCV和OCR识别图片表格数据

    千次阅读 2019-03-24 21:00:00
    ♚作者:jclian,喜欢算法,热爱分享,希望能结交更多志同道合的朋友,一起学习Python的道路上走得更远!  很多时候,我们的数据来源形式是多种多样的,有时候数...
  • Python 与 Excel 表格综合实例(一):给表格增加序号,根据表格已有内容增加索引前言:需求一:增加序列号需求二:按已有内容新增列结尾: 前言: 前面我们花了6篇博客,把Python的xlwt与xlrd两个第三方模块对Excel...
  • echart表格中日期格式化

    千次阅读 2020-02-14 18:33:58
    使用echart表格显示数据,但是后台提供的时间为13位时间戳,显示表格也是时间戳,将时间戳转化为YYYY-MM-DD HH-MM-SS时间格式如下: 参考几种方法: ① element-ui 表格数据时间格式化的方法 ② vue element-ui...
  • 本文从三篇表格识别领域的精选论文出发,深入分析了深度学习在表格识别任务的应用。 表格识别是文档分析与识别领域的一个重要分支,其具体目标是从表格中获取和访问数据及其它有效信息。众所周知,本质上表格是...
  • 介绍一个用Python对Excel表格数据进行去重、分类、异常处理及分析的实例。
  • 首先到phpexcel官网上下载最新的phpexcel类,下周解压缩一个classes文件夹,里面包含了PHPExcel.php和PHPExcel的文件夹,这个类文件和文件夹是我们需要的,把classes解压到你项目的一个目录,重名名为phpexcel,...
  • JSP分页显示数据

    万次阅读 多人点赞 2017-01-20 16:35:09
    最近做一个小程序,用到了JSP的分页。虽然只是最简单的分页,但是还是花了我不少时间。这看似简单的功能,实现起来还是稍微有点麻烦。实现分页功能,需要知道数据的总个数,每页应该有多少条数据,以及当前页码。...
  • 比如 table表 字段: id name bianhao address shijian 1 张三 001 北京 2013-12-1 2 李四 001 北京 2013-12-1 最后显示的内容为: 字段: name1 name2 bianhao address shijian 张三 李四 001 北京 ...
  • python修改docx文档的表格内中文字体

    千次阅读 2020-10-01 22:07:42
    from docx import Document from docx.enum.text import WD_PARAGRAPH_ALIGNMENT...#读取文档的第一个表格数据 tables = document.tables[0] #第该表格3行1列的单元格内输入“能几日又中秋?as12A” run = tables.c.
  • 商业数据分析过程数据地图是很直观的可视化的表达形式,比如利用地图展示各区域销售额、利润等;比如展示全国仓库分布和运输和以优化运输网络;又比如统计商场人流量及分布,用热力地图展示,指导门店分布和...
  • 概述 自动化测试过程,我们不可避免的要面临GUI对象识别的问题。目前主流的测试工具如RFT、QTP等,都能够很好的识别标准的对象元素,如windows的按钮、下拉列表,IE的html标签等。但是也有一些定制的控件和...
  • SpreadJS是一个面向企业级应用开发的综合性、高效能的HTML5电子表格控件。SpreadJS有着强大的表单处理能力和电子表格功能。这些功能包括跨表单注释和计算,这样就能够充分利用多个表单上的数据和公式。SpreadJS拥有...
  • 在表格td,有两个属性控制居中显示 align——表示左右居中——left,center,right valign——控制上下居中——left,center,right 这两个属性综合使用,就可以让单元格的内容上下左右都居中显示。 但是有的时候...
  • phpexcel中文教程-设置表格字体颜色背景样式、数据格式、对齐方式、添加图片、批注、文字块、合并拆分单元格、单元格密码保护
  • 数据库, 数据仓库, 数据集市,数据湖,数据中

    千次阅读 多人点赞 2019-02-22 16:21:47
    数据仓库和数据集市的区别 作者:修鹏李 出处:CSDN 大数据:数据仓库和数据库的区别 作者:南宫蓉 出处:简书 第一篇:数据仓库概述 第二篇:数据库关系建模 作者:穆晨 出处:CNBLOS 摘要 本文简要介绍...
  • 本文代码实现基本按照《数据结构》课本目录顺序,外加大量的复杂算法实现,一篇文章足够。能换你一个收藏了吧?
  • 针对这类问题小编也是无能为力,只能为大家先分享几个我们以后工作经常会遇到的Excel表格填充技巧,让你以后工作当中尽量的少走弯路! 一、填充文本 当我们在表格中需要将本的重要信息提炼到单独的一个...
  • 介绍HTML中表格、列表和表单的基本用法
  • HTML5:表格

    千次阅读 2016-01-28 15:12:20
    表格的作用是显示二维数据HTML5不再允许用表格控制页面内容的布局,而是采用新增的CSS表格特性(这里不涉及CSS,将后面介绍)。下面主要介绍用于制作表格的HTML元素。构建表格表格的基本元素包括:table、tr...
  • 作为一个学习用Python进行数据分析的新手来说,通过本文来记录分享一些我用Python的pandas、numpy来分析Excel表中数据数据清洗和整理的工作,目的是熟悉numpy以及pandas基础操作,所有操作利用Excel均可以方便...
  • WEB入门二 表格和表单

    千次阅读 2017-02-14 13:57:51
    Ø 理解表单的作用Ø 熟练掌握表单常用元素本章简介表格对于网页制作而言极其重要,其最明显的优点是能够以行列整齐的形式来显示文本、数字、图片信息。而且,表格还可以用于固定文本或图像的显示位置。经常上网的...
  • Tomcat配置连接池和数据

    千次阅读 2015-03-04 14:03:18
    1、DataSource接口介绍 (1)DataSource 概述 JDBC1.0原来是用DriverManager类来产生一个对数据源的连接。...根据DataSource的实现方法,数据源既可以是从关系数据库,也电子表格,还可以是一个表格

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,419
精华内容 10,167
关键字:

在表格中如何显示数据综合