一个最简单的demo,只用一个html,不需要任何其它配置
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/xspreadsheet.css">
<script src="https://unpkg.com/[email protected]/dist/xspreadsheet.js">script>
<script src="https://unpkg.com/[email protected]/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应该没问题。
在npm中使用
作者说不推荐,放弃
导入和导出
作者给的例子是另一个项目里的:link
这个例子下载后在win上有bug:
改了之后就,浏览器打开index.html后就可以使用了,效果还不错。导出之后会丢失所有格式。
先看一个加载json的例子
<link rel="stylesheet"
href="https://unpkg.com/[email protected]/dist/xspreadsheet.css">
<script src="https://unpkg.com/[email protected]/dist/xspreadsheet.js">script>
<script src="https://unpkg.com/[email protected]/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字符串。
现在用 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/[email protected]/dist/xspreadsheet.css">
<script src="https://unpkg.com/[email protected]/dist/xspreadsheet.js">script>
<script src="https://unpkg.com/[email protected]/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文件直接打开,发现可以顺利生成图表。
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
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页title>
head>
<link rel="stylesheet"
href="https://unpkg.com/[email protected]/dist/xspreadsheet.css">
<script src="https://unpkg.com/[email protected]/dist/xspreadsheet.js">script>
<script src="https://unpkg.com/[email protected]/dist/locale/zh-cn.js">script>
<body>
<h1>x-spreadsheeth1>
<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
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Xiaotaotaotitle>
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是可以编辑的,后续继续研究如何保存修改的数据。