x-spreadsheet学习

1,888 阅读2分钟

blog.csdn.net/qq_38391187…

使用

一个最简单的demo,只用一个html,不需要任何其它配置

<link rel="stylesheet" href="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/xspreadsheet.css">
<script src="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/xspreadsheet.js"></script>
<script src="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/locale/zh-cn.js"></script>
<div id="x-spreadsheet-demo">
  <script>
    x_spreadsheet.locale('zh-cn');  //中文
    var htmlout = document.getElementById('x-spreadsheet-demo')
    var xs = x_spreadsheet(htmlout
  </script>
</body>
</div>

现在的问题是如何把插件加载json数据,以及处理保存的问题。数据导出成excel应该没问题。

从数据库读取数据解析为表格

先看一个加载json的例子

<link rel="stylesheet" href="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/xspreadsheet.css">
<script src="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/xspreadsheet.js"></script>
<script src="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/locale/zh-cn.js"></script>
<div id="x-spreadsheet-demo">
    <script>
        x_spreadsheet.locale('zh-cn');  //中文
        var htmlout = document.getElementById('x-spreadsheet-demo')
        data = [{"name":"Sheet1","freeze":"A1","styles":[],"merges":[],
        "rows":{
            "0":{"cells":{"0":{"text":"id"},"1":{"text":"name"}}},
            "1":{"cells":{"0":{"text":"1"},"1":{"text":"Tom"}}},
            "2":{"cells":{"0":{"text":"2"},"1":{"text":"Hall"}}},
            "3":{"cells":{"0":{"text":"3"},"1":{"text":"Sure"}}},
            "len":5},
            "cols":{"len":6},
            "validations":[],
            "autofilter":{}}]
        var xs = x_spreadsheet(htmlout).loadData(data)
        console.log("表格返回的数据为:\n", xs.getData())  // getData得到一个object对象,要把它转为json用JSON.stringify()
        console.log("json字符串格式为:\n", JSON.stringify(xs.getData()))
    </script>
</div>

上面的例子中,各出的json格式和从数据库拿到的不一样,所以我们如果想把从数据库拿出的直接json样式展示出来,需要自己写一个转换函数。这个在后端还是前端都可以,我们就在后端完成这个工作。

假设由数据库得到一个df对象,现在把它拼接成这种形式的json字符串。

我们发现json中指定的行和列,如果新增行列是在前面加,所以指定的时候就比数据库的数据多一行一列。

现在用 python 来实现格式转换:dataframe -> dict -> dict2 -> list

dict2的格式

在这里插入图片描述
这是一个四维字典,用 dict2['0']['cells']['0']['text']

可以访问到 第一个数据,这里就是 id, 但实际上数据是二维的,二四维是固定的‘cells’和’text’。

方案一:直接用字符串拼接

def df2xspreadsheetjson(df) -> str:
    '''
    df对象转为 x-spreadsheet格式的json字符串
    :param df: 从数据库得到的dataframe
    :return: str
    '''
    cols = []
    # 重命名标题行,主要考虑标题行空等情况
    for col in df.columns:
        if col == "":
            cols.append("N/A")  # 标题行空,一般不可能
        elif col is None:
            cols.append("NULL")  # 表是空的
        else:
            cols.append(col)  # 复制过去
    # print(cols)
    df.columns = cols

    if df.shape[0] < 1 or df.shape[1] < 1:
        return '{}'

    metrics = cols
    df = df[metrics]  # 取我们需要的字段
    # 直接拼接字符串
    info = ''
    # 先拼接标题行
    info += '\"0\":{\"cells\":{'
    for i in range(len(metrics)):
        if i != len(metrics)-1:
            info += '\"' + str(i) + '\":' + '{\"text\":\"' + str(metrics[i]) + "\"},"
        else:
            info += '\"' + str(i) + '\":' + '{\"text\":\"' + str(metrics[i]) + "\"}"
    info += '}},'

    for index, row in df.iterrows():
        info += '\"'+str(index+1) + '\":'
        for j in range(len(metrics)):
            if j == 0:
                info += "{\"cells\":{"
            # print(row[col])
            if j != len(metrics)-1:
                info += '\"' + str(j)+'\":' + '{\"text\":\"' + str(row[metrics[j]]) + "\"},"
            else:
                info += '\"' + str(j) + '\":' + '{\"text\":\"' + str(row[metrics[j]]) + "\"}"
        info += '}},'

    rows = '{' + info + "len:" + str(df.shape[0]+2) + '}'  # 加标题行长度,最好再加上一行+1+1
    cols = 'cols\":{\"len\":' + str(df.shape[1]+1) + '}}'
    return rows

# test
data = {'水果': ['苹果', '梨', '草莓'],
        '数量': [3, 2, 2],
        '价格': [10, 9, None]}
data2 = {"0": {"cells": {"0": {"text": "id"}, "1": {"text": "name"}}},
         "1": {"cells": {"0": {"text": "1"}, "1": {"text": "Tom"}}},
         "2": {"cells": {"0": {"text": "2"}, "1": {"text": "Hall"}}},
         "3": {"cells": {"0": {"text": "3"}, "1": {"text": "Sure"}}}}
df = pd.DataFrame(data)
print(df)
s = df2xspreadsheetjson(df)
print(s)

方案二:用多维dict

TODO

用我们刚刚python脚本的结果,来试验一下:

<link rel="stylesheet" href="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/xspreadsheet.css">
<script src="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/xspreadsheet.js"></script>
<script src="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/locale/zh-cn.js"></script>
<div id="x-spreadsheet-demo">
    <script>
        x_spreadsheet.locale('zh-cn');  //中文
        var htmlout = document.getElementById('x-spreadsheet-demo')
        data = [{"name":"Sheet1","freeze":"A1","styles":[],"merges":[],
        "rows":{
            "0":{"cells":{"0":{"text":"id"},"1":{"text":"name"}}},
            "1":{"cells":{"0":{"text":"1"},"1":{"text":"Tom"}}},
            "2":{"cells":{"0":{"text":"2"},"1":{"text":"Hall"}}},
            "3":{"cells":{"0":{"text":"3"},"1":{"text":"Sure"}}},
            "len":5
            },
            "cols":{"len":6},
            "validations":[],
            "autofilter":{}}]
        rows2 = {"0":{"cells":{"0":{"text":"水果"},"1":{"text":"数量"},"2":{"text":"价格"}}},"1":{"cells":{"0":{"text":"苹果"},"1":{"text":"3"},"2":{"text":"10.0"}}},"2":{"cells":{"0":{"text":"梨"},"1":{"text":"2"},"2":{"text":"9.0"}}},"3":{"cells":{"0":{"text":"草莓"},"1":{"text":"2"},"2":{"text":"nan"}}},len:5}
        data2 = {"rows":rows2, "cols":{"len":4}}
        var xs = x_spreadsheet(htmlout).loadData(data2)
        console.log("表格返回的数据为:\n", xs.getData())  // getData得到一个object对象,要把它转为json用JSON.stringify()
        console.log("json字符串格式为:\n", JSON.stringify(xs.getData()))
    </script>
</div>

HTML文件直接打开,发现可以顺利生成图表。

在flask中使用xspread-sheet

新建一个flask项目

pip install flask, flask_script
mkdir flaskSheet
cd flaskSheet
flask run

这是一个标准的flask demo。
在app文件夹新建utils/trans.py

import pandas as pd

def df2xspreadsheetjson(df) -> str:
    '''
    df对象转为 x-spreadsheet格式的json字符串
    :param df: 从数据库得到的dataframe
    :return: str
    '''
    cols = []
    # 重命名标题行,主要考虑标题行空等情况
    for col in df.columns:
        if col == "":
            cols.append("N/A")  # 标题行空,一般不可能
        elif col is None:
            cols.append("NULL")  # 表是空的
        else:
            cols.append(col)  # 复制过去
    # print(cols)
    df.columns = cols

    if df.shape[0] < 1 or df.shape[1] < 1:
        return '{}'

    metrics = cols
    df = df[metrics]  # 取我们需要的字段
    # 直接拼接字符串
    info = ''
    # 先拼接标题行
    info += '\"0\":{\"cells\":{'
    for i in range(len(metrics)):
        if i != len(metrics)-1:
            info += '\"' + str(i) + '\":' + '{\"text\":\"' + str(metrics[i]) + "\"},"
        else:
            info += '\"' + str(i) + '\":' + '{\"text\":\"' + str(metrics[i]) + "\"}"
    info += '}},'

    for index, row in df.iterrows():
        info += '\"'+str(index+1) + '\":'
        for j in range(len(metrics)):
            if j == 0:
                info += "{\"cells\":{"
            # print(row[col])
            if j != len(metrics)-1:
                info += '\"' + str(j)+'\":' + '{\"text\":\"' + str(row[metrics[j]]) + "\"},"
            else:
                info += '\"' + str(j) + '\":' + '{\"text\":\"' + str(row[metrics[j]]) + "\"}"
        info += '}},'

    rows = '{' + info + "len:" + str(df.shape[0]+2) + '}'  # 加标题行长度,最好再加上一行+1+1
    cols = 'cols\":{\"len\":' + str(df.shape[1]+1) + '}}'
    return rows

这里也新建一个__init__.py文件。
app文件夹下新建demo.py

from flask import Flask, render_template
from flask import request
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate,MigrateCommand
from flask_script import Shell,Manager
import json
import pandas as pd

from utils.trans import df2xspreadsheetjson
 
app = Flask(__name__)
 
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql://XXXX:XXXXXXX@localhost:3306/mydb"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['WTF_CSRF_ENABLED'] = False
db = SQLAlchemy(app)
 
migrate = Migrate(app,db)
manager = Manager(app)
manager.add_command('db',MigrateCommand)
 
 
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True, index=True)
    age = db.Column(db.Integer, default=18)
 
    def __repr__(self):
        return 'User:%s'%self.name
 
 
@app.route('/show')
def demo():
    # user = User.query.all()
    # name = user.name
    # age = user.age
    # data = {
    #     "name": name,
    #     "age": age
    # }
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker
    engine = create_engine("mysql://user:passwd@localhost:3306/mydb?charset=utf8mb4")
    conn = engine.connect()
    data = pd.read_sql("select * from users limit 100",conn)
    
    data = df2xspreadsheetjson(data)
  
    return render_template("index.html", data=data)
 
@app.route('/login', methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get("username")
        userage = request.form.get("userage")
        user = User(name=username, age=userage)
        db.session.add(user)
        db.session.commit()
    return render_template("login.html")
 
if __name__ == '__main__':
    db.create_all()
    app.run(debug=True)
    manager.run()

templates文件夹下新建 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<link rel="stylesheet"
    href="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/xspreadsheet.css">
<script src="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/xspreadsheet.js"></script>
<script src="https://unpkg.com/x-data-spreadsheet@1.1.1/dist/locale/zh-cn.js"></script>

<body>
<h1>x-spreadsheet</h1>

<!-- 为了让js能够读取数据,把它放到一个容器中 -->
<div id="dataid" d="{{data}}" style="display:none"></div>  

<div id="x-spreadsheet-demo">
    <script>
        var daer=  document.getElementById('dataid').getAttribute('d')
	    console.log(daer)
        x_spreadsheet.locale('zh-cn');  //中文
        var htmlout = document.getElementById('x-spreadsheet-demo')
        rows2= eval('(' + daer+ ')'); //  字符串转化为json对象
        data2 = [{"name":"表格" , "rows":rows2, "cols":{"len":20}} ]// 拼接,注意可以放多张表
        var xs = x_spreadsheet(htmlout).loadData(data2)
        console.log("表格返回的数据为:\n", xs.getData())  // getData得到一个object对象,要把它转为json用JSON.stringify()
        console.log("json字符串格式为:\n", JSON.stringify(xs.getData()))
    </script>
</div>

</body>
</html>

新建 login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Xiaotaotao</title>
</head>
<body>
 
<form method="post">
    <label>姓名:</label><input type="text" name="username" placeholder="请输入姓名"><br/>
    <label>年纪:</label><input type="password" name="userage" placeholder="请输入年纪"><br/>
    <input type="submit" value="提交">
</form>
 
</body>
</html>

最后运行 python demo.py

浏览器打开localhost:5000/login向数据库写入数据,localhost:5000/show可以显示数据。效果如下:

在这里插入图片描述
加载并渲染10000行的数据大概要3秒,还行。1000行以x无y延迟。由于x-spreadsheet是可以编辑的,后续继续研究如何保存修改的数据。