需要在高级中可开启datav模式
-参考视屏
--
-
- - 1. DataV基础应用 -
- - 2. DataV翻盘器 -
DataV配置方法文档: - - 图表
-diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 73f69e0..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/docs/404.html b/docs/404.html deleted file mode 100644 index 6bf4f93..0000000 --- a/docs/404.html +++ /dev/null @@ -1,54 +0,0 @@ - - - -
- - - - - - - - - -需要在高级中可开启datav模式
-参考视屏
-DataV配置方法文档: - - 图表
-将base.html 改为 basevue.html, 将自动开启加载vue和elementui
-
-
--注意vue的变量引用在 模板编辑界面中, 写法变更为 {[ ]}
-
新增一个拖拽图形,然后修改相应的数据集及图形
-数据集端
-select H1, H2, qty, rate from smartdemo2 limit 100
-
图形端
-let dataset = __dataset__;
-let tableData = ds_createMap_all(dataset);
-
-vapp.d0={
- tableData: tableData
-}
-
模板Body区域加入element组件el-table,并用拖拽容器进行包裹
-<div class="smtdrag" id="id_1639824145817">
- <el-table :data="d0.tableData" stripe border height="100%" style="width: 100%">
- <el-table-column label="hero">
- <el-table-column fixed width="180" prop="H1" label="H1" :default-sort = "{prop:'H2',order:'descending'}"></el-table-column>
- <el-table-column sortable width="180" prop="H2" label="H2"></el-table-column>
- </el-table-column>
- <el-table-column sortable prop="qty" label="qty"></el-table-column>
- <el-table-column prop="rate" label="rate"></el-table-column>
- </el-table>
-</div>
-
<!--显示变量message-->
-<p>{[ message ]}</p>
-<!--循环产生li,变量sites-->
-<ol>
- <li v-for="site in sites">
- {[ site.name ]}
- </li>
-</ol>
-<!--绑定输入值变量use-->
-<input type="checkbox" v-model="use">
-<!--显示控制-->
-<p v-if="seen">现在你看到我了</p>
-<p v-show="seen">现在你看到我了</p>
-<!--绑定属性-->
-<a :href="url"></a>
-<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
-<div :class="[errorClass ,isActive ? activeClass : '']"></div>
-<!--绑定点击方法-->
-<a @click="doSomething"></a>
-
需要在设定–>高级设定中可开启vue模式
-系统默认为初始vue及内置vue的data有17个变量(从d0, d1… d16) -你可以在图形编辑器中给vue的变量赋值, 赋值方式:
-vapp.d0 = xxxx
-
-
你可以将d0赋值为字典, 如:
-vapp.d0 = { 'index1': 100, 'index2': 300}
-
--注意,我们修改了vue在模板中变量的默认引用方式, 你需要采用如下方法引用: -{[d0.index1]}
-
你可以在模板的script中加入自定义代码来覆盖系统默认的
-<script>
- var vapp = new Vue({el: '#vue_app', delimiters: ['{[', ']}'],
- data: {
- tableData:''
- },
- methods: {
- formatter(row, column) {
- return row.address;
- }
- }
- });
-</script>
-
点击smartchart图标,切换到菜单固定模式, 你可看到主题的选择
-
-
-
--注意: 复制出来的仪表盘, 数据集是与原仪表盘公用的!!
-
--免费版使用者必须保留SmartChart相关版权标识及LOGO,禁止对其进行修改和删除 -如果违反,将保留对侵权者追究责任的权利
-
微信客服不提供技术咨询, 如有使用方法的疑惑,建意加QQ群:476715246 进行沟通
-功能 | -免费版 | -专业版 | -中台版 | -个人版 | -
---|---|---|---|---|
栅格布局 | -V | -V | -V | -V | -
DATAV | -V | -V | -V | -V | -
拖拽布局 | -- | V | -V | -V | -
自由开发 | -- | V | -V | -V | -
切换图表主题 | -- | V | -V | -V | -
主题自由设计 | -- | V | -V | -V | -
引入JS | -- | V | -V | -V | -
引入CSS | -- | V | -V | -V | -
上传静态资源 | -- | V | -V | -V | -
使用VUE | -V | -V | -V | -V | -
数据集开发 | -V | -V | -V | -V | -
所有数据源 | -V | -V | -V | -V | -
图形开发 | -V | -V | -V | -V | -
图形商店 | -V | -V | -V | -V | -
普通模板应用 | -V | -V | -V | -V | -
专业模板应用 | -- | V | -V | -V | -
复制仪表盘 | -V | -V | -V | -V | -
钻取 | -V | -V | -V | -V | -
联动 | -V | -V | -V | -V | -
筛选 | -V | -V | -V | -V | -
单点登录 | -V | -V | -V | -V | -
嵌入认证 | -- | V | -V | -V | -
LDAP认证 | -- | V | -V | -V | -
Juypter | -V | -V | -V | -V | -
快捷存档 | -- | V | -V | -V | -
数据加速 | -- | V | -V | -V | -
数据API服务 | -- | V | -V | -V | -
后台API刷新 | -- | V | -V | -V | -
仪表盘同步 | -- | V | -V | -V | -
仪表盘版本管理 | -- | V | -V | -V | -
数据填报 | -- | V | -V | -V | -
报表Portal | -- | V | -V | -V | -
多级项目菜单 | -- | - | V | -- |
3D场景 | -- | V | -V | -V | -
中国式报表 | -- | V | -V | -V | -
零代码数据集 | -- | V | -V | -V | -
生产部署文档 | -- | V | -V | -- |
个性化修改 | -- | V | -V | -- |
授权书 | -- | V | -V | -- |
商业授权 | -- | V | -V | -- |
优先咨询 | -- | V | -V | -- |
专业边框背景 | -- | V | -V | -- |
3K+图形样列 | -- | V | -V | -- |
100+大屏模板 | -- | V | -V | -- |
大模型AI应用 | -- | V | -V | -- |
低代码ETL | -- | - | V | -- |
调度平台 | -- | - | V | -- |
智慧BI | -- | - | V | -- |
数据资产 | -- | - | V | -- |
数据质量 | -- | - | V | -- |
数据血缘 | -- | - | V | -- |
租户管理 | -- | - | V | -- |
在开发模式下,点击“开发管理”->数据源->新增
-
配置连接池参数,注意驱动填写是备注中有写的名称
-
你可以通过新建一个数据集来测试连接池的连通性
-点击“保存” 后,回到数据集列表 点击如下图标"E",进入数据集开发界面
-
在开发界面调试
-
模式 | -名称 | -预览注入 | -查询注入 | -
---|---|---|---|
0 | -严格模式 | -V | -V | -
1 | -开发模式 | -V | -X | -
2 | -宽松模式 | -X | -X | -
--对MPP数据库如starrocks,impala…,如注入为V则有可能影响排序
-
数据库 | -驱动填写 | -需安装 | -使用说明 | -
---|---|---|---|
Mysql | -mysql | -默认支持 | -- |
Mysql连接池 | -mysqlpool | -pip install DBUtils | -- |
Sqlite | -sqlite | -默认支持 | -连接地址填写绝对路径 | -
API | -任意 | -默认支持 | -参考数据集说明文档 | -
GPT | -任意 | -默认支持 | -参考GPT说明文档 | -
EXCEL | -任意 | -默认支持 | -参考数据集说明文档 | -
SQL Server | -mssql | -pip install pymssql | -- |
SQL Server连接池 | -mssqlpool | -- | - |
ORACLE | -oracle | -pip install cx_Oracle | -- |
ORACLE连接池 | -oraclepool | -- | - |
PostgreSql | -gp | -pip install psycopg2 | -- |
GP | -gp | -pip install psycopg2-binary | -- |
Impala | -impala | -pip install impyla | -- |
Hive | -hive | -pip install impyla | -- |
DB2 | -db2 | -pip install ibm_db | -- |
达梦 | -dm | -pip install dmPython | -- |
Python | -python | -pip install pandas, openpyxl | -参考数据集->特殊数据源 | -
Redis | -redis | -pip install redis | -参考数据集->特殊数据源 | -
Mongodb | -mongodb | -pip install pymongo | -参考数据集->特殊数据源 | -
Clickhouse | -clickhouse | -pip install clickhouse_driver | -- |
Elasticsearch | -es | -pip install elasticsearch==7.13.0 | -参考数据集->特殊数据源 | -
Prometheus | -prometheus | -- | 参考数据集->特殊数据源 | -
influxdb | -influxdb | -pip install influxdb | -- |
Sqlalchemy | -sqlalchemy | -pip install sqlalchemy | -参考数据集->特殊数据源 | -
JDBC | -jdbc | -pip install JayDeBeApi | -参考数据集->特殊数据源 | -
飞书EXCEL | -feishuExcel | -- | 专业版本指导 | -
钉钉EXCEL | -dingdingExcel | -- | 专业版本指导 | -
向量数据库 | -- | - | 专业版本指导 | -
大模型 | -- | - | 专业版本指导 | -
JDBC | -jdbc | -pip install JayDeBeApi | -参考数据集->特殊数据源 | -
自定义 | -自定义 | -用户自由定义 | -参考数据集->特殊数据源 | -
快速应用开发好的模板,极大地提高开发和学习效率
-你可以快速应用本地备份的模板, 我们内置了一个 通用的数据查询和下载模板
-方法同上"应用本地模板", 注意应用商店模板为收费增值服务
- -由于版本的变更, 一些图标可能会有一些变化, 但位置无太大的变化
-在Shell或CMD命令行执行
-pip3 install smartchart
-#如果安装过程下载缓慢,建意使用镜象安装
-pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple smartchart -U
-
-#升级方法:
-pip3 install smartchart -U
-
#本地测试启动:
-smartchart
-#服务器部署启动方式:
-smartchart runserver 0.0.0.0:8000 --insecure --noreload
-#如出现套接字,端口被占用, 可修改端口号启动, 如
-smartchart runserver 0.0.0.0:8001 --insecure --noreload
-
#如果忘记密码, 可以使用此命令重置
-smartchart changepassword 用户名
-
-
--左则的菜单为动态项目名, 随着您的报表增长自动变化
-
-
功能项 | -子项 | -使用说明 | -
---|---|---|
菜单栏 | -- | -点击可展开隐藏, 可移动位置 | -
设定 | -常规设定 | -权限管理,水印,模板应用,版本控制.. | -
- | 高级设定 | -开启模板/移动端适应/VUE/DATAV/场景背景.. | -
- | 批量布局 | -批量修改容器的HTML, 不常用 | -
- | 容器管理 | -批量查看/管理/删除容器, 不常用 | -
模板 | -- | -进入低代码开发,引入JS/CSS,上传,组件… | -
新增 | -拖拽图形 | -新增一个带拖拽容器的图形组件 | -
- | 栅格图形 | -新增一个带栅格容器的图形组件 | -
- | 上线数据集 | -新增一个已开发好的数据集(需要是上线状态) | -
- | 懒加载数据集 | -新增后在菜单栏可查看编辑,此数据集需自定义触发查询,默认可引用全局变量名为data序号, 如data0 | -
- | 通用数据集 | -新增后在菜单栏可查看编辑,主动触发查询,全局变量名为data序号, 如data0 | -
- | 静态组件 | -新增一个不带数据集及图形的html组件, 如图片,文字描述… | -
- | 复杂报表 | -使用类excel的方式开发报表 | -
拖拽开关 | -- | -切换拖拽锁定, 如需编辑EXCEL图形组件, 一般需要锁定 | -
弹窗开关 | -- | -切换编辑器是嵌入方式还是新开页面 | -
自定义主题 | -- | -修改echarts的主题, 仅当前仪表盘有效 | -
调试开关 | -- | -切换是否显示调试打印的日志,开启网格辅助定位及精确拖拽定位 | -
主题选择 | -- | -切换显示内置echarts主题 | -
预览 | -- | -切换开发模式下的预览模式 | -
数据集 | -- | -菜单栏会显示未激活/通用/懒加载数据集 | -
功能项 | -子项 | -使用说明 | -
---|---|---|
序号 | -- | -唯一标识容器ID,可以通过 #container_序号 来选择容器, 如样式选择:#container_0{}, JS选择:$(’#container_0’).xx | -
数据集开发 | -- | -打开数据集开发界面, 可切换数据源,数据定时刷新,数据开发… | -
图形开发 | -- | -打开图形开发界面, 可进行实时图形配置调试, 切换内置图形, 自定义图形, 图形商店… | -
容器开发 | -- | -打开容器开发界面, 进行栅格布局调整, 拖拽方式不常用… | -
隐藏容器 | -- | -将容器切换为未激活状态,如果永久删除… | -
-
功能项 | -子项 | -使用说明 | -
---|---|---|
模板开关 | -- | -开启模板开发, 新增拖拽图形后会自动开启 | -
移动适配开关 | -- | -不固定宽高比例模式, 如果非大屏建意开启 | -
VUE | -- | -开启后可以使用VUE模式开发 | -
DATAV | -- | -开启后可以使用DATAV的组件及VUE | -
极简菜单 | -- | -开启后隐藏仪表盘中不常用工具图标 | -
数据集模式 | -- | -开启后仅用于查看和管理数据集 | -
背景模板 | -- | -可选择预设的大屏背景 | -
-
功能项 | -子项 | -使用说明 | -
---|---|---|
标题 | -修改名称 | -修改数据集名称 | -
- | 联动设定 | -图形联动相关的配置 | -
- | 权限设定 | -数据集权限管理 | -
数据源 | -- | -点击显示默认数据库的表清单 | -
- | 切换数据源 | -切换数据源 | -
- | 编辑数据源 | -编辑当前数据源配置 | -
- | 新增数据源 | -新增数据源 | -
- | 刷新设定 | -设定数据集的后台缓存及前端刷新间隔时间 | -
- | 数据库清单 | -获取当前数据源所有的数据库清单 | -
- | 表清单 | -不选择数据库时, 获取默认数据库表清单, 选中数据库名称,可获取选中数据库表清单 | -
- | 表结构 | -选中表名,可获取表的字段描述 | -
- | 建表语句 | -选中表名,可获取表的建表语句 | -
- | 表样列 | -选中表名,可获取表的样列数据 | -
- | 表条数 | -选中表名,可获取表的总条数, 也可选中一段查询获取总条数 | -
工具 | -代码折叠 | -折叠选中的代码 | -
- | 代码展开 | -展开选中的代码 | -
- | 代码格式化 | -格式化选中的代码 | -
- | 获取执行脚本 | -一般用于参数调试时获取实际脚本 | -
GPT | -- | -进行AI问答式开发 | -
执行 | -- | -执行预览数据集,或选中一段脚本执行 | -
保存数据集 | -- | -保存并刷新数据集后台缓存 | -
编辑器设定 | -- | -设定编辑器的主题等 | -
-
功能项 | -子项 | -使用说明 | -
---|---|---|
实时调试 | -- | -默认开启, 修改配置后, 实时显示图形, 当大量数据处理时, 建意关闭 | -
调色板 | -- | -可进行颜色选择, 需复制颜色代码到配置项 | -
图形 | -基础图形 | -系统内置基础图形, 如柱/线/点/饼/中国式报表…, 可通过配制衍生出各种图形.. | -
- | 个人图形 | -可以保存个人修改或转化过的图形进行复用 | -
- | 图形商店 | -参考smartchart官方样列图形, 复制粘贴即可使用 | -
- | Echarts社区 | -参考echarts官方样列图形, 复制粘贴后转化即可使用 | -
参考 | -- | -常用的echarts配置项菜单, 复制粘贴即可使用 | -
工具 | -转化 | -将echarts原生图形转化为smartchart | -
- | 代码 | -代码折叠/展开等 | -
功能项 | -子项 | -使用说明 | -
---|---|---|
图形 | -- | -适用于在将静态模板转化过程中或复杂布局场景使用, 常规建意采用在仪表盘界面中新增 | -
容器 | -拖拽 | -选中组件代码段, 将为组件加上拖拽容器代码 | -
- | 栅格 | -选中组件代码段, 将为组件加上栅格容器代码 | -
- | DATAV边框 | -选中组件代码段, 将为组件加上DATAV边框容器代码 | -
编辑 | -数据集 | -选中带图形的代码段div_list.xx, 打开数据集编辑, 可用快捷键CTRL+Q | -
- | 图形 | -选中带图形的代码段, 打开图形编辑 | -
- | 容器 | -选中带图形的代码段, 打开容器编辑 | -
样式 | -- | -常样html组件样式, 如背景颜色, 字体.. | -
组件 | -- | -常用html组件, 如图片, 按钮, 轮播.. | -
UI组件 | -- | -常用elementUI组件, 使用方法需参考文档 | -
3D模型 | -- | -加载并使用3D模型场景 | -
资源 | -- | -不太常用的资源, 如加载词云, 地图… | -
工具 | -- | -代码折叠,对齐转化,快照定存,文件上传,数据集批量授权.. | -
编辑器设定 | -- | -设定编辑器的主题等 | -
目前的权限管理,大概如下:
-点击进入后台的图标, 在后台中你可以控制用户的开发权限
-
-
-
-新建用户默认是没有开发权限的, 在首页也看不到任何开发相关的菜单
如果你需要给用户开发权限, 需要设定如下:
-
-
-
-
你可以在 仪表盘设定 中进行权限管理
-
-
-
-
-
-
-
更多功能可自行探索, 比如交互点击响应事件, 贴图等 -也可购买专业版本解锁高级用法
-{
- "smtgpt": {
- "api_key": "你的识别码"
- }
-}
-
<el-select v-model="select" slot="prepend" placeholder="请选择">
- <el-option label="王者荣耀排名" value="smartdemo2"></el-option>
- <el-option label="评论数据集" value="532"></el-option>
-</el-select>
-
现在你可以进行chatgpt数据问答了, 更多应用请参考第6章节"集成 AI 生成"
-实在想不出用什么名称来描述这个功能, 将就这个吧
-
-
-
上线数据集在仪表盘中不可以修改, 如需修改数据集, 按如下图复制出来后即可修改
-
-
由于版本的变更, 一些图标可能会有一些变化, 但位置无太大的变化
-
-
-
select H1 as heroname, sum(qty) as 出场数 from smartdemo2
-group by H1
-order by sum(qty) desc
-
-
--关于数据集内容请参考第 2 章数据集说明
-
-
--关于图形开发内容请参考第 3 章图形开发说明
-
--关于布局内容请参考第 4 章布局说明
-
-
select H1 as 英雄名称, sum(qty) as 英雄1出场数 from smartdemo2
-group by H1 order by sum(qty) desc;
-select count(1) as 总出场数 from smartdemo2
-
-
-
-
-
你也可直接导入excel或wps开发好的模板, 点击左下方的上传即可
-默认的填充方向, 是按列方向向下填充 -如果需要按行方向向右填充, 可在单元格字段标识后面加上__, 如下:
-#df0.英雄名称__
-
默认不会填充格式, 采用单元格自身的格式 -如果需要自动采用"单元格字段标识"的格式, 可在单元格字段标识后面加上!, 如下:
-#df0.英雄名称!
-
--格式填充在数据预览不会生效, 仅在报表预览及用户模式下生效
-
如果需要同时修改填充方向, 可标识如下:
-#df0.英雄名称!__
-
当然我们还有更多可定制的功能, 自行探索吧 -也可购买专业版本解锁高级用法
-- 请先观看迁移视屏
---以上过程建意观看视屏
-
请观看系列视屏, 相信人人都会
-启动显示 以一种访问权限不允许的方式做了一个访问套接字的尝试 -出现这种情况在Windows中很常见,就是端口被占用,酷狗音乐会占用8000端口 -使用netstat -ano|findstr 8000 找到进程号 -使用taskkill /pid 进程号 /F
-输入命令找不到smartchart -检查你是否有安装多个python环境出现环境变量冲突,请卸载一个或取消一个环境变量
-如法安装pip -请确认在安装python时,有没有加入环境变量, 可自行加入, 或卸载重装
-关于mac版本安装后的各种问题,目前来看最大的可能是/Library/Developer/CommandLineTools这个目录下有python3,应该是在某一个版本的Xcode command line tools安装时生成的, -可以先把python3全部卸载,再重新按说明安装,命令行中输入python3 和 pip3, 找不到command时才说明完全卸载成功
-sudo rm -rf /Library/Developer/CommandLineTools
-sudo rm -f /usr/bin/python3
-
如果密码忘记了怎么办 -命令行输入smartchart changepassword 你的用户名
-由于urllib3升级可以出现以下报错, 可以通过pip install urllib3==1.26.15解决
-<PACKAGE> depends on urllib3==2.0.0
-requests 2.29.0 depends on urllib3<1.27 and >=1.21.1
-
试用专业版激活码需要每3天激活一次,激活方式:
-
-
由于开发很忙, 文档可能会写得有不尽之处, 多多包涵
-SQLite3版本错误 -在部分操作系统下(比如CentOS 7)使用SQLite3数据库运行会出现如下的错误提示:
-django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).
-
这表明操作系统自带的sqlite3版本过低,需要将系统的sqlite3进行升级。
-以下是一种方法,来自于 StackOverlow:
-1、下载新版本的SQLite3
-wget https://www.sqlite.org/2019/sqlite-autoconf-3290000.tar.gz
-
2、解压文件
-tar zxvf sqlite-autoconf-3290000.tar.gz
-
3、进行解压后的目录
-cd sqlite-autoconf-3290000
-
4、配置安装目录
-./configure --prefix=$HOME/opt/sqlite
-
5、编译安装
-make && make install
-
6、指定环境变量
-export PATH=$HOME/opt/sqlite/bin:$PATH
-export LD_LIBRARY_PATH=$HOME/opt/sqlite/lib
-export LD_RUN_PATH=$HOME/opt/sqlite/lib
-
完成之后可以运行sqlite3 –version 命令来查看当前的SQLite3版本。
---如果还是不行, 报错如下: - -
--可如下方式处理
# 安装
-pip3 install pysqlite3
-pip3 install pysqlite3-binary
-
-# 编辑django的文件, 路径参考报错
-vi xxxxxx/lib/python3.9/site-packages/django/db/backends/sqlite3/base.py
-# 修改内容
-# from sqlite3 import dbapi2 as Database # 注释掉这里
-from pysqlite3 import dbapi2 as Database
-
-# 然后保存退出就可以了 :wq!
-
* Centos 7
-* Python 3.9
-/data/smartchart/ 项目主目录
-/data/smartchart/tools 项目相关软件
-下述内容中,凡是涉及到/data/smartchart路径的,都可以将其修改为你自己系统上的路径。
-
cd /data/smartchart/tools
-yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel
-下载https://npm.taobao.org/mirrors/python/3.9.0/
-上传服务器,放入安装目录解压 或者
-Wget https://npm.taobao.org/mirrors/python/3.9.0/Python-3.9.0.tgz
-tar -zxvf Python-3.9.0.tgz
-
-进行源码目录
-配置安装路径
-./Python-3.9.0/configure --prefix=/data/smartchart/tools/python3
-编译安装
-make && make install
-建立软链接
-ln -s /data/smartchart/tools/python3/bin/python3.9 /usr/bin/python3
-ln -s /data/smartchart/tools/python3/bin/pip3.9 /usr/bin/pip3
-测试是否安装成功
-python3 --version
-
python3 -m venv myvenv
-cd myvenv
-source bin/activate
-
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple smartchart -U
-
smartchart runserver 0.0.0.0:8000 --insecure --noreload
-
购买专业版本,提供企业生产部署及无网离线部署方案
-Windows部署与正常部署一样
-如果你的原始数据库中表的格式如下, 表名tb_name
-城市 | -户型 | -数量 | -
---|---|---|
长沙 | -A | -12 | -
长沙 | -A | -23 | -
上海 | -B | -19 | -
查询的sql:
-select 城市,户型,sum(数量) AS 数量
-from tb_name group by 城市,户型
-
正常查询的结果为
-[['城市','户型','数量'],
- ['长沙','A',35],
- ['上海','B ',19]]
-
由于生成的数据格式第二行是[字符,字符,数值],后台会智能进行转列动作, -生成图表更容易使用格式:
-[['Categroy','A','B'],
- ['长沙', 35, 0],
- ['上海', 0, 19]]
-
--注意: 在数据集预览可能会看到是第一种查询结果, 但到图形中其实是第二种智能转化过的了
-
再比如表的数据格式, 指标是展开的:
-城市 | -A | -B | -
---|---|---|
长沙 | -10 | -12 | -
上海 | -11 | -19 | -
长沙 | -9 | -10 | -
我们可以写的sql是
-select 城市, sum(A) as A, sum(B) as B
-from tb_name group by 城市
-
这样得到的结果和我们标准格式也是一样的
-[['城市','A','B'],
- ['长沙', 19, 22],
- ['上海', 11, 19]]
-
有时一个数据集可能只用一个SQL查询还不够,比如你需要一个清单数据,同时你需要一个汇总数据做为说明在图形中显示,这样你就需要使用多条SQL语句,在数据集中的写法你只需要用分号隔开,如:
-//数据集中的查询
-select ... from xxx;
-select ..... from xxxxxxx
-
-// 传递到图形中的格式为:
-{"df0":[[...]]. "df1":[[......]]}
-df0, df1分别对应的是第一段和第二段查询
-
开发前建意先观看视屏, 了解基础说明, 视屏有点老和现在界面不一样, -目前很多功能已经做成可视化配置, 理解过程即可, 具体以文档为准
-
-
-
--当然你也可以任意JS中使用"data序号"这个全局变量, 如上就是data2
-
-
-- 英雄1
-select H1, count(1) as qty1 from smartdemo2
-group by H1
-limit 10;
--- 英雄2
-select H2, count(1) as qty2 from smartdemo2
-group by H2
-limit 10
-
-
--注意: 在V6.3版本之前通用数据集的序号必须小于需要引用的数据集, 如通用数据为3, 那么2号数据集是不可以引用3
-
当没有修改过懒加载数据集的图形时,默认的全局变量为"data序号", -例如懒加载数据集序号为0, 你可在模板的script中使用data0来获取数据集刷新后的数据
-在有些场景, 可以在懒加载的图形中自定义处理逻辑, -比如我们需要把数据转化成vue常用的格式, 然后给vue的变量赋值:
-let dataset = __dataset__;
-dataset = ds_createMap_all(dataset);
-vapp.ds1 = dataset; //赋值给vue
-
--当有编辑过图形后,你不再可以用"data序号"来使用数据
-
在有一些场景,需要在前端获取参数, 之后触发数据查询, 比如筛选项. -你可以使用ds_setParam(‘参数名’, 参数值)设定参数, -比如刷新1号数据集
-ds_setParam('city', '顺德');
-ds_setParam('province', '广东');
-ds_refresh(1);
-
smartchart提供非常精细的数据刷新功能,及内存加速功能
-你可以在数据集开发界面的菜单中,连接图标–>刷新设定,进行设定
-
-
你可以设定前端页面数据集向后端请求刷新的时间间隔,单位秒
---如果你发现定时刷新,数据并没有变化,可能原因是您数据的缓存时间设定大于定时刷新的时间
-
smartchart专业版提供内存加速技术,对数据库仅需请求一次,之后都是毫秒级响应
-数据集状态, 决定了数据集是否在页面开启时即刷新, 或刷新是同步还是异步
-状态 | -组 | -定时 | -手动 | -异步 | -
---|---|---|---|---|
标准图形 | -大于0 | -大于0 | -X | -V | -
VUE图形 | -大于0 | -大于0 | -X | -X | -
共用数据集 | --2 | -大于0 | -X | -X | -
懒加载数据集 | --2 | --1 | -V | -X | -
懒加载数据集 | --2 | --2 | -V | -V | -
excel数据集 | --3 | -大于0 | -X | -X | -
请参考文档 - 后台主动触发刷新
-接口返回一定要是JSON格式
-你可选择任意数据连接
-以下是简单的get及post方法样列
-//GET 方法:
-dataset= {
-"url":"https://www.smartchart.cn/smartdata/api/?i=loaddataset1&j=1"
-}
-
-//POST 方法:
-dataset= {
-"url":"https://www.smartchart.cn/smartdata/api",
-"method":"POST",
-"data":{"i":"loaddataset1", "j":"1"}
- ...
-}
-
可以传入参数做出联动效果
-dataset= {
-"url":"https://www.smartchart.cn/smartdata/api",
-"method":"POST",
-"data":{"i":"loaddataset1", "j":"/*$参数名*/"}
-...
-}
-
可以增加header等认证方式
-dataset= {
-"url":"https://www.smartchart.cn/smartdata/api",
-"method":"GET",
-"headers":{"Cookie":"xxxxxxx"}
-...
-}
-
-
-
模糊查询
-body = {
- 'query': { # 查询命令
- 'match': { # 查询方法:模糊查询(会被分词)。比如此代码,会查到只包含:“我爱你”, “中国”的内容
- 'name': '刘'
- }
- },
- 'size': 20 # 不指定默认是10,最大值不超过10000(可以修改,但是同时会增加数据库压力)
-}
-
-term,精准单值查询
-# 注:此方法只能查询一个字段,且只能指定一个值。类似于mysql中的where ziduan='a'
-body ={
- 'query':{
- 'term':{
- 'ziduan1.keyword': '刘婵' # 查询内容等于“我爱你中国的”的数据。查询中文,在字段后面需要加上.keyword
- # 'ziduan2': 'liuchan'
- }
- }
-}
-erms,精准多值查询
-#此方法只能查询一个字段,但可以同时指定多个值。类似于mysql中的where ziduan in (a, b,c...)
-body ={
- "query":{
- "terms":{
- "ziduan1.keyword": ["刘婵", "赵云"] # 查询ziduan1="刘婵"或=赵云...的数据
- }
- }
-}
-multi_match,多字段查询
-# 查询多个字段中都包含指定内容的数据
-body = {
- "query":{
- "multi_match":{
- "query":"我爱你中国", # 指定查询内容,注意:会被分词
- "fields":["ziduan1", "ziduan2"] # 指定字段
- }
- }
-}
-
-prefix,前缀查询
-body = {
- 'query': {
- 'prefix': {
- 'ziduan.keyword': '我爱你' # 查询前缀是指定字符串的数据
- }
- }
-}
-
-# 注:英文不需要加keyword
-
-wildcard,通配符查询
-body = {
- 'query': {
- 'wildcard': {
- 'ziduan1.keyword': '?刘婵*' # ?代表一个字符,*代表0个或多个字符
- }
- }
-}
-# 注:此方法只能查询单一格式的(都是英文字符串,或者都是汉语字符串)。两者混合不能查询出来。
-
-regexp,正则匹配
-body = {
- 'query': {
- 'regexp': {
- 'ziduan1': 'W[0-9].+' # 使用正则表达式查询
- }
- }
-}
-bool,多条件查询
-# must:[] 各条件之间是and的关系
-body = {
- "query":{
- "bool":{
- 'must': [{"term":{'ziduan1.keyword': '我爱你中国'}},
- {'terms': {'ziduan2': ['I love', 'China']}}]
- }
- }
- }
-
-# should: [] 各条件之间是or的关系
-body = {
- "query":{
- "bool":{
- 'should': [{"term":{'ziduan1.keyword': '我爱你中国'}},
- {'terms': {'ziduan2': ['I love', 'China']}}]
- }
- }
- }
-
-# must_not:[]各条件都不满足
-body = {
- "query":{
- "bool":{
- 'must_not': [{"term":{'ziduan1.keyword': '我爱你中国'}},
- {'terms': {'ziduan2': ['I love', 'China']}}]
- }
- }
- }
-
-
-
-# bool嵌套bool
-# ziduan1、ziduan2条件必须满足的前提下,ziduan3、ziduan4满足一个即可
-body = {
- "query":{
- "bool":{
- "must":[{"term":{"ziduan1":"China"}}, # 多个条件并列 ,注意:must后面是[{}, {}],[]里面的每个条件外面有个{}
- {"term":{"ziduan2.keyword": '我爱你中国'}},
- {'bool': {
- 'should': [
- {'term': {'ziduan3': 'Love'}},
- {'term': {'ziduan4': 'Like'}}
- ]
- }}
- ]
- }
- }
-}
-
--首先你需要新建python连接器, 由于安全控制只允许超级管理员建立
-
-
# 变量说明
-一般使用ds来表达数据集类型的变量, 如ds1,ds2
-一般使用df来表达pandas对象, 如df1,df2
-# 内置函数说明
-ds_get(id) #获取目标ds数据集[不常用]
-ds_sql(conn_name, sql) #通过数据源连接的名称及SQL语句获取ds数据集
-ds_df(ds) #将ds数据集转化成pandas的df对象
-ds_list(df) #将pandas的df对象转化成ds数据集
-
读取Excel数据处理, 如需上传页面可参考"数据上传"说明
-import pandas as pd
-# 读取excel文件给df对象
-df = pd.read_excel('文件路径', '表名')
-# 按照省份列统计数量列的加总
-df = df.groupby('省份').agg({'数量':'sum'}).reset_index()
-# 将df对象转化为ds输出
-ds = ds_list(df)
-
生成字典格式的数据集供多个图形使用
-import pandas as pd
-df = pd.read_excel('/Users/../smartdemo.xlsx', 'demo')
-# 生成0号df
-df0 = df.groupby('c3').agg({'qty':'sum'}).reset_index()
-# 生成1号df
-df1 = df.groupby(['province','c3']).agg({'qty':'sum'}).reset_index()
-# 转化为ds并输出
-ds = {'df0': ds_list(df0), 'df1': ds_list(df1)}
-
直接执行SQL,可用于跨数据源处理, 比如来自两个不同系统的数据关联
-import pandas as pd
-# 查询条件一
-sql_str1 = '''select H1 as heroname, sum(qty) as 出场数 from T
-/* where H2 = '$H2' */
-group by H1 order by sum(qty) desc'''
-# 查询条件二
-sql_str2 = 'select heroname, qty as 上月出场数 from xxx'
-
-# 获取第一个df数据
-ds1 = ds_sql('连接名称1', sql_str1)
-df1 = ds_df(ds1)
-# 获取第一个df数据
-ds2 = ds_sql('连接名称2', sql_str2)
-df2 = ds_df(ds2)
-# 关联两个数据源的数据
-df = pd.merge(df1,df2,how='left',on=['heroname'])
-ds = ds_list(df)
-
注意: 最终需要把数据集的结果赋值给ds变量!!
---参数传递的方法与标准数据集一样
-
import pandas as pd
-# 一次性读取excel的多个表格
-dfs = pd.read_excel('/../demo.xlsx', ['sheet1','sheet2'])
-# 指定表格名称,获取表格的df
-df1 = dfs['sheet1']
-# 选取指定列
-df = df[['date','time']]
-# 选择指定行列
-df = df.loc[:10,['A','B','C']]
-# 按照条件筛选
-df[df.A==10]
-df[df.A.isin([10,20,30])] #包含
-df[(df.A>10)&(df.B=='a')|(df.C=='c')] # 多条件且或
-# 重命名列
-df.columns=['A','B','C']
-# 列日期处理
-df['时间'] = df['时间'].dt.strftime('%Y-%m-%d')
-# 自定义列处理
-df['B'] = df['B'].apply(lambda x:x*2)
-# 指定列排序
-df.sort_values('时间')
-df.sort_values(['A','B'], ascending=False)
-# 合并多个df需要表头一样
-df = pd.concat([df1,df2]).reset_index()
-# 指定关联字段名,关联两个df(left, inner, right, outer)
-df = pd.merge(df1,df2,how='left',on=['key1','key2'])
-# 聚合统计
-df = df.groupby(['province','c1'])['qty'].sum().reset_index()
-df = df.groupby("employees")["score"].agg(["sum","max","min","mean","size"]).reset_index()
-df = df.groupby("employees").agg({"salary":"sum", "score":"mean"}).reset_index()
-# 透视表
-df=pd.pivot_table(df,index=['C1','C2'],values=['D1','D2','D3'],aggfunc=[np.sum,np.mean],fill_value=0)
-df=df.pivot(index='C1', columns='C2')['D1']
-
你可能会有这样的一些需求, 展示数据是要通过外部的程序计算好,如一些实时的计算场景,用spark计算好的数据 或爬虫爬取的数据, 然后写入redis或nosql的数据库,最后由前端图形直接展示或数据下载,SmartChart支持这一块的应用
-你可以创建一个redis的连接池, 然后按照通用的方法建立数据集 -不同的是, 数据集的SQL区不再是写sql代码, 而只需要写redis中的keyname
-如redis中存储的数据是keyname 为 “指标A”, 数据 ‘{“长沙”:1,“上海”:2}’ -这样我们只需要在数据集中写上
-指标A
-
即可, -最后你会得到{“长沙”:1,“上海”:2}的返回结果
-如果你需要的是表格格式, 那么你只需要往redis中存入一个二维数组, 比如: -[[“省份”,“数量”],[“长沙”,1],[“上海”,2]]
---注意数据存入redis为字符串格式,你可使用python的json.dumps来生成字符串格式存入
-
比如还有一个"指标B", 数据是'12345' -我们可以同时写上两个指标,用分号隔开:
-指标A;指标B
-
最后你会得到的结果是: -{ -“指标A”:{“长沙”:1,“上海”:2}, -“指标B”:12345 -}
-用于获取kafka指定分区的最后一条记录, 用于实时场景 -使用方法参考"自定义数据源" -以下为参考代码:
-def dataset(*args, **kwargs):
- """
- 返回查询数据集
- :return: 二维数组或JSON字典
- """
- from kafka import KafkaConsumer, TopicPartition
- import json
-
- sqlList = args[0] # 数据集编辑界面的输入已按分号拆分成数组 [sql1, sql2...]
- config = args[1] # 相关的配置字典{'host','port','user','password','db'}
- # 插入你的数据查询及处理代码, 生成result即可
- result = {}
- consumer = KafkaConsumer(sasl_mechanism='PLAIN',
- security_protocol='SASL_PLAINTEXT',
- sasl_plain_username=config['user'],
- sasl_plain_password=config['password'],
- bootstrap_servers=config['host'],
- auto_offset_reset='earliest',
- api_version=(1, 0, 0),
- consumer_timeout_ms=50,
- value_deserializer=lambda v: json.loads(v.decode('utf-8')),
- )
- topic = sqlList[0]
- partition = int(config['db'])
- tp = TopicPartition(topic=topic, partition=partition)
- consumer.assign([tp])
- end_offsets = consumer.end_offsets([tp]).get(tp) # 获取当前消费者最大偏移量
- consumer.seek(tp, offset=end_offsets-1)
- for message in consumer:
- result = message.value
- break
- return result
-
-def insert_dataset(*args, **kwargs):
- """
- 数据填报实现
- """
- from kafka import KafkaProducer
- import json
-
- contents = args[0] # 传入的数据集二维数组格式
- table = args[1] # 配置中的表名
- config = args[3] # 相关的配置字典{'host','port','user','password','db'}
- # 插入你的写入数据逻辑代码
- producer = KafkaProducer(sasl_mechanism='PLAIN',
- security_protocol='SASL_PLAINTEXT',
- sasl_plain_username=config['user'],
- sasl_plain_password=config['password'],
- bootstrap_servers=config['host'],
- value_serializer=lambda v: json.dumps(v).encode('utf-8')
- )
- producer.send(table, value=contents, partition=int(config['db']))
-
连接池正常配置即可
-数据集开发中,填写查询需求:
-
-
{"db": "db1", "table": "tb1", "filter": {"name": "Zarten"},
-"projection": {"_id": 0}, "sort": [["_id", 1]], "limit": 10}
-
由于返回的字典格式, 如需转化成二维数组, 可使图形中的转化函数ds_mapToList
-let dataset=ds_mapToList(__dataset__);
-
除table,其它都为可选参数
-参数 | -说明 | -样列 | -
---|---|---|
db | -数库名,默认连接设定中db名 | -- |
table | -表名[必填] | -- |
filter | -筛选项,具体用法参考下文 | -{“name”: “Zarten”,“date”:“2020-10-01”} | -
projection | -显示列 | -{“name”: 1,“date”:1} | -
sort | -排序,-1为降序 | -[[“date”, -1]] | -
limit | -限定返回数量 | -- |
且条件
-{"age":{"$gt":22}, "name":{"$regex":"user"}}
-
或条件
-{ "$or": [ {"age": {"$gt": 22}}, {"name": {"$regex": "user"}} ] }
-
比较查询 -$lt和<,$lte和<=,$gt和>,gte和>=,ne和!=是一一对应的
-{"field_name": {"$lt": value, "$gt": value}}
-
关联查询$in和$nin
-{"field_name": {"$in": [1,5,8]}}
-
$regex为模糊查询的字符串提供正则表达式功能
-{"$or": [{"field_name": {'$regex': value}},{"field_name2": {"$regex": value}}]}
-
驱动: prometheus -连接地址: http://ip:9090
-输入常规查询命令即可
-
-
-返回值为prometheus标准json, 你需要在图形编辑器中使用js处理数据
let dataset=__dataset__;
-//获取返回值列表中的第一值
-dataset = dataset.data.result[0].value;
-print(dataset)
-
如获得的数据
-{
-"status":"success",
-"data":{
- "resultType":"vector",
- "result":[
- {"metric":{"__name__":"starrocks_fe_routine_load_jobs","group":"fe","instance":"xxx:8030","job":"StarRocks_Cluster01","state":"CANCELLED"},
- "value":[1671188429.573,"0"]
- },
- {"metric":{"__name__":"starrocks_fe_routine_load_jobs","group":"fe","instance":"xxx:8030","job":"StarRocks_Cluster01","state":"NEED_SCHEDULE"},
- "value":[1671188429.573,"2"]}
- ...
- ]
- }
-}
-
图形中处理成smartchart标准格式
-//处理prometheus
-let df = __dataset__;
-let result = df.data.result;
-let dataset = [['instance','state','qty']]; //二维表头
-for(let item of result){
- let pmetric = item.metric;
- let pvalue = item.value;
- dataset.push([pmetric.instance,pmetric.state,pvalue[1]]);
-}
-dataset = ds_pivot(dataset); //列转行
-
如果带时序的, 数据处理参考
-let df = __dataset__;
-let result = df.data.result;
-let dataset = [['instance','seq','qty']]; //二维表头
-for(let item of result){
- let pmetric = item.metric;
- let pvalues = item.values;
- let startv = pvalues[0][1]; //初始值
- for(let i=1; i<pvalues.length; i++){
- let pvalue = pvalues[i];
- dataset.push([i,pmetric.instance,pvalue[1]-startv]); //计算增长值
- }
-}
-dataset = ds_pivot(dataset); //列转行
-
你可能会用上的时间戳转文本格式
-function getLocalTime(nS) {
- return new Date(nS * 1000).toLocaleString();
-}
-
使用分号(;)分隔查询
-
-
常规的连接池的设定, 大家应该都很清楚了,
-Smartchart也支持sqlalchemy连接, 对于一些smartchart不支持的数据源可以使用此方法
-配置方法:
-
-
-只用填以上内容, 其它可留空
-连接地址的写法参考sqlalchemy说明:
可选参数。一个标准的链接URL是这样的:
-dialect+driver://username:password@host:port/database
-dialect,是数据库类型,大概包括:sqlite, mysql, postgresql, oracle, or mssql.
-driver,是使用的数据库API,驱动,连接包,随便叫什么吧。
-username,用户名
-password,密码
-host,网络地址,可以用ip,域名,计算机名,当然是你能访问到的。
-port,数据库端口。
-databas,数据库名。
-其实这些也就dialect和dirver需要解释。
-
-二:连接sqlite3
-1,驱动
-sqlite3是个文件数据库,不需要什么驱动,或者说python内置了驱动。
-2,标准连接参数
-# sqlite://<nohostname>/<path>
-没有hostname
-3,各种链接参数
-# 相对路径,就是这个python文件同目录下foo.db
-engine = create_engine('sqlite:///foo.db')
-#绝对路径
-#Unix/Mac下用四条////表示
-engine = create_engine('sqlite:////absolute/path/to/foo.db')
-#Windows下用三条///加盘符路径用两条\\
-engine = create_engine('sqlite:///C:\\path\\to\\foo.db')
-#Windows 也可以这么用三条///加盘符路径用一条\
-engine = create_engine(r'sqlite:///C:\path\to\foo.db')
-#数据库建在内存里。URI保持为空即可
-engine = create_engine('sqlite://')
-
-三:连接mysql(mariadb)
-sqlalchemy默认使用mysql-python作为链接驱动,既default模式
-选哪种驱动,就装哪个包。
-1,default默认链接方式
-engine = create_engine('mysql://scott:tiger@localhost/foo')
-2,# mysql-python,声明使用mysql-python驱动
-engine = create_engine('mysql+mysqldb://scott:tiger@localhost/foo')
-3,MySQL-connector-python 声明使用MySQL-connector-python驱动(推荐使用)
-engine = create_engine('mysql+mysqlconnector://scott:tiger@localhost/foo')
-4,OurSQL 声明使用OurSQL驱动
-engine = create_engine('mysql+oursql://scott:tiger@localhost/foo')
-
-四:连接Microsoft SQL Server
-sqlalchemy默认使用 pyodbc作为链接驱动。
-1,pyodbc
-engine = create_engine('mssql+pyodbc://scott:tiger@mydsn')
-2,pymssql
-engine = create_engine('mssql+pymssql://scott:tiger@hostname:port/dbname')
-
-
-五:连接PostgreSQL
-PostgreSQL默认使用 psycopg2作为链接驱动,既default模式
-1, default
-engine = create_engine('postgresql://scott:tiger@localhost/mydatabase')
-2,psycopg2
-engine = create_engine('postgresql+psycopg2://scott:tiger@localhost/mydatabase')
-3, pg8000
-engine = create_engine('postgresql+pg8000://scott:tiger@localhost/mydatabase')
-
- 六:连接Oracle
-Oracle可能只有 cx_oracle一个驱动包,既default模式和声明模式一样。
-1,default
-engine = create_engine('oracle://scott:tiger@127.0.0.1:1521/sidname')
-2,cx_oracle
-engine = create_engine('oracle+cx_oracle://scott:tiger@tnsname')
-
对于实时程度要求比较高的情况下, 如果你后端已有实现websocket的接口, smartchart也可以很方便的接入 -参考以下步骤即可
-
-
let ws_data = [['初始化','V'],['A','2']];
- let ws = null;
-
- if('webSocket' in window){
- print('支持webSocket');
- ws = new webSocket('ws://127.0.0.1:2222/abc');
- //连接成功
- ws.onopen = function(){
- print('ws连接成功');
- }
- //接收消息
- ws.onmessage = function(evt){
- ws_data = evt.data;
- ds_refresh(0);
- }
- }
- else{
- print('浏览器不支持ws')
- }
-
有一些场景, 比如已有一些固定的筛选器,或是需要测试用,或者Demo,或者其它图形需要用到一些共用的已确定好的数据 -这样我们可以不需要通过查询数据库的方式, 而直接写入数据集, 支持数组和字典的格式 -你只需要在数据集中起始写入 dataset= , 这样就是默认是固定数据
-
-
-
-
-
smartchart默认是不自带文件上传功能 -但是smartchart是可以自已创造上传页面, 在模板商店中你可以找到相关模板进行购买 -然后通过模板下载的方式下载后进行操作 -这样每一个页面是可以单独使用权限控制的,就和控制报表权限一样,你还可能按需随意定制页面
-
-
默认的上传主目录是在项目的log的文件夹下面, -你可以在setting.py(自定义django) 或 config.ini中设定UPLOAD_PATH来修改你的上传目录 -比如你上传页面的报表ID是23, 那么文件将会被上传到UPLOAD_PATH/23/你的文件名
-你需要使用 - python连接器, 来操作你的上传的数据, 内置了变量ds_path为你的上传目录, 所以可以更方便的读取上传的文件,如上文件 -df = pd.read_excel(ds_path+’/23/文件名')
-用于smart-vector向量数据库 -使用方法参考"自定义数据源" -以下为参考代码, 企业级向量化方案需专业版本:
-from smart_chart.common.smartvector import SmartVectorDB, Text2VecEmbeddingFunction
-text_vector = Text2VecEmbeddingFunction()
-
-def dataset(*args, **kwargs):
- promote = args[0][0]
- db_config = args[1]
- table = db_config.get('table', 'vectors_key')
- return SmartVectorDB(db_config=db_config, text_vector=text_vector, table=table).get(promote)
-
-
-def insert_dataset(*args, **kwargs):
- contents = args[0]
- table = args[1]
- connect_dict = args[3]
- docIndex = contents[0].index('document')
- contents[0].append('embedding')
- for item in contents[1:]:
- item.append(str(text_vector(item[docIndex])[0]))
- SmartVectorDB(db_config=connect_dict, text_vector=text_vector)._execute_load(contents, table)
- return len(contents) - 1
-
--smartchart已实现大部分常用的数据源连接, 对于其它的, 您也可以使用python数据源进行处理 -但是使用python数据源有一定的缺陷, 需要在dataset上写python代码, 不能复用 -对于一些带连接信息的还需要重复写入 -所以你还可以使用自定义数据源
-
def dataset(*args, **kwargs):
- """
- 返回查询数据集
- :return: 二维数组或JSON字典
- """
- sqlList = args[0] # 数据集编辑界面的输入已按分号拆分成数组 [sql1, sql2...]
- config = args[1] # 数据连接界面的配置{'host','port','user','password','db'}
- input = sqlList[0] # 数据集界面的输入
- host = config['host']
- user = config['user']
- password = config['password']
- # 插入你的数据查询及处理代码, 生成result即可
- result = [[]]
-
- return result
-
-def insert_dataset(*args, **kwargs):
- """
- 数据填表实现
- """
- contents = args[0] # 传入的数据集二维数组格式
- table = args[1] # 配置中的表名
- config = args[3] # 相关的配置字典{'host','port','user','password','db'}
- # 插入你的写入数据逻辑代码
-
在任意的仪表盘开发界面中 “模板” –> 点击上传图标, 将这个python文件上传即可
-
-
新建数据源, 驱动填写之前上传过的文件名, 比如我们这个是diy_conn, 其它参数按照你自定义的需求填写, 会自动传入你的自定义函数,之后你可以正常使用这个自定义数据源了
-
-
--如果你对python不熟悉, 也可咨询客服按需定制, 你只需上传即可使用
-
驱动: smtpmail -连接地址: smtp.xxx -库名:为空采用SMTP, 非空采用SMTP_SSL
-在数据集编辑器加直接输入
-{
- "tolist":"xx@qq.com",
- "sub":"测试",
- "content":"<h3>测试一下</h3>"
-}
-
Smartchart提供了很多通用的图形,你可以在商店中直接使用 -如果要个性化,需要你进行自定义,比如你可能需要在同一个图上展示柱形图和线性图
-开发前建意先观看视屏, 了解基础说明, 视屏有点老和现在界面不一样, -目前很多功能已经做成可视化配置, 理解过程即可, 具体以文档为准
-首先我们在ECHART官网可能找一个你喜欢的图形, 如下简单柱形图链接:
-
-
-
打开我们可以看对应的option:
-option = {
- xAxis: {
- type: 'category',
- data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
- },
- yAxis: {
- type: 'value'
- },
- series: [{
- data: [120, 200, 150, 80, 70, 110, 130],
- type: 'bar'
- }]
-};
-
复制到Smartchart图形编辑器, 点击菜单工具–>转化为smartchart, 会自动进行初步转化
-
-
接下来我们就进行下改造, 请注意对比, 你只需照着复制即可
-let dataset = __dataset__ //传入dataset
-let legend_label = ds_rowname(dataset) //可选, 自动获取legend
-let xlabel = dataset[0].slice(1) //x轴的标签列
-dataset = ds_createMap(dataset) //转化成KV格式
-
-//初始化series
-let series=[];
-series.push({
- data: dataset[legend_label[0]], //对应的第一个图列
- type: 'bar'
- });
-series.push({
- data: dataset[legend_label[1]], //对应的第二个图列
- type: 'line'
- });
-
-option__name__= {
- xAxis: {
- type: 'category',
- data: xlabel //X轴的标签
- },
- yAxis: {
- type: 'value'
- },
- series:series,
-};
-
这样一个柱形+线性图就出来了
-
-
-
当然一个图形还有很多其它的元素, 比如标题, legend, 等等 更多option的配置项, 可以点击”!“号图标查看,你可以直接参考echarts的设定, 完全一样!!
-以下我们做了些简单的修改
-option__name__ = {
- title: {
- text: '自定义图示例',
- left: 'center'
- }, //定义标题的显示
- tooltip: {
- trigger: 'item',
- formatter: '{a} <br/>{b} : {c}' //鼠标移动提示的格式
- },
- legend: {
- left: 'left',
- data: legend_label
- }, //定义图例的显示
- xAxis: {
- type: 'category',
- data: xlabel
- }, //定义X轴的显示
- yAxis: {
- type: 'value'
- },
- //图例定义
- series:series,
-};
-
//关于自动化series, 可以参考以下代码
-let series =[];
-for (let i=1;i<dataset[0].length;i++){
- series.push({type: 'bar'})
-}
-
Smartchart让你使用echarts没有门槛
-myChart__name__.setOption(option__name__);
-
需要开启模板开发模式, 并开启basevue模板
-
-
<!--表格-->
- <div class="smtdrag" id="id_1654907858638">
- <el-table
- :data="tableData.slice((currentPage-1)*pageSize, currentPage*pageSize)"
- height="100%"
- size="mini"
- header-cell-class-name="tablehead"
- border
- style="width: 100%">
- <el-table-column v-for="item in tableHead" :label="item.label" :property="item.prop" sortable>
- </el-table-column>
- </el-table>
- <!--表格结束-->
- <!--分页控件-->
- <el-pagination align='center'
- @size-change="handlerSizeChange"
- @current-change="handlerCurrentChange"
- :current-page="currentPage"
- :page-size="pageSize"
- layout="total,sizes,prev,pager,next,jumper"
- :total="tableData.length"
- ></el-pagination>
- <!--分页控件结束-->
- </div>
-
var vapp = new Vue({el: '#vue_app', delimiters: ['{[', ']}'],
- data: {
- tableData:[], //表数据
- tableHead:[], //表头
- currentPage:1,
- total:20,
- pageSize:10
-
- },
- methods: {
- //处理分页数量
- handlerSizeChange(val){
- this.currentPage = 1;
- this.pageSize=val;
- },
- //处理页选择
- handlerCurrentChange(val){
- this.currentPage = val;
- }
- }
-
- });
-
在数据集编辑器中写入查询
-select * from smartdemo2
-limit /* $limit -- */ 100
-
let df0 = __dataset__;
-//处理表头
-let columnsDict = {'c1':'渠道','qty':'数量'};
-let tableHead = [];
-let tableHeadLabel;
-for (let i=0;i<df0[0].length;i++){
- if(columnsDict.hasOwnProperty(df0[0][i])){
- df0[0][i] = columnsDict[df0[0][i]]
- }
- tableHeadLabel=df0[0][i];
- tableHead.push({label: tableHeadLabel, prop:df0[0][i]});
-}
-
-//VUE赋值
-vapp.tableHead = tableHead;
-vapp.tableData=ds_createMap_all(df0);
-
-
--smartchart内置了这个查询模板, 你可以通过 - 本地模板恢复快速应用
-
--如果你还不熟悉html, 建意先花几分钟看下文档, 推荐 - HTML基础 -实际应用中有不熟悉的组件, 你都可以通过baidu搜索到, 如时间选择器 - -
-
比如我们要实现一个有多选项和按钮的网页元素
-
-
从各大搜索平台上我们可以找到html的代码是:
-<label><input type="checkbox">孙尚香</label>
-.....
-<button id='id_select0'>提交</button>
-
那么我们可以直接在图形编辑器写上
-let dataset=__dataset__;
-let table = '';
-table = `<label><input type="checkbox">孙一香</label>
- <label><input type="checkbox">孙二香</label>
- <label><input type="checkbox">孙三香</label>`
-table = table + "<button id='id_select0'>提交</button>"
-
-dom__name__.innerHTML=table;
-
但是由于我们是要通过传入的数据动态变化的,所以只需要做简单修改
-let dataset=__dataset__;
-let table = '';
-for (let i=1;i<dataset.length;i++){
- table = `${table}<label><input type="checkbox"/>${dataset[i][0]}</label> `
-}
-table = table + "<button id='id_select__name__'>提交</button>"
-
-dom__name__.innerHTML=table;
-
所有html你都可以进行转化成smartchart组件, -你可以通过学习”万能表格系列视屏“ 来了解通用组件开发 - - 第一波 - - 第二波 - - 第三波
-在做自定义html组件的时候你可能需要用得上:
-一、向上遍历
-$("span").parent().css({
- "color":"red",
- "border":"1px solid red"
-})
-
$("span").parents().css({
- "color":"red",
- "border":"1px solid red"
-})
-
$("span").parentsUntil("div").css({ //向上查找直到遇见div元素为止
- "color":"red",
- "border":"1px solid red"
-})
-
二、向下遍历
-1. children() 查找子元素[按照从属关系]
-
-$("ul").children("li:first-child")
-
-2. find() 按照指定的条件向下查找
-
-$("ul").find("span")
-
三、水平遍历
-1. siblings() 获取元素的所有兄弟元素
-
-$(".start").siblings().css({color:"red",border:"2px solid red"})
-
-2. next() 获取元素的下一个兄弟元素
-
-$(".start").next().css({color:"red",border:"2px solid red"})
-
-3. nextAll() 获取其后的所有兄弟元素
-
-$(".start").nextAll().css({color:"red",border:"2px solid red"})
-
-4. nextUntil() 查找后面所有的兄弟元素,直到遇见某个元素为止
-
-$(".start").nextUntil("li:last-child").css({color:"red",border:"2px solid red"})
-
-5. prev() 查找上一个兄弟元素
-
-$("li.start").prev().css({color:"red",border:"2px solid red"})
-
-6. prevAll() 查找上面所有的兄弟元素
-
-$("li.start").prevAll().css({color:"red",border:"2px solid red"})
-
-prevUntil() 查找上面所有的兄弟元素,直到遇见某个元素为止
-$(".start").prevUntil("li:first").css({"color":"red","border":"2px solid red"})
-
四、过滤
-1. first() 获取第一个元素
-
-$("li").first().css("color","red");
-
-2. last() 获取最后一个元素
-
-$("li").last().css("color","red");
-
-3. not() 获取不是…的元素
-
-$("li").not(":eq(2)").css("font-size","26px");
-
-4. eq(n) 获取索引为n的元素
-
-$("li").eq(3).css("background","green");
-
-5. has() 检测某个子元素是否存在
-
-$("li").eq(1).has("span").length)
-
-6. filter() 筛选出与符合条件的DOM元素
-
-$("div")..filter(".middle")
-
-7. is() 用来判断是否符合条件
-
-$("p").parent().is("div") //判断p的父元素是不是div,是就返回true,不是就返回false
-
五、each遍历
-1. each() 方法为每个匹配元素规定要运行的函数。
-
-$(selector).each(function(index,element){
- .....
-})
-//index 表示当前遍历元素的索引
- element 当前的元素(也可使用 "this" 选择器)
-
-2. $.each(obj,function( index,value){})
-
在图形开发中,我们可能需要使用js对传递过来的数据进行处理
-函数名 | -函数说明 | -样列 | -
---|---|---|
ds_transform(dataset) | -行列转置 | -- |
ds_createMap(dataset) | -将数组生成结果表示为key->[], 常用于echarts指定数据 | -- |
ds_createMap_all(dataset) | -将二维数组转成字典[{A:A1,B:B1,C:C1},…], 常用于饼图 | -- |
ds_mapToList(dataset) | -将字典还原成二维数组, 常用于将nosql(mongodb,es..)数据源数据处理 | -- |
ds_pivot(dataset) | -将二维数组(维度,维度,值)的第二列的维度透视为行 | -- |
ds_distinct(dataset) | -对单个或多个二维数组去重 | -- |
ds_filter(dataset, fun) | -fun为函数如: item=>item[0]==‘顺德’ | -- |
ds_sort(dataset, index=0, asc=true) | -按照列序号排序,默认升序,index参数可以是函数,如(a,b)=>a.qty-b.qty | -- |
ds_remove_column(dataset,remove_list=[0]) | -默认移除第一列, 也要移除指定的多个列 | -- |
ds_split(data,sep=’,’,head_add=[]) | -将第一列拆分成多个字段,默认逗号分隔, 如果不传表头,取SQL中的字段名拆分 | -- |
函数名 | -函数说明 | -样列 | -
---|---|---|
ds_leftjoin(a,b) | -按照第一列左关联两个数据集 | -- |
ds_crossjoin(a,b) | -- | - |
ds_fulljoin(a,b) | -- | - |
ds_union(a,b) | -合并两个数据集,取第一个数据集的表头 | -- |
函数名 | -函数说明 | -样列 | -
---|---|---|
ds_param(name) | -传入参数名,获取图形点击时传递来的参数值 | -- |
ds_setParam(‘参数名’, 参数值) | -设定全局参数, 此方法将自动判断当参数值为空时, 删除参数回到初始未传参状态 | -- |
ds_refresh(序号, param=filter_param) | -刷新图形,默认采取全局参数刷新,也可指定param,参数为字典{“参数名”:“值”,…} | -- |
函数名 | -函数说明 | -样列 | -
---|---|---|
ds_rowname(dataset,start_row=1,column=0) | -获取指定列的数据, 常用于获取维度 | -- |
ds_toThousands(num) | -转逗号分隔的千分位 | -- |
ds_round(num,qty=2) | -小数点处理, 默认保留两位小数 | -- |
函数名 | -函数说明 | -样列 | -
---|---|---|
ds_excel_refresh(dataset) | -刷新复杂报表, dataset格式:{df0:二维数组, df1:二维数组,..} | -- |
ds_excel_value(fillCells,clear=false) | -指定单元格获取复杂报表中的数据, fillcells格式:[‘A1’,‘B2’],一般用于数据填报 | -- |
函数名 | -函数说明 | -样列 | -
---|---|---|
ds_save(序号, contents) | -- 保存数据 | -- |
ds_download(文件名, dataset) | -- 下载数据 | -- |
ds_uploadfile(file, filename, callback=null) | -上传文件 | -- |
//数组追加
-dataset.push(item)
-//数组前方插入
-dataset.unshit(item)
-//切片
-dataset = dataset.slice(1) //从序号1个开始到最最后一个
-dataset = dataset.slice(5, 10) //从第序号5开始截取到第10个
-dataset = dataset.slice(-3) //截取最后三个元素
-//循环遍历
-//for最快,但可读性比较差(smartchart推荐)
-//forEach比较快,能够控制内容
-//for...in比较慢,不方便
-for(let i=0; i<dataset.length; i++){
-
-}
-
假设dataset的格式是, SQL = Select 维度1,维度2,数据 from xxxx, 生成的数据集如下
-dataset = [['category','C1','C2'],
- ['R1', 12, 18],
- ['R2', 10, 17] ]
-
result = ds_createMap(dataset)
-结果 = {"category":['C1','C2'],
- "R1" : [12, 10],
- "R2" : [18, 17]}
-
result = ds_createMap_all(dataset)
-结果 = [{"category":"R1", "C1": 12, "C2": 18},
- {"category":"R2", "C1": 10, "C2": 17}]
-
result = ds_rowname(dataset)
-结果 = ['R1','R2']
-
result = ds_transform(dataset)
-结果 = [['category','R1','R2'],
- ['C1', 12, 10],
- ['C2', 18, 17]]
-
//假设需要关联的数据集格式:
-dataset2 = [['category','C3'],
- ['R1', 38],
- ['R6', 13]]
-//处理后的结果:
-result = ds_leftjoin(dataset, dataset2)
-结果 = [['category','C1','C2','C3'],
- ['R1', 12, 18, 38],
- ['R2', 10, 17, 0] ]
-
//比如需要将dataset3的户型变成指标
-dataset3 = [['城市','户型','数量'],
- ['长沙','A',35],
- ['上海','B',19]]
-//处理后的结果:
-result = ds_pivot(dataset3)
-结果 = [["城市","A","B"],
- ["长沙",35,0],
- ["上海",0,19]]
-
//比如移除第1列(序号0)
-result=ds_remove_column(dataset,remove_list=[0])
- 结果 = [['R1','R2'],
- [12, 10],
- [18, 17] ]
-
常规数据集中提到 A类数据源的情况, 格式都是:
-维度A 维度B 数据
-但还有情况比如你有一个数据格式是:
-维度A 维度B 维度C 数据
-你需要在表格中将 A,B维度做维度, 但C做透视为指标名进行展示
-由于我们的数据透视只支持"字符, 字符, 数值"的SQL写法,
-所以如果要多维, 我们需要做下转变, 可以写成:
-select concat_ws(',',维度A,维度B) AS 维度,维度C,SUM(数据) AS 度量
- from tablename group by 维度, 维度C
-得到的数据样式
-dataset=[['维度','C1','C2'....]
- ,['A1,B1',1,1...]
- ,['A2,B2',2,2...]]
-最终在图形数据集处理中, 我们可以使用如下函数进行转化:
-dataset = ds_split(dataset,',',['维度A','维度B'])
-',' : 参数为分隔符
-['维度A','维度B'] : 指第一个字段需要拆分的表头名称
-
-最终得到的数据就是多维度透视
-[['维度A','维度B','C1','C2'....]
-,['A1','B1',1,1...]
-,['A2','B2',2,2...]]
-
-
只需要围绕option进行定制设定。echarts使用 option 来描述其对图表的各种需求,包括:有什么数据、要画什么图表、图表长什么样子、含有什么组件、组件能操作什么事情等等。简而言之,option 表述了:数据、数据如何映射成图形、交互行为。
-
-
在系列之上,echarts 中各种内容,被抽象为“组件”。例如,echarts 中至少有这些组件:xAxis(直角坐标系 X 轴)、yAxis(直角坐标系 Y 轴)、grid(直角坐标系底板)、angleAxis(极坐标系角度轴)、radiusAxis(极坐标系半径轴)、polar(极坐标系底板)、geo(地理坐标系)、dataZoom(数据区缩放组件)、visualMap(视觉映射组件)、tooltip(提示框组件)、toolbox(工具栏组件)、series(系列)、…
-
-
多数组件和系列,都能够基于top / right / down / left / width / height 绝对定位(坐标基于echarts容器)。
-其中,他们每个值都可以是:
-绝对数值(例如 bottom: 54 表示:距离 echarts 容器底边界 54 像素)。
-或者基于 echarts 容器高宽的百分比(例如 right: ‘20%’ 表示:距离 echarts 容器右边界的距离是 echarts 容器宽度的 20%)。
-如下图的例子,对 grid 组件(也就是直角坐标系的底板)设置 left、right、height、bottom 达到的效果。
-
-
如下图, 不同系列映射到不同的坐标系
-
-
在您理解了echarts的配置方法后,你可以在"参考"的菜单中找到对应的常用参考项
-
-
-Echarts的配置项目非常多, 如果需要更多配置, 您可以参考
- 速查手册
-
Smartchart内置了LineUp图形
-LineUp is an interactive technique designed to create, visualize and explore rankings of items based on a set of heterogeneous attributes.
-
-
LineUp图形参考
-ds_loadcss('smt_LineUp');
-ds_loadjs('smt_LineUp');
-let dataset = __dataset__;
-dataset = ds_createMap_all(dataset);
-try{Ljs__name__.destroy()}catch{}
-Ljs__name__ = LineUpJS.asTaggle(dom__name__, dataset);
-
-// 点击选中行响应动作
-Ljs__name__.on(LineUpJS.LineUp.EVENT_SELECTION_CHANGED, (selection) => {
- console.log(Ljs__name__.data._data[selection]);
- //通过以上log可以查看到数据格式, 以下就是标准的联动写法
- filter_param['LineupParam'] = Ljs__name__.data._data[selection].xx
- ds_refresh(2);
-});
-
-//更多响应动作
-Ljs__name__.on(LineUpJS.LineUp.EVENT_HIGHLIGHT_CHANGED, (highlight) => {
-});
-
-// document.querySelector('button#select').addEventListener('click', () => {
-// Ljs__name__.setSelection([1, 2, 3]);
-// });
-
-// document.querySelector('button#highlight').addEventListener('click', () => {
-// Ljs__name__.setHighlight(50);
-// });
-
-// 获取筛选后的数据并下载(来源于"路阳" 赞助开发)
-outputStr=Ljs__name__.data.exportTable(Ljs__name__.data.getRankings()[0], {});
-outputStr = outputStr.replace(/\t/g, ',');
-ds_download('abc.csv', outputStr);
-
在"模板"中加载图标资源
-
-
-使用方法, 可参考
- font-awesome菜鸟教程
-V5图标名称参考, 也可以
- 图标样列查询
-
-
-
-
-
例如你的html如下
-<div id="smtid" style="height:100%">
- <ul>
- <li>smartchart</li>
- <li>bigdata</li>
- <li>echarts</li>
- <li>make it great</li>
- </ul>
-</div>
-
你只需要在图形中使用以下函数, 即可实现在无缝滚动
-//smtid是滚动容器的ID
-ds_liMarquee('#smtid')
-//如果 class="smtclass", 那么也可以使用类选择器
-ds_liMarquee('.smtclass')
-
我们也可以使用更多的配置方法
- marconfig={
- playtime: 3000, //滚动3秒
- pausetime: 3000, //停3秒
- config:{
- direction: 'up',//向上滚动
- runshort: false,//内容不足时不滚动
- scrollamount: 20//速度
- }
- }
-//传入自定义配置
-ds_liMarquee('#smtid', marconfig)
-
更多config说明:
-名称 | -类型 | -默认值 | -说明 | -
---|---|---|---|
direction | -字符串 | -left | -滚动方向,可选 left / right / up / down | -
loop | -整数 | --1 | -循环次数,-1 为无限循环 | -
scrolldelay | -整数 | -0 | -每次重复之前的延迟 | -
scrollamount | -整数 | -50 | -滚动速度,越大越快 | -
circular | -布尔值 | -true | -无缝滚动,如果为 false,则和 marquee 效果一样 | -
drag | -布尔值 | -true | -鼠标可拖动 | -
runshort | -布尔值 | -true | -内容不足是否滚动 | -
hoverstop | -布尔值 | -true | -鼠标悬停暂停 | -
xml | -布尔值 | -false | -加载xml 文件 | -
inverthover | -布尔值 | -false | -反向,即默认不滚动,鼠标悬停滚动 | -
smartchart内置了滚动表格, 可以一键生成
-如需修改表格的样式, 如字体,颜色等, 可以在模板中重定义样式 -具体样式写法, 参考 - 样式快速入门
-/*表头样式*/
-.smtlisthead{
- background: #fff2cc;
- color: red;
- height: 5rem;
-}
-
-.smtlisthead span{
- height: 5rem;
-}
-
-/*表格本体样式*/
-.smtlistnav{
- height: calc(100% - 5rem);
- color: red;
- overflow: auto;
-}
-
-.smtlistnav li span{
- height: 3rem;
-}
-
-/*修改奇数行背景,偶数行将odd改为even*/
-.smtlistnav ul li:nth-child(odd){ background: rgba(100,100,100,.1);}
-
<span>
- <span style="width:32rem;height:100%;flex-shrink:0;justify-content:left"><span>
-</span>
-
let lastClickDom;
-let lastDomColor;
-$('#smtlist__name__, li').click(function(params){
- try{lastClickDom.css('background', lastDomColor)}catch{}
- lastDomColor = $(this).css('background');
- $(this).css('background', 'yellow');
- lastClickDom = $(this);
- let myparam = $(this).children('span').eq(0).text(); //获取点击的参数
- //以下加入你的action
-
-});
-
smartchart默认只会引echarts的基础图形, -如需使用更多图形,可在模板javascript标签中自行引用
-中国地图
-<script src="/static/smartchart/opt/smt_china.js"></script>
-世界地图
-<script src="/static/smartchart/opt/smt_world.js"></script>
-统计图
-<script src="/static/smartchart/opt/smt_ecStat.js"></script>
-水球图
-<script src="/static/smartchart/opt/smt_liquidfill.js"></script>
-词云
-<script src="/static/smartchart/opt/smt_wordcloud.js"></script>
-百度地图
-<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@5/dist/extension/bmap.min.js"></script>
-
-
大家可能比较熟悉使用F12来查看网页日志, 但有的同学会觉得这不够方便, 所以 -smartchart加入了可以页面直接显示日志的功能
-你只需要点击如下菜单, 即可切换是否显示日志
-
-
-当在刷新页面或执行时, 日志将直接显示在右下角中, 而且还能显示出对应出错的图表序号
-
-
smartchart基于python的使用习惯, 重定义的专用的日志打印函数print
-你可以在图形编辑器中使用些函数即可打印日志
-比如看看鼠标放在echarts图上params, 在编辑界面和console中都能看到日志, 方便你进行调试
-
-
不仅仅这些, 你可能会想写太多打印日志, 上线了不好 -smartchart已为你想到这些, 如果你在仪表盘中没有开启日志显示, print函数是不会打印任何日志
-有了仪表盘日志显示, 我们还能做更多的事情, 比如实时显示你拖拽的坐标, 让你精确定位
-
-
对于仪表盘中不再使用的数据集,你可能会考虑删除
-首先smartchart推荐你不做删除,因为你可以保留他, 当你下次有新增数据集的需求时再拿出来
-所以优先推荐使用隐藏的方法,你可以在数据集编辑界面找到他
-
-
-如果你实在需要删除,可以在“报表”界面先中不需要容器后,选中删除,后保存
-
推荐先观看视屏了解 - smartchart布局方式
-容器 | -说明 | -
---|---|
定位容器 | -用于图形定位, 有拖拽和栅格两种. 在界面上新增时会自带;在模板编辑中新增图形时需自行加入容器 | -
图形容器 | -用于图形选择, 使用id选择器, 如序号为2的容器, 选择器为#container_2 | -
图形 | -可视化的实际单位, 如选择图形中的table标签, 可使用#container_2 table | -
--6.0版本中拖拽布局方式已可同时满足电脑端/移动端的需求
-
当你新增一个栅格图形时, smartchart会给你一段默认的代码
-<div class="el-col-xs-24 el-col-md-24" style="padding:0.2rem;height:50%;" >
- <div style="height:100%;" id="container_{name}"></div>
-</div>
-
-el-col-md-24 : 电脑端宽度设定
-控制图形父容器的宽度, 整行分成24个栅格, 如果你想让图形占一半, 就可以改成el-col-md-12
-el-col-xs-24 :移动端宽度设定
-padding:0.5% 0.5%:
-控制图形的上下, 左右内边距, padding: 上 右 下 左
-比如你想要图形在容器中往下走一点, 你可写成 padding: 1% 0 0 0
-height:50%;
-盒子的高度, 相对于父容器的高度, 最外层即浏览器高度
-
你可以考虑先用响应式布局把整体框架画出来, 如果要加一下装饰的情况, 可以用绝对定位(拖拽布局)来实现
- 1. $参数名 : 当有传递参数时将替换相关的值
- 2. /* ... $参数名 ... */ : 当参数写在这个区间时, 如果外部没有传入参数, 会自动忽略这一段代码
- 3. -- : 标识之后单行的代码会被忽略
-
理解以下样列后, 可录活使用组合出各种可能的需求
-select xx from table_name where calmonth = '$month'
-
select xx from tablename where
-calmonth =/*'$calmonth' -- */ to_char(sysdate,'YYYYMM')
-
-假设没有传递参数, 那么/* .. */将会消失, 即真正的查询是:
-select xx from tablename where
-calmonth = to_char(sysdate,'YYYYMM')
-假设有传递参数, 比如calmonth=202305, 那/* .. */打开, 查询会是这样
-select xx from tablename where
-calmonth = '202305' -- to_char(sysdate,'YYYYMM')
-由于"--" 会忽略掉之后整行的代码, 所以真正的查询就是
-select xx from tablename where
-calmonth = '202305'
-
select xx from tablename where 1=1 /* and city = '$city' */
-/* and calmonth ='$calmonth'*/
-
select /*$calmonth,*/ city, count(1) as qty from tablename
-group by /*$calmonth,*/ city
-
select /*月份, -- $Month */
-城市, sum(度量) from tablename
-where 1=1
-/* and 月份 > '$Month' */
-group by 城市
-/*,月份 -- $Month*/
-
以上应用可以通过参数是否有传递,来实现开关代码段的效果, 有些场景可能还希望通过参数值来进行代码段的开关 -如下代码可以实现当传递参数type=1 或 type=2 时执行不同的代码段 -使用 “$参数__值” 的方式做为开关
-/* select count(1) as qty from tablename1 -- $type__1*/
-/* select count(1) as qty from tablename2 -- $type__2*/
-
再比如 -当参数D传值为"月份"时是统计2022年按月的统计, -传"日期"时统计的是2022年10月按天的统计
-select $D, count(1) as qty from tablename
-where 1 = 1
-/* and year='2022' -- $D__日期 $D__月份 */
-/* and month='10' -- $D__日期 */
-group by
-$D
-
--加了"–“是为了避免语法错误
-
对于开发人员来说, 带参数的SQL调试不方便, 所以支持你在sql中写入默认参数 -方法如下格式, 你可以在sql编辑器的最上方写上 /* {xxxxxx} */, -会默认在开发调试模式下取这些参数, 在用户模式下会忽略
-/* {"月份":"202009","城市":"中山"} */
-select xxx from table xxx
-
--TIPS -当设定参数后, 联动过程dataset的缓存功能失效, 所以不要让带参数的查询设计得太慢 -参数如果存在一些非法字符可能会有问题, 比如参数中不可以有#号
-
select ... from tablename
-/* where xxxx = '$参数名' */
-
比如需要点击0号图形, 指定其它图形联动
-只需要打开0号图形的数据集编辑页面, 点击标题的位置
-
-
然后输入相关的参数即可, 以下为sample
-
-
参数值设定的方法, 你可以先留空, 然后保存, F12打开浏览器调试方法
-点击0号图形你需要点击的动作, 可以在调试窗口的console看到输入的log
-比如我们需要传递的参数值是"廉颇", 那么取数据的方法就是data.name, 你把这个填入即可
-
-
这样就实现按所选数据或所选系列钻取/联动了, 重新点击当前所选, 恢复原来的
---在图形编辑器中, 可以使用函数 ds_param(‘参数名’) 来获取传入的参数值
-
myChart__name__.on('click', function(params){
- let myparam = params.seriesName; //获取点击的值
- ds_setParam('参数名', myparam); //填写你的数据集的SQL设定中对应的参数名
- ds_setParam('参数名2', myparam2); //你可以赋值给多个参数
- ds_refresh(3); //3 为你要刷新图形序号
-});
-
ds_setParam('参数名', 参数值)
-//此方法将自动判断当参数值为空,null或0时, 删除参数回到初始未传参状态
-//所以请注意此方法无法传递参数空,null及0, 如需传递请将0转化成字符
-//如果你的参数中存在非法字符如&=, 你可以使用js的encodeURIComponent函数进行转义后赋值
-
myChart__name__.on('click', function (params) {
- let myparam = `¶m={"参数名": "${params.seriesName}"}`;
- //拼成url并传参,具体参考数据集说明中的数据联动url传参的方法
- let myurl='/echart/?type=目标报表名'+ myparam;
- window.open(myurl,'_blank','toolbar=no,scrollbar=no,top=100,left=100,width=800,height=500');
-});
-
SMARTCHART实现筛选最简单的方法只需要配一个控件即可:
-建立一个筛选清单数据集, 自动获取筛选的列表,那么可以这么写
select xxxx as city from tablename.....
- -- 则会生成一个如下的数据集:
- [['city'],[选项1],[选项2],..]
-
良好的习惯, 先保存在数据集 -然后在筛选器数据集中的js编辑器(图形编辑器),填下如下代码:
-//如要要美化, 自已加样式, 只要保证id="id_select__name__"
-let dataset=__dataset__;
-let table ='<span>标题</span><select id="id_select__name__">';
-table = table + '<option value="" selected>----</option>';
- for(let i=1;i<dataset.length;i++){
- table = table + '<option>' + dataset[i][0] + '</option>';
- }
-table = table + '</select></div></div>'
-
-dom__name__.innerHTML=table;
-
这个时候你已经可以看到筛选器了
-
-
现在我们来设定联动效果
-假设需要被筛选的数据集的SQL这样写,数据集的序号是0 -那么在需要被联动的数据集中,如使用pcity做为参数写查询, 比如:
-select xx, xx, xx from tablename /* where xx = '$pcity' */
-
现在回到我们筛选器数据集,点击标题的位置, 我们需要使0号图形被筛选器联动, 设置如下即可:
-
-
然后你就可以看到筛选效果了, 非常的简单方便, 需要多个图形被联动, 只需用逗号分隔即可 比如: 0, 2, 4
-筛选器的美化请学习样式调整
-以我们内置的仪表盘为例:
-select distinct H1 as heroname from smartdemo2 limit 10
-
//生成html组件
-let dataset=__dataset__;
-let table = '';
-for (let i=1;i<dataset.length;i++){
- table = `${table}<label><input name="select__name__" type="checkbox" value="${dataset[i][0]}" />${dataset[i][0]}</label> `;
-}
-table = table + "<button id='id_select__name__'>提交</button>";
-dom__name__.innerHTML=table;
-
-//button 的点击响应
-$('#id_select__name__').click(()=>{
- let res = [];
- //获取所有选项并放入res列表中
- $("input[name='select__name__']:checked").each(function(i){
- res.push("'" + $(this).val() + "'");
- });
- let H1 = res.toString(); //拼接成参数
- ds_setParam('H1',H1); //参数赋值
- ds_refresh(1); //刷新1号图
- }
-)
-
select H1 as heroname, sum(qty) as 出场数 from smartdemo2
-where 1=1
-/* and H1 in ($H1) */
-group by H1
-order by sum(qty) desc
-
这样就完成了,任何其他需求,都可以采用类似方法自定义
-TIPS
---如果这个点击的图形有可能被其它图形点击联动或存在定时刷新,需要加入如下unbind否则会触发多次刷新 -$(’#id_select__name__’).unbind(‘click’).click(…..
-
--如需取消联动,恢复到初始效果, 你需要删除参数, 如: -delete filter_param[‘H1’]
-
在模板中使用basesimple
-
-
-此时smartchart不会引用任何echarts, vue组件, 完全由您自已控制引入
-
由于你在开发中仅需要用到filter_param及ds_refresh, 建意新建一个js文件, 文件内容:
-//以下为辅助方法, 发布时, 无需放入smartchart模板中
-var filter_param = {};
-function ds_refresh(num){
- if(num === 0){你对应的图形中赋值代码,调试代码}
- if(num === 1){....}
- ......
-}
-
然后将这个js文件在你的项目中引用调试使用
-打包完成后会有相应的css, js 和index.html文件, 将index.html中的代码复制贴粘到对应的模板区域中即可
-点击 模板开发 中的菜单即可上传你的资源文件, 如css, js, 图片等
-
- 视屏介绍说明
-
-
可直接上传单个文件或zip包上传, 注意zip包中不可以有中文文件名 -上传后会提示引用路径为/static/custom/仪表盘ID/…
-可把资源打包为zip文件, 上传名以usr_开头, 如usr_tp.zip -上传完后不会有路径提示, 引用路径为/static/custom/usr_tp/….
-上传后不会有路径提示
-在"模板"的style中加入以下样式
-@-webkit-keyframes spin {
- from {-webkit-transform: rotate(0deg);}
- to {-webkit-transform: rotate(360deg);}
-}
-@keyframes spin {
- from {transform: rotate(0deg);}
- to {transform: rotate(360deg);}
-}
-
-.Rotate {
- -webkit-animation: spin 3s linear 3s 5 alternate;
- animation: spin 3s linear infinite;
-}
-
如需任意组件自动旋转, 只需将Rotate这个类给到这个组件即可, 比如图形
-<img class="Rotate" src="https://www.smartchart.cn/media/editor/smc162_20220407150432307320.png">
-
常见变形沿着Y轴, 其它变形方式自已搜索, 比如需要0号,1号图形变形的样式写法
-#container_0{transform:skewY(10deg);}
-#container_1{transform:skewY(-10deg);}
-
@keyframes changeColor {
-0% {color: #f00;}
-25% {color: #0f0;}
-50% {color: #00f;}
-75% {color: #ff0;}
-100% {color: #f00;}
-}
-.Colorchange {animation: changeColor 3s infinite;}
-
效果如下:
-
-
把你的可视化页面移动的显示器上吧, 进入"模板" 开发页面(上节介绍如何进入)
-此方式当你新增数据集图形时会自动识别, 无需手动在模板在添加
-采用此方式请不要采用点击模板上方菜单的图形新增
-如需编辑图形或数据集可按如下方式:
-
-
-
-
你可以模板中按照常规的H5页面编辑, 只是在需要插入图形的地方插入即可(建意通过模板上方的图形新增) -开始畅快的开发
-
-
--如果使用自由布局, 请删除自动化DIV这一段代码 - -
-
可以观看视屏, 视屏比较老和现在不太一样, 仅参考即可 - - 自由开发模式视屏
-要想大屏做得好, 样式要写得好 -可是我们不是前端的同学也能写样式么 -当然可以, 相信你观看完以下视屏即可
-- 快速上手样式开发
-CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素。 -当浏览器读到一个样式表,它就会按照这个样式表来对文档进行格式化(渲染)。
-CSS实例
-每个CSS样式由两个组成部分:选择器和声明。声明又包括属性和属性值。每个声明之后用分号结束。
-
-
-CSS注释
/*这是注释*/
-
注释是代码之母, smartchart编辑中你可以使用CTRL+/快捷注释
-CSS的几种引入方式 -行内样式 -行内式是在标记的style属性中设定CSS样式。不推荐大规模使用。
-<p style="color: red">Hello world.</p>
-
内部样式 -嵌入式是将CSS样式集中写在网页的
标签对的标签对中。格式如下: -<head>
- <style>
- p{
- background-color: #2b99ff;
- }
- </style>
-</head>
-
外部样式 -外部样式就是将css写在一个单独的文件中
-<link href="mystyle.css" rel="stylesheet" type="text/css"/>
-
基本选择器
-/*元素选择器*/
-p {color: "red";}
-
-/*ID选择器*/
-#i1 {
- background-color: red;
-}
-
-/*类选择器*/
-.c1 {
- font-size: 14px;
-}
-p .c1 {
- color: red;
-}
-
注意: -样式类名不要用数字开头(有的浏览器不认)。 -标签中的class属性如果有多个,要用空格分隔。
-通用选择器
-* {
- color: white;
-}
-
组合选择器
-/*后代选择器*/
-/*li内部的a标签设置字体颜色*/
-li a {
- color: green;
-}
-/*儿子选择器*/
-/*选择所有父级是 <div> 元素的 <p> 元素*/
-div>p {
- font-family: "Arial Black", arial-black, cursive;
-}
-/*毗邻选择器*/
-/*选择所有紧接着<div>元素之后的<p>元素*/
-div+p {
- margin: 5px;
-}
-/*弟弟选择器*/
-/*i1后面所有的兄弟p标签*/
-#i1~p {
- border: 2px solid royalblue;
-}
-/*属性选择器*/
-/*用于选取带有指定属性的元素。*/
-p[title] {
- color: red;
-}
-/*用于选取带有指定属性和值的元素。*/
-p[title="213"] {
- color: green;
-}
-/*找到所有title属性以hello开头的元素*/
-[title^="hello"] {
- color: red;
-}
-/*找到所有title属性以hello结尾的元素*/
-[title$="hello"] {
- color: yellow;
-}
-/*找到所有title属性中包含(字符串包含)hello的元素*/
-[title*="hello"] {
- color: red;
-}
-/*找到所有title属性(有多个值或值以空格分割)中有一个值为hello的元素:*/
-[title~="hello"] {
- color: green;
-}
-
分组和嵌套
-分组
-当多个元素的样式相同的时候,我们没有必要重复地为每个元素都设置样式,我们可以通过在多个选择器之间使用逗号分隔的分组选择器来统一设置元素样式。
-例如:
-div, p {
- color: red;
-}
-上面的代码为div标签和p标签统一设置字体为红色。
-
-嵌套
-多种选择器可以混合起来使用,比如:.c1类内部所有p标签设置字体颜色为红色。
-.c1 p {
- color: red;
-}
-
伪类选择器
-/* 未访问的链接 */
-a:link {
- color: #FF0000
-}
-/* 鼠标移动到链接上 */
-a:hover {
- color: #FF00FF
-}
-/* 选定的链接 */
-a:active {
- color: #0000FF
-}
-/* 已访问的链接 */
-a:visited {
- color: #00FF00
-}
-/*input输入框获取焦点时样式*/
-input:focus {
- outline: none;
- background-color: #eee;
-}
-
伪元素选择器
-/*first-letter*/
-/*常用的给首字母设置特殊样式:*/
-p:first-letter {
- font-size: 48px;
- color: red;
-}
-before
-
-/*在每个<p>元素之前插入内容*/
-p:before {
- content:"*";
- color:red;
-}
-after
-
-/*在每个<p>元素之后插入内容*/
-/*before和after多用于清除浮动。*/
-p:after {
- content:"[?]";
- color:blue;
-}
-
CSS继承
-继承是CSS的一个主要特征,它是依赖于祖先-后代的关系的。继承是一种机制,它允许样式不仅可以应用于某个特定的元素,还可以应用于它的后代。例如一个body定义了的字体颜色值也会应用到段落的文本中。
-body {
- color: red;
-}
-此时页面上所有标签都会继承body的字体颜色。然而CSS继承性的权重是非常低的,是比普通元素的权重还要低的0。
-我们只要给对应的标签设置字体颜色就可覆盖掉它继承的样式。
-p {
- color: green;
-}
-
选择器的优先级
-我们上面学了很多的选择器,也就是说在一个HTML页面中有很多种方式找到一个元素并且为其设置样式,那浏览器根据什么来决定应该应用哪个样式呢?
-其实是按照不同选择器的权重来决定的,具体的选择器权重计算方式如下图:
-
-
-除此之外还可以通过添加 !important方式来强制让样式生效,但并不推荐使用。
-因为如果过多的使用!important会使样式文件混乱不易维护。
-万不得已可以使用!important
宽和高
-width属性可以为元素设置宽度。
-height属性可以为元素设置高度。
-块级标签才能设置宽度,内联标签的宽度由内容来决定。
-
字体属性
-文字字体
-font-family可以把多个字体名称作为一个“回退”系统来保存。如果浏览器不支持第一个字体,则会尝试下一个。浏览器会使用它可识别的第一个值。
-简单实例:
-body {
- font-family: "Microsoft Yahei", "微软雅黑", "Arial", sans-serif
-}
-字体大小
-p {
- font-size: 14px;
-}
-如果设置成inherit表示继承父元素的字体大小值。
-
-字重(粗细)
-font-weight用来设置字体的字重(粗细)。
-值描述normal默认值,标准粗细bold粗体bolder更粗lighter更细100~900设置具体粗细,400等同于normal,而700等同于boldinherit继承父元素字体的粗细值
-
-文本颜色
-color
-颜色是通过CSS最经常的指定:
-十六进制值 - 如: #FF0000
-一个RGB值 - 如: RGB(255,0,0)
-颜色的名称 - 如: red
-还有rgba(255,0,0,0.3),第四个值为alpha, 指定了色彩的透明度/不透明度,它的范围为0.0到1.0之间。
-
文字属性
-文字对齐
-text-align 属性规定元素中的文本的水平对齐方式。
-值描述left左边对齐 默认值right右对齐center居中对齐justify两端对齐
-
-文字装饰
-text-decoration 属性用来给文字添加特殊效果。
-值描述none默认。定义标准的文本。underline定义文本下的一条线。overline定义文本上的一条线。line-through定义穿过文本下的一条线。inherit继承父元素的text-decoration属性的值。
-
-常用的为去掉a标签默认的自划线:
-a {
- text-decoration: none;
-}
-首行缩进
-将段落的第一行缩进 32像素:
-p {
- text-indent: 32px;
-}
-
背景属性
-/*背景颜色*/
-background-color: red;
-/*背景图片*/
-background-image: url('1.jpg');
-/*
- 背景重复
- repeat(默认):背景图片平铺排满整个网页
- repeat-x:背景图片只在水平方向上平铺
- repeat-y:背景图片只在垂直方向上平铺
- no-repeat:背景图片不平铺
-*/
-background-repeat: no-repeat;
-/*背景位置*/
-background-position: left top;
-/*background-position: 200px 200px;*/
-支持简写:
-background:#336699 url('1.png') no-repeat left top;
-使用背景图片的一个常见案例就是很多网站会把很多小图标放在一张图片上,然后根据位置去显示图片。减少频繁的图片请求。
-
边框
-/*边框属性*/
-border-width
-border-style
-border-color
-#i1 {
- border-width: 2px;
- border-style: solid;
- border-color: red;
-}
-/*通常使用简写方式:*/
-#i1 {
- border: 2px solid red;
-}
-/*边框样式 值描述none无边框。dotted点状虚线边框。dashed矩形虚线边框。solid实线边框。*/
-/*除了可以统一设置边框外还可以单独为某一个边框设置样式,如下所示:*/
-#i1 {
- border-top-style:dotted;
- border-top-color: red;
- border-right-style:solid;
- border-bottom-style:dotted;
- border-left-style:none;
-}
-/*border-radius属性能实现圆角边框的效果。*/
-/*将border-radius设置为长或高的一半即可得到一个圆形。*/
-
display属性
-用于控制HTML元素的显示效果。
-值意义display:"none"HTML文档中元素存在,但是在浏览器中不显示。一般用于配合JavaScript代码使用。display:"block"默认占满整个页面宽度,如果设置了指定宽度,则会用margin填充剩下的部分。display:"inline"按行内元素显示,此时再设置元素的width、height、margin-top、margin-bottom和float属性都不会有什么影响。display:"inline-block"使元素同时具有行内元素和块级元素的特点。
-display:"none"与visibility:hidden的区别:
-visibility:hidden: 可以隐藏某个元素,但隐藏的元素仍需占用与未隐藏之前一样的空间。也就是说,该元素虽然被隐藏了,但仍然会影响布局。
-display:none: 可以隐藏某个元素,且隐藏的元素不会占用任何空间。也就是说,该元素不但被隐藏了,而且该元素原本占用的空间也会从页面布局中消失。
-
CSS盒子模型
-margin: 用于控制元素与元素之间的距离;margin的最基本用途就是控制元素周围空间的间隔,从视觉角度上达到相互隔开的目的。
-padding: 用于控制内容与边框之间的距离;
-Border(边框): 围绕在内边距和内容外的边框。
-Content(内容): 盒子的内容,显示文本和图像。
-
-
margin外边距
-.margin-test {
- margin-top:5px;
- margin-right:10px;
- margin-bottom:15px;
- margin-left:20px;
-}
-推荐使用简写:
-.margin-test {
- margin: 5px 10px 15px 20px;
-}
-顺序:上右下左
-
-常见居中:
-.mycenter {
- margin: 0 auto;
-}
-padding内填充
-.padding-test {
- padding-top: 5px;
- padding-right: 10px;
- padding-bottom: 15px;
- padding-left: 20px;
-}
-推荐使用简写:
-.padding-test {
- padding: 5px 10px 15px 20px;
-}
-顺序:上右下左
-
-补充padding的常用简写方式:
-提供一个,用于四边;
-提供两个,第一个用于上-下,第二个用于左-右;
-如果提供三个,第一个用于上,第二个用于左-右,第三个用于下;
-提供四个参数值,将按上-右-下-左的顺序作用于四边;
-
float
-在 CSS 中,任何元素都可以浮动。
-浮动元素会生成一个块级框,而不论它本身是何种元素。
-关于浮动的两个特点:
-浮动的框可以向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止。
-由于浮动框不在文档的普通流中,所以文档的普通流中的块框表现得就像浮动框不存在一样。
-三种取值
-left:向左浮动
-right:向右浮动
-none:默认值,不浮动
-
overflow溢出属性
-值描述visible默认值。内容不会被修剪,会呈现在元素框之外。hidden内容会被修剪,并且其余内容是不可见的。scroll内容会被修剪,但是浏览器会显示滚动条以便查看其余的内容。auto如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容。inherit规定应该从父元素继承 overflow 属性的值。
-overflow(水平和垂直均设置)
-overflow-x(设置水平方向)
-overflow-y(设置垂直方向)
-
定位(position)
-static
-static 默认值,无定位,不能当作绝对定位的参照物,并且设置标签对象的left、top等值是不起作用的的。
-relative(相对定位)
-相对定位是相对于该元素在文档流中的原始位置,即以自己原始位置为参照物。有趣的是,即使设定了元素的相对定位以及偏移值,元素还占有着原来的位置,即占据文档流空间。对象遵循正常文档流,但将依据top,right,bottom,left等属性在正常文档流中偏移位置。而其层叠通过z-index属性定义。
-注意:position:relative的一个主要用法:方便绝对定位元素找到参照物。
-absolute(绝对定位)
-定义:设置为绝对定位的元素框从文档流完全删除,并相对于最近的已定位祖先元素定位,如果元素没有已定位的祖先元素,那么它的位置相对于最初的包含块(即body元素)。元素原先在正常文档流中所占的空间会关闭,就好像该元素原来不存在一样。元素定位后生成一个块级框,而不论原来它在正常流中生成何种类型的框。
-重点:如果父级设置了position属性,例如position:relative;,那么子元素就会以父级的左上角为原始点进行定位。这样能很好的解决自适应网站的标签偏离问题,即父级为自适应的,那我子元素就设置position:absolute;父元素设置position:relative;,然后Top、Right、Bottom、Left用百分比宽度表示。
-另外,对象脱离正常文档流,使用top,right,bottom,left等属性进行绝对定位。而其层叠通过z-index属性定义。
-fixed(固定)
-fixed:对象脱离正常文档流,使用top,right,bottom,left等属性以窗口为参考点进行定位,当出现滚动条时,对象不会随着滚动。而其层叠通过z-index属性 定义。 注意点: 一个元素若设置了 position:absolute | fixed; 则该元素就不能设置float。这 是一个常识性的知识点,因为这是两个不同的流,一个是浮动流,另一个是“定位流”。但是 relative 却可以。因为它原本所占的空间仍然占据文档流。
-在理论上,被设置为fixed的元素会被定位于浏览器窗口的一个指定坐标,不论窗口是否滚动,它都会固定在这个位置。
-
z-index
-#i2 {
- z-index: 999;
-}
-设置对象的层叠顺序。
-z-index 值表示谁压着谁,数值大的压盖住数值小的,
-只有定位了的元素,才能有z-index,也就是说,不管相对定位,绝对定位,固定定位,都可以使用z-index,而浮动元素不能使用z-index
-z-index值没有单位,就是一个正整数,默认的z-index值为0如果大家都没有z-index值,或者z-index值一样,那么谁写在HTML后面,谁在上面压着别人,定位了元素,永远压住没有定位的元素。
-从父现象:父亲怂了,儿子再牛逼也没用
-
opacity -用来定义透明效果。取值范围是0~1,0是完全透明,1是完全不透明。
-功能 | -WIN | -MAC | -说明 | -
---|---|---|---|
显示菜单 | -CTRL-, | -Command-, | -- |
折叠其它 | -Alt-0 | -Command-Option-0 | -- |
查找替换 | -Ctrl-F | -Command-F | -- |
重复选中 | -Ctrl-D | -Command-D | -- |
注释选中 | -Ctrl-/ | -Command-/ | -- |
取消修改 | -Ctrl-z | -Command-z | -- |
重新执行 | -Ctrl-y | -Command-y | -- |
数据集编辑 | -Ctrl-Q | -Ctrl-Q | -- |
选中大写 | -Ctrl-U | -Ctrl-U | -- |
选中小写 | -SHIFT-Ctrl-U | -SHIFT-Ctrl-U | -- |
具体使用方法请观看视屏 - 版本控制使用说明视屏
-可以在模板->点击如下图标, 完成快速备份(注意会覆盖历史), 备份号统一为:SNAPSHOT
-
-
你也可以在设定->备份恢复 中进行按版本备份
-
-
当不输入KEY值, 点击 本地备份时,可以查询此仪表盘已有的备份
-
-
备份可以在任意的仪表盘中进行恢复,如果是当前仪表盘, 仅输入KEY即可,比如:V01, -如果是跨仪表盘恢复, KEY需要带上仪表盘的编号如13_V01
-可以支持多种恢复模式, 只需要在KEY前面加上前缀即可, 如FORCEV01, FORCE13_V01..
-具体方法购买专业版本后提供
-由于公司内部人员是对业务最熟悉, 一般也都是后台数据管理相关的人员, 一般不太会有专职的前端开发和UI -所以数据开发人员可以使用smartchart开发仪表盘数据集, 并使用拖拽功能完成一个粗糙一点的框架和图形设计 -一般情况下已经可以满足数据可视化的需求
-如果需要达到更专业的可视化效果, 可以外包前端/UI或在smartchart社区咨询, 由于需求变得非常简单 -而前端开发又是一个通用技能, 可以使用非常廉价的费用获得最大的效果
---专业的事情专业来做, 才能达到效益最大化, 这是smartchart的设计理念 -不管用什么工具, 在同等资源的投入下, 数据分析人员开发的可视化效果很难达到专业前端UI的效果 -另外BI/数据开发人员的费用可是比前端高的, 也更稀缺
-
--使用后台API刷新,建意将仪表盘中数据集的缓存时间设置长一些,比如2天(2880分钟)
-
(购买专业版本后支持)
-API_TOKEN = 'xxxxxxxx'
-
找到你要刷新的仪表盘编码, 你可以在打开的仪表盘url上面找到这个type id
-后台访问如下api url即可
-http://ip:端口/echart/refresh_ds/?type=你的报表ID&token=你设定的API_TOKEN
-
为保持产品的轻量化及坚持专业的产品做专业的事情, 归一化统一化的架构设计, 我们不会集成相关调度系统, -一般我们推荐使用您自有的调度工具或平台, 如airflow, 我们也有相关的配套产品
-如果您仅仅是简单应用, 也无需使用专用调度来增加运维复杂度, 可以使用linux自带的即可
-vim refresh_smartchart.sh
-
echo start refresh $(date "+%Y-%m-%d %H:%M:%S")
-curl http://ip:端口/echart/?type=你的报表ID1&token=你设定的API_TOKEN
-curl http://ip:端口/echart/?type=你的报表ID2&token=你设定的API_TOKEN
-echo end refresh $(date "+%Y-%m-%d %H:%M:%S")
-
--如果你的网址是https, 可如下方法使用curl
-
curl -k --insecure "https://www.baidu.com”
-
chmod 775 refresh_smartchart.sh
-
# 编辑crontab
-crontab -e
-# 比如需要每天晚上5点10分执行
-10 5 * * * /data/smartchart/refresh_smartchart.sh >>/data/smartchart/log.txt 2>&1
-
-# 定时参数说明
-* * * * *
-- - - - -
-| | | | |
-| | | | +----- 星期中星期几 (0 - 6) (星期天 为0)
-| | | +---------- 月份 (1 - 12)
-| | +--------------- 一个月中的第几天 (1 - 31)
-| +-------------------- 小时 (0 - 23)
-+------------------------- 分钟 (0 - 59)
-
需要要下载数据集的数据到本地
-仅需要录活使用ds_download这个函数, 你可以开发出非常个性化的下载功能
-ds_download(name, dataset)
-参数说明:
-name: 文件名称
-dataset: 可以是二维数组也可以是字符串
-
可在"模板" 中新建一个下载按钮并包裹拖拽容器, 指定button的ID,如id_down1, 拖拽到你需要的位置
-
-
在任意一个图形开发或js代码段中加入以下代码即可
-$('#id_down1').click(()=>{
- ds_download('报表数据.csv', dataset);
-});
-
这样就可以实现点击按钮下载数据了
-dataset={
- "table":"表名"
-}
-
dataset={
- "table":"表名(字段1, 字段2)"
-}
-
- 具体可参考视屏
- <h1 class="smtdrag" id="id_1648895680659">数据填报</h1>
- <div class="smtdrag" id="id_1648895855760">
- <label>用户</label><input id="id_visitor">
- </div>
- <div class="smtdrag" id="id_1648895859160">
- <label>动作</label><input id="id_action">
- </div>
- <div class="smtdrag" id="id_1648895956207">
- <button id="idbtn01">提交</button>
- </div>
-
-
$('#idbtn01').click(function(){
- //获取填写的数据
- let visitor = $('#id_visitor').val();
- let action = $('#id_action').val();
- //拼接一个填写好的数组
- let dataset = [visitor, action];
- //上传填写的数据
- //0:为上文新建的数据集序号, dataset:要写入的数据
- print(ds_save(0, dataset));
- })
-
- 具体可参考视屏
-//定义excel表格中需获取数据单元格
-let fillCells = ['D4', 'D5'];
-//获取数据并清空单元格
-let dataset = ds_excel_value(fillCells,clear=true);
-//可以加入填报中用户名[可选]
-dataset.unshift('$username'); //如果需要加入用户名
-//写入数据库中
-print(ds_save(0, dataset));
-
//只写入一行数据, 样列如下:
-dataset = ['a','b']
-//同时写入多行数据:
-dataset = [[], ['a1','b1'],['a2', 'b2']]
-//如果需要自动记录写入者用户名:
-dataset = ['$username', 'b']
-
mongodb写入方式
-ds_save(0, {"h1":123, "h2":"bb"});
-ds_save(0, [[],{"h1":123, "h2":"aa"},{"h1":1234, "h2":"dd"}]);
-
--下文提到的"秘钥"即管理员在API服务设定中的"token"
-Content-Type为application/x-www-form-urlencoded
-
接口请求URL
-/echart/dataset_api/?visitor=xxx&token=xxx&type=xxx&stamp=xxxxx¶m={"xx":"xxx","xx":"xxxx"}
-
参数说明
-visitor: 用户名
-type: 接口数据集ID
-stamp: 时间戳(1970年1月1日到生成时间的毫秒数)
-token: 采用sha1加密, token=SHA1(秘钥 + stamp + visitor + type)
-param: 数据集查询参数清单(可选), 格式json字符串, 如: '{"参数A":"xxxx", "参数B":"xxxx"}'
-
接口返回格式Json
-{
-"data":[[]],
-"result":"success",
-"maxpg":1,
-"pg":1
-}
-
返回值说明
-data : 二维数组, 第一行为表头, 样列数据如下
-[["heroname", "qty"],["镜",658],["猪八戒",591]]
-result : success 或 error
-maxpg/pg : GET请求固定为1不分页
-
适用于后台定时同步数据,查询请用GET请求方法
-#接口请求格式:
-url: /echart/dataset_api/
-#请求参数data:
-{
-"visitor":"xxx",
-"token":"xxx",
-"stamp":xxxxx,
-"type":"xxx",
-"pagesize":"xxx",
-"pg":"xxx",
-"param":'{"xxx":"xxxx"}'
-}
-#data参数说明
-visitor: 用户名
-type: 接口数据集ID
-stamp: 时间戳(1970年1月1日到生成时间的毫秒数)
-token: 采用sha1加密, token=SHA1(秘钥 + stamp + visitor + type)
-Pagesize: 采用分页, 每页的数据量大小
-pg: 返回第几页
-param: 数据集查询参数清单(可单), 拼接成json字符串, 如:
-'{"参数A":"xxxx", "参数B":"xxxx"}'
-
-#接口返回格式Json:
-{
-"data":[[]],
-"result":"success",
-"maxpg":xxx, #最大页数
-"pg":xx, #当前页数
-"casheflag": xx, #如果是999说明命中缓存
-"total":xx, #总条数
-}
-
--注意: -只有post是分页的, 第一页是带标题的, 后面页不带标题 -由于post方式会使用缓存进行分页,如命中缓存传参不会生效,小数据量请使用get方式请求 -不要请求大数据量,大量数据请采用limit, offset传参方式进行分页
-
GET 请求
-#接口请求格式:
-url= '/echart/dataset_api/?visitor=xxx&token=xxx&type=数据集名或id'
-#接口返回格式Json:
-{
-"data":[[]],
-"result":"success",
-"maxpg":1,
-"pg":1
-}
-
-POST请求
-#接口请求格式:
-url: /echart/dataset_api/
-data:
-{
-"visitor":"xxx",
-"token":"xxx",
-"type":"xxx", #数据集名或id名
-"pagesize":"xxx", #每页数据条数
-"pg":"xxx", #返回第几页
-"param":'{"xxx":"xxxx"}' #参数可选
-}
-#接口返回格式Json:
-{
-"data":[[]],
-"result":"success",
-"maxpg":xxx, #最大页数
-"pg":xx, #当前页数
-"casheflag": xx, #如果是999说明命中缓存
-"total":xx, #总条数
-}
-
-注意:
-只有post是分页的, 第一页是带标题的, 后面页不带标题
-由于post方式会使用缓存, 小数据量建议你使用get方式请求
-
{
- "test": {
- "token": "smartchart"
- },
- "test2": {
- "token": "smartchartxxx",
- "host": ["10.10.10.10","10.10.10.23"],
- "limit": 60,
- "log":1,
- "cors": 1
- }
-}
-
--可选设定参考test2 -host:API白名单配置,limit:一分钟内可调用次数, log:日志记录方式. -cors:永许跨域访问
-
--API请求方式请参考 - 数据服务API
-
SMC_HOME_PAGE=/echart/?type=2
-
SMC_HOME_PAGE='/echart/?type=2'
-
这样就可以在首页直接显示设定好的页面了
-在首页–> 头像位置下拉菜单 –> 服务配置, 输入并修改为你的以下配置即可使用
-{
- "smtgpt": {
- "url": "chatgpt",
- "api_type": "azure",
- "api_base": "https://xxx.openai.azure.com/",
- "api_version": "2023-03-15-preview",
- "api_key": "xxxxxxxxxx",
- "engine": "xxxx"
- }
-}
-
在首页–> 头像位置下拉菜单 –> 服务配置, 输入并修改为你的以下配置即可使用
-{
- "smtgpt": {
- "api_type": "open_ai",
- "api_base": "https://api.openai.com/v1",
- "api_key": "xxxxxx",
- "engine": "gpt-3.5-turbo"
- }
-}
-
比如迅飞的GPT, 首先新建一个文件, 如gptxinhuo.py -写入python脚本
-def gpt_dataset(prompt, **kwargs):
- #你的处理过程
-
- return {'msg': message, 'token': 0, 'status': 200}
-
在任意仪表盘开发界面–> 模板 –> 工具 –> 文件上传 菜单中将此文件上传即可 -之后AI生成服务将采用你自定义的服务, 如果不熟悉python脚本, 可联系客服定制
-{
- "smtgpt": {
- "api_type": "gptxinhuo",
- "appid": "xxx",
- "api_key": "xxxx",
- "api_secret": "xxx",
- "api_base": "ws://spark-api.xf-yun.com/v1.1/chat"
- }
-}
-
'/echart/smart_login?id=xxx&stamp=xxx&token=xxx&url=/'
-'''
-参数说明:
-id: 用户名(在smartchart平台中管理)
-stamp: 时间戳(1970年1月1日到生成时间的毫秒数)
-token: 采用sha1加密, token=SHA1(链接秘钥+stamp+id)
- 请在安装smartchart的这台机器上设定环境变量SMART_KEY = 链接秘钥
-url: 登录成功后跳转链接
-'''
-
import time
-import hashlib
-import os
-
-"""
-参数说明:
-id: 用户名(在smartchart平台中管理)
-stamp: 时间戳(1970年1月1日到生成时间的毫秒数)
-token: 采用sha1加密, token=SHA1(链接秘钥+stamp+id)
-url: 登录成功后跳转链接
-"""
-
-SMART_CHART_URL = 'http://127.0.0.1:8000'
-LOGIN_URL = SMART_CHART_URL + '/echart/smart_login?id={id}&stamp={stamp}&token={token}&url={url}'
-SMART_KEY = 链接秘钥
-
-
-def get_smarturl(username, url='/'):
- stamp = int(time.time() * 1000)
- id = username
- res = SMART_KEY + str(stamp) + id
- token = hashlib.sha1(res.encode('utf-8')).hexdigest()
- LOGIN_DICT = {
- "id": id,
- "stamp": stamp,
- "token": token,
- "url": url
- }
-
- # 拼接好的url,直接访问
- visit_url = LOGIN_URL.format(**LOGIN_DICT)
- return visit_url
-
与单点登录类似, 单点登录用于直接登录到平台访问报表 -但对于只嵌入报表, 用此方法更合适(需升级到5.3.11以上)
-嵌入的url: '/echart/?type={reportName}&visitor={visitor}&token={token}&stamp={stamp}'
-参数说明:
-reportName: 报表名或报表ID
-visitor: 用户名(在smartchart平台中管理)
-stamp: 时间戳(1970年1月1日到生成时间的毫秒数)
-token: 采用sha1加密, token=SHA1(链接秘钥+stamp+visitor+reportName)
-
用户名和秘钥设定参考 - 数据服务API的config文件
-同时你需要将visitor加入到对应的报表权限查看访问
-以下为python版的url生成样列,你可以转化成你对应的开发语言
-import time
-import hashlib
-import os
-
-SMART_CHART_URL = 'http://127.0.0.1:8000'
-reportID = '报表ID'
-LOGIN_URL = SMART_CHART_URL + '/echart/?type={reportID}&visitor={visitor}&token={token}&stamp={stamp}'
-TOKEN = 链接秘钥
-
-
-def get_smarturl(username, reportName):
- stamp = int(time.time() * 1000)
- visitor = username
- res = TOKEN + str(stamp) + visitor + reportID
- token = hashlib.sha1(res.encode('utf-8')).hexdigest()
- VISIT_DICT = {
- "visitor": id,
- "stamp": stamp,
- "token": token,
- "reportID": reportName
- }
-
- # 拼接好的url,直接访问
- visit_url = LOGIN_URL.format(** VISIT_DICT)
- return visit_url
-
如果你需要对用户进行一些数据权限控制, 可以避免用户越权访问
-可以通过传入参数"id", 如/echart/?type=xxx&visitor=xx&token=xx&stamp=xxxxx&id=xxx
-后台会把这个id转化为参数名"_id"给对应的查询来进行数据权限控制
-加密参数需把id加入, 例如id=john
-那么 token=SHA1(链接秘钥+stamp+visitor+reportName+id)
-
如果需要将参数也加入认证中, 为保持兼容性, 我们把param这个参数改为params(具体参考参数文档中param的写法)
-'/echart/?type={reportName}&visitor={visitor}&token={token}&stamp={stamp}¶ms=xxxx'
-加密参数把params加入, 例如params为 {"a":"1","b":2"}
-res = TOKEN + str(stamp) + visitor + reportName + '{"a":"1","b":2"}'
-
你可能需要把Smartchart生成的图形嵌入到其它系统 -首先,所有smartchart设计出来的仪表盘都有一个访问url -你可以直接访问:
-http://localhost:8000/echart?type=仪表盘名称
-
-如:http://localhost:8000/echart?type=demo
-
但是smartchart默认是有权限管理的,所以如果你需要嵌入你自己的系统又不考虑权限,你可以在“设定” –> 公开
-
-
然后在你的网页就可以直接iframe了
-<iframe src="http://localhost:8000/echart?type=demo" style="width:100%;height:100%"></iframe>
-
如果你想对smartchart前端二次开发或关闭debug模式后找不到资源 -在settings中加入
-STATIC_ROOT = os.path.join(BASE_DIR, "static")
-
执行以下命令将静态文件静态文件克隆到根目录
-python3 manage.py collectstatic
-
可以在你的django项目中直接使用smartchart做为应用插件 - - 你可以查看相关视屏
-INSTALLED_APPS = [
- 'smart_chart.smartui',
- ....
- ....
- 'smart_chart.echart'
-]
-
MIDDLEWARE 中注释掉XFrameOptionsMiddleware
-检查确保在Templates的设定处有DIRS的相关设定
-TEMPLATES = [
- {
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [BASE_DIR / 'templates'], #此处需要有
- 'APP_DIRS': True, #也要有
- .....
- },
-]
-
LANGUAGE_CODE = 'zh-hans'
- TIME_ZONE = 'Asia/Shanghai'
- USE_I18N = True
- USE_L10N = True
- USE_TZ = False # 此处必须为False
-
from django.conf.urls import include
- from django.views.generic import RedirectView
-
path('echart/', include('smart_chart.echart.urls')),
- path('', RedirectView.as_view(url='/echart/index/')), #首页,可自定义路由
-
python manage.py makemigrations
- python manage.py migrate
-
python manage.py createsuperuser
-
python manage.py runserver
-
10. 点击首页的组件升级进行初始化 !!!! 重要!!!
-
-
一般来说如果你使用django遇到的问题,都不是smartchart导致的 -作者也很难给你解答, 建议你可以进行有偿问答
---Smartchart支持像pyecharts, Matplotlib 等python绘图工具一样在Jupyter中使用, -但她更加方便, 更加炫酷 和 通用化, 不仅仅是一个绘图工具, 而且是一个平台
-
-
你需要在jupyter相同的python环境中安装smartchart客户端
-pip install smartchart
-或
-pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple smartchart
-
from smart_chart import Smart
-Smart().set_auth('用户名','密码',url = 'http://xxxxx')
-
Smart().set_auth('用户名','密码')
-
from smart_chart import Smart
-mysmart = Smart()
-dataset = [['A','B','C'],[12,34,23],[22,33,37]]
-#把数据写入临时数据集并显示图形
-mysmart.set(1,dataset)
-#随意命名临时数据集, 不一定需要smartchart中数据集已有的
-mysmart.set('DD', dataset)
-
-#从已有的数据集中获取数据(格式参考smartchart数据集)
-ds1 = mysmart.get(1)
-ds2 = mysmart.get('DD')
-
mysmart.set('barxxx', dataset) #显示柱形图, 另外还有linexxx, piexxx
-
mysmart.set('myds_1', dataset, push=1) #参数push=1, 将实例化数据集
-
--实例化的数据集, 在图形编辑区点击, 可以进入定制化图形开发, 可使用原生的Echarts配置和实时调试,或直接使用社区图形(第一次使用,有一个登记的过程, 按提示进行) - -
-
#不加push, 将使用新的数据采用myds_1的图形临时显示, 而不会改变原myds_1的数据
-mysmart.set('myds_1', dataset)
-
你可以通过参数来设定图形的高宽, 是否嵌入等个性化要求
-# width, height指定图形嵌入显示的宽高
-# embed 默认不嵌入, embed=1 嵌入, embed=0 不嵌入
-# editor 是否显示图形菜单, 1显示, 0不显示
-# push 是否持久化数据集 push=1, 无则新建有则保存数据
-# url 报表访问的url,默认是localhost
-
-#可以全局初始化设定
-mysmart = Smart(width=xx, height=xx, embed=1, editor='')
-#也可以全局单独进行设定
-mysmart.url = 'http://ip:8000'
-mysmart.embed = 1
-
-#也可以针对单独的一个图形设定
-mysmart.set(1,dataset,embed=1,height=200,editor=0)
-
Smartchart的set支持直接set Pandas的dataframe对象
-from smart_chart import Smart
-import pandas as pd
-mysmart = Smart()
-df = pd.read_excel('manual_smartdemo.xlsx', 'sheet1')
-mysmart.set('excelsample', df.sample(10))
-
-
-df1 = df.groupby('province').agg({'qty':'sum'}).reset_index()
-mysmart.set('ec_df1', df1, push=1)
-
-df2 = df.groupby('c1').agg({'qty':'sum'}).reset_index()
-mysmart.set('ec_df2', df2, push=1)
-
-df4 = df.groupby('province').agg({'qty':'count'}).reset_index()
-mysmart.set('ec_df4', df1, push=1)
-
-
-df3 = df.groupby('c3').agg({'qty':'sum'}).reset_index()
-print(df3)
-df3.loc[1, 'qty'] = df3.loc[1, 'qty'] * 100
-print(df3)
-
-mysmart.set('ec_df3', df3, push=1)
-
-#mysmart.set('pie0', df1)
-#df2 = df.groupby(['province','c3']).agg({'qty':'sum'}).reset_index()
-#print(df2)
-#mysmart.set('ssss', df2)
-#print(mysmart.get(15))
-
-
打造全生态的数据应用数据管理的平台,解决传统企业上中台难,上中台贵,见效慢的问题. 平台与时俱进,不断完善与优化中 -可以通过视屏了解我们的架构设计 - 企业数字化与smartchart的一站式解决方案
-实现全生态的数据服务,数据治理平台(数据采集, 数据加工, 数据分享,数据管理,数据应用)
-
-
功能 | -功能描述 | -
---|---|
多系统数据集成 | -支持多系统、多数据源类型的数据拉通,整合,包括内部业务数据库数据、外部第三方接口数据、IoT数据、埋点数据、手工补录数据等 | -
数据填报服务 | -支持线下手工数据线上化,用于收集业务线下手工录入数据、制作问卷等场景,且支持数据录入的分级分权管理 | -
数据流批处理产品 | -存储和计算平台。支持亚秒级的实时数据写入,实时数据查询,查询速度是同类产品的10倍以上,支持万级的并发查询,有效支持数千用户的实时数据分析需求 | -
数据开发及管理 | -一站式数据开发管理平台,支持PB级数据的数据模型设计,数据仓库开发,数据存储等功能, 高效的API开发流程,仅需几分钟完成API数据服务开发, 数据复用度, 技术复用度高,可面向用户端的数据上传下载能力, 仅需几分钟即可配置数据上传下载界面 | -
数据治理能力平台SmartPip | -支持任务管理、调度、监控的工作流平台,实时生成数据血缘。企业版已将部分组件做了封装,提升了高可用性,低代码开发平台, 开发及运维效率高,柔性更强,更便于与其它自研应用系统打通,满足企业个性化需求 | -
可视化平台SmartChart | -低代码可视化分析平台(中台旗舰版),用于敏捷地BI报表、看板、大屏等开发,拥有上万级图库、UI模板库。UI实现度可达到90%以上. 支持服务数据(API)低代码开发,支持单点登录,支持报表的嵌入 | -
数据服务 | -支持高效安全的数据服务能力,面向不同的用户提供差异化的能力和工具支撑,不同类型的用户可以在一定范围内分析数据、共享数据结果 1.API接口:支持高效易用功能强大的API接口开发,接口数据后台自动刷新、定时刷新 2.自助取数:支持用户通过拖拉拽的方式快速进行数据探索和展示 3.数据订阅:支持可配置方式通过邮件给用户主动推送预警数据、经营分析结果数据 4.智能数据助手:深度融合GPT,适配所有数据开发和数据分析的场景,数据分析人员可以实现无代码开发,业务人员可通过自然语言进行数据探索及分析,最大程度让大模型技术提升开发和数据分析效率。 5.智慧BI:支持将所有常见商用BI融合在同一个数据门户, 支持多级动态菜单 |
-
h&&(l=0),l=l||0,g=l+c,g
- A NoBI platform that Connect Data to Insight.
- 0&&(e="."===(e=t)?null:e),e};Qt({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,greediness:6,argTypes:["math","math","size","text","math","math"]},handler:function(t,e){var r=t.parser,a=e[4],n=e[5],i=Vt(e[0],"atom");i&&(i=Ut(e[0],"open"));var o=i?Lr(i.text):null,s=Vt(e[1],"atom");s&&(s=Ut(e[1],"close"));var h,l=s?Lr(s.text):null,m=Ft(e[2],"size"),c=null;h=!!m.isBlank||(c=m.value).number>0;var u="auto",p=Vt(e[3],"ordgroup");if(p){if(p.body.length>0){var d=Ft(p.body[0],"textord");u=Er[Number(d.text)]}}else p=Ft(e[3],"textord"),u=Er[Number(p.text)];return{type:"genfrac",mode:r.mode,numer:a,denom:n,continued:!1,hasBarLine:h,barSize:c,leftDelim:o,rightDelim:l,size:u}},htmlBuilder:Rr,mathmlBuilder:Or}),Qt({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler:function(t,e){var r=t.parser,a=(t.funcName,t.token);return{type:"infix",mode:r.mode,replaceWith:"\\\\abovefrac",size:Ft(e[0],"size").value,token:a}}}),Qt({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:function(t,e){var r=t.parser,a=(t.funcName,e[0]),n=function(t){if(!t)throw new Error("Expected non-null, but got "+String(t));return t}(Ft(e[1],"infix").size),i=e[2],o=n.number>0;return{type:"genfrac",mode:r.mode,numer:a,denom:i,continued:!1,hasBarLine:o,barSize:n,leftDelim:null,rightDelim:null,size:"auto"}},htmlBuilder:Rr,mathmlBuilder:Or});var Hr=function(t,e){var r,a,n=e.style,i=Vt(t,"supsub");i?(r=i.sup?ue(i.sup,e.havingStyle(n.sup()),e):ue(i.sub,e.havingStyle(n.sub()),e),a=Ft(i.base,"horizBrace")):a=Ft(t,"horizBrace");var o,s=ue(a.base,e.havingBaseStyle(w.DISPLAY)),h=Oe(a,e);if(a.isOver?(o=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:h}]},e)).children[0].children[0].children[1].classes.push("svg-align"):(o=Dt.makeVList({positionType:"bottom",positionData:s.depth+.1+h.height,children:[{type:"elem",elem:h},{type:"kern",size:.1},{type:"elem",elem:s}]},e)).children[0].children[0].children[0].classes.push("svg-align"),r){var l=Dt.makeSpan(["mord",a.isOver?"mover":"munder"],[o],e);o=a.isOver?Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:l},{type:"kern",size:.2},{type:"elem",elem:r}]},e):Dt.makeVList({positionType:"bottom",positionData:l.depth+.2+r.height+r.depth,children:[{type:"elem",elem:r},{type:"kern",size:.2},{type:"elem",elem:l}]},e)}return Dt.makeSpan(["mord",a.isOver?"mover":"munder"],[o],e)};Qt({type:"horizBrace",names:["\\overbrace","\\underbrace"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=t.funcName;return{type:"horizBrace",mode:r.mode,label:a,isOver:/^\\over/.test(a),base:e[0]}},htmlBuilder:Hr,mathmlBuilder:function(t,e){var r=Re(t.label);return new ve.MathNode(t.isOver?"mover":"munder",[Me(t.base,e),r])}}),Qt({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:function(t,e){var r=t.parser,a=e[1],n=Ft(e[0],"url").url;return r.settings.isTrusted({command:"\\href",url:n})?{type:"href",mode:r.mode,href:n,body:ee(a)}:r.formatUnsupportedCmd("\\href")},htmlBuilder:function(t,e){var r=se(t.body,e,!1);return Dt.makeAnchor(t.href,[],r,e)},mathmlBuilder:function(t,e){var r=Se(t.body,e);return r instanceof ge||(r=new ge("mrow",[r])),r.setAttribute("href",t.href),r}}),Qt({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:function(t,e){var r=t.parser,a=Ft(e[0],"url").url;if(!r.settings.isTrusted({command:"\\url",url:a}))return r.formatUnsupportedCmd("\\url");for(var n=[],i=0;i {if(!((a-=s)>=i))return;let o=t*n[i];const l=s*t;for(let u=i,h=i+l;u 0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=i[0],s[i[0]]=1,i.length===2&&(s[i[1]]=0,this.triangles[1]=i[1],this.triangles[2]=i[1]))}voronoi(e){return new Dv(this,e)}*neighbors(e){const{inedges:r,hull:n,_hullIndex:i,halfedges:a,triangles:s,collinear:o}=this;if(o){const d=o.indexOf(e);d>0&&(yield o[d-1]),d
- 功能特色
- #
-
-
-
-"},t}(),O={"\xee":"\u0131\u0302","\xef":"\u0131\u0308","\xed":"\u0131\u0301","\xec":"\u0131\u0300"},E=function(){function t(t,e,r,a,n,i,o,s){this.text=void 0,this.height=void 0,this.depth=void 0,this.italic=void 0,this.skew=void 0,this.width=void 0,this.maxFontSize=void 0,this.classes=void 0,this.style=void 0,this.text=t,this.height=e||0,this.depth=r||0,this.italic=a||0,this.skew=n||0,this.width=i||0,this.classes=o||[],this.style=s||{},this.maxFontSize=0;var h=function(t){for(var e=0;e
=s)){var P=void 0;(a>0||t.hskipBeforeAndAfter)&&0!==(P=c.deflt(O.pregap,p))&&((C=Dt.makeSpan(["arraycolsep"],[])).style.width=P+"em",R.push(C));var D=[];for(r=0;r0?3*c:7*c,d=e.fontMetrics().denom1):(m>0?(u=e.fontMetrics().num2,p=c):(u=e.fontMetrics().num3,p=3*c),d=e.fontMetrics().denom2),l){var y=e.fontMetrics().axisHeight;u-o.depth-(y+.5*m)0&&(u+=M,p-=M)}var z=[{type:"elem",elem:n,shift:p,marginRight:b,marginLeft:y},{type:"elem",elem:a,shift:-u,marginRight:b}];x=Dt.makeVList({positionType:"individualShift",children:z},e)}else if(n){p=Math.max(p,m.sub1,n.height-.8*m.xHeight);var A=[{type:"elem",elem:n,marginLeft:y,marginRight:b}];x=Dt.makeVList({positionType:"shift",positionData:p,children:A},e)}else{if(!a)throw new Error("supsub must have either sup or sub.");u=Math.max(u,i,a.depth+.25*m.xHeight),x=Dt.makeVList({positionType:"shift",positionData:-u,children:[{type:"elem",elem:a,marginRight:b}]},e)}var T=me(l,"right")||"mord";return Dt.makeSpan([T],[l,Dt.makeSpan(["msupsub"],[x])],e)},mathmlBuilder:function(t,e){var r,a=!1,n=Vt(t.base,"horizBrace");n&&!!t.sup===n.isOver&&(a=!0,r=n.isOver),!t.base||"op"!==t.base.type&&"operatorname"!==t.base.type||(t.base.parentIsSupSub=!0);var i,o=[Me(t.base,e)];if(t.sub&&o.push(Me(t.sub,e)),t.sup&&o.push(Me(t.sup,e)),a)i=r?"mover":"munder";else if(t.sub)if(t.sup){var s=t.base;i=s&&"op"===s.type&&s.limits&&e.style===w.DISPLAY?"munderover":s&&"operatorname"===s.type&&s.alwaysHandleSupSub&&(e.style===w.DISPLAY||s.limits)?"munderover":"msubsup"}else{var h=t.base;i=h&&"op"===h.type&&h.limits&&(e.style===w.DISPLAY||h.alwaysHandleSupSub)?"munder":h&&"operatorname"===h.type&&h.alwaysHandleSupSub&&(h.limits||e.style===w.DISPLAY)?"munder":"msub"}else{var l=t.base;i=l&&"op"===l.type&&l.limits&&(e.style===w.DISPLAY||l.alwaysHandleSupSub)?"mover":l&&"operatorname"===l.type&&l.alwaysHandleSupSub&&(l.limits||e.style===w.DISPLAY)?"mover":"msup"}return new ve.MathNode(i,o)}}),te({type:"atom",htmlBuilder:function(t,e){return Dt.mathsym(t.text,t.mode,e,["m"+t.family])},mathmlBuilder:function(t,e){var r=new ve.MathNode("mo",[be(t.text,t.mode)]);if("bin"===t.family){var a=we(t,e);"bold-italic"===a&&r.setAttribute("mathvariant",a)}else"punct"===t.family?r.setAttribute("separator","true"):"open"!==t.family&&"close"!==t.family||r.setAttribute("stretchy","false");return r}});var Zr={mi:"italic",mn:"normal",mtext:"normal"};te({type:"mathord",htmlBuilder:function(t,e){return Dt.makeOrd(t,e,"mathord")},mathmlBuilder:function(t,e){var r=new ve.MathNode("mi",[be(t.text,t.mode,e)]),a=we(t,e)||"italic";return a!==Zr[r.type]&&r.setAttribute("mathvariant",a),r}}),te({type:"textord",htmlBuilder:function(t,e){return Dt.makeOrd(t,e,"textord")},mathmlBuilder:function(t,e){var r,a=be(t.text,t.mode,e),n=we(t,e)||"normal";return r="text"===t.mode?new ve.MathNode("mtext",[a]):/[0-9]/.test(t.text)?new ve.MathNode("mn",[a]):"\\prime"===t.text?new ve.MathNode("mo",[a]):new ve.MathNode("mi",[a]),n!==Zr[r.type]&&r.setAttribute("mathvariant",n),r}});var Kr={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},Jr={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};te({type:"spacing",htmlBuilder:function(t,e){if(Jr.hasOwnProperty(t.text)){var r=Jr[t.text].className||"";if("text"===t.mode){var a=Dt.makeOrd(t,e,"textord");return a.classes.push(r),a}return Dt.makeSpan(["mspace",r],[Dt.mathsym(t.text,t.mode,e)],e)}if(Kr.hasOwnProperty(t.text))return Dt.makeSpan(["mspace",Kr[t.text]],[],e);throw new o('Unknown type of space "'+t.text+'"')},mathmlBuilder:function(t,e){if(!Jr.hasOwnProperty(t.text)){if(Kr.hasOwnProperty(t.text))return new ve.MathNode("mspace");throw new o('Unknown type of space "'+t.text+'"')}return new ve.MathNode("mtext",[new ve.TextNode("\xa0")])}});var Qr=function(){var t=new ve.MathNode("mtd",[]);return t.setAttribute("width","50%"),t};te({type:"tag",mathmlBuilder:function(t,e){var r=new ve.MathNode("mtable",[new ve.MathNode("mtr",[Qr(),new ve.MathNode("mtd",[Se(t.body,e)]),Qr(),new ve.MathNode("mtd",[Se(t.tag,e)])])]);return r.setAttribute("width","100%"),r}});var ta={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},ea={"\\textbf":"textbf","\\textmd":"textmd"},ra={"\\textit":"textit","\\textup":"textup"},aa=function(t,e){var r=t.font;return r?ta[r]?e.withTextFontFamily(ta[r]):ea[r]?e.withTextFontWeight(ea[r]):e.withTextFontShape(ra[r]):e};Qt({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup"],props:{numArgs:1,argTypes:["text"],greediness:2,allowedInText:!0},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"text",mode:r.mode,body:ee(n),font:a}},htmlBuilder:function(t,e){var r=aa(t,e),a=se(t.body,r,!0);return Dt.makeSpan(["mord","text"],Dt.tryCombineChars(a),r)},mathmlBuilder:function(t,e){var r=aa(t,e);return Se(t.body,r)}}),Qt({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){return{type:"underline",mode:t.parser.mode,body:e[0]}},htmlBuilder:function(t,e){var r=ue(t.body,e),a=Dt.makeLineSpan("underline-line",e),n=e.fontMetrics().defaultRuleThickness,i=Dt.makeVList({positionType:"top",positionData:r.height,children:[{type:"kern",size:n},{type:"elem",elem:a},{type:"kern",size:3*n},{type:"elem",elem:r}]},e);return Dt.makeSpan(["mord","underline"],[i],e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mo",[new ve.TextNode("\u203e")]);r.setAttribute("stretchy","true");var a=new ve.MathNode("munder",[Me(t.body,e),r]);return a.setAttribute("accentunder","true"),a}}),Qt({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler:function(t,e,r){throw new o("\\verb ended by end of line instead of matching delimiter")},htmlBuilder:function(t,e){for(var r=na(t),a=[],n=e.havingStyle(e.style.text()),i=0;iwn in jr?Nst(jr,wn,{enumerable:!0,configurable:!0,writable:!0,value:fn}):jr[wn]=fn;var vl=(jr,wn,fn)=>(Bst(jr,typeof wn!="symbol"?wn+"":wn,fn),fn);var jr=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function wn(t){var e=t.default;if(typeof e=="function"){var r=function(){return e.apply(this,arguments)};r.prototype=e.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(t).forEach(function(n){var i=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(r,n,i.get?i:{enumerable:!0,get:function(){return t[n]}})}),r}function fn(t){throw new Error('Could not dynamically require "'+t+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}var b_={exports:{}};(function(t,e){(function(r,n){t.exports=n()})(jr,function(){var r;function n(){return r.apply(null,arguments)}function i(g){return g instanceof Array||Object.prototype.toString.call(g)==="[object Array]"}function a(g){return g!=null&&Object.prototype.toString.call(g)==="[object Object]"}function s(g,E){return Object.prototype.hasOwnProperty.call(g,E)}function o(g){if(Object.getOwnPropertyNames)return Object.getOwnPropertyNames(g).length===0;for(var E in g)if(s(g,E))return;return 1}function l(g){return g===void 0}function u(g){return typeof g=="number"||Object.prototype.toString.call(g)==="[object Number]"}function h(g){return g instanceof Date||Object.prototype.toString.call(g)==="[object Date]"}function d(g,E){for(var I=[],O=g.length,G=0;G{i<<=2,a<<=2,s<<=2,e(r,n,i+0,a+0,s),e(r,n,i+1,a+1,s),e(r,n,i+2,a+2,s),e(r,n,i+3,a+3,s)}}function F0(t){const e=Math.floor(t);if(e===t)return RR(t);const r=t-e,n=2*t+1;return(i,a,s,o,l)=>{if(!((o-=l)>=s))return;let u=e*a[s];const h=l*e,d=h+l;for(let f=s,p=s+h;f=f)if(b>=f&&e===xl){const k=oo(d,f,x);isFinite(k)&&(k>0?f=(Math.floor(f/k)+1)*k:k<0&&(f=(Math.ceil(f*-k)+1)/-k))}else p.pop()}for(var m=p.length;p[0]<=d;)p.shift(),--m;for(;p[m-1]>f;)p.pop(),--m;var _=new Array(m+1),y;for(a=0;a<=m;++a)y=_[a]=[],y.x0=a>0?p[a-1]:d,y.x1=a=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r=i)&&(r=i)}return r}function H0(t,e){let r,n=-1,i=-1;if(e===void 0)for(const a of t)++i,a!=null&&(r=a)&&(r=a,n=i);else for(let a of t)(a=e(a,++i,t))!=null&&(r=a)&&(r=a,n=i);return n}function Tl(t,e){let r;if(e===void 0)for(const n of t)n!=null&&(r>n||r===void 0&&n>=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r>i||r===void 0&&i>=i)&&(r=i)}return r}function G0(t,e){let r,n=-1,i=-1;if(e===void 0)for(const a of t)++i,a!=null&&(r>a||r===void 0&&a>=a)&&(r=a,n=i);else for(let a of t)(a=e(a,++i,t))!=null&&(r>a||r===void 0&&a>=a)&&(r=a,n=i);return n}function Tu(t,e,r=0,n=t.length-1,i){for(i=i===void 0?so:V0(i);n>r;){if(n-r>600){const l=n-r+1,u=e-r+1,h=Math.log(l),d=.5*Math.exp(2*h/3),f=.5*Math.sqrt(h*d*(l-d)/l)*(u-l/2<0?-1:1),p=Math.max(r,Math.floor(e-u*d/l+f)),m=Math.min(n,Math.floor(e+(l-u)*d/l+f));Tu(t,e,p,m,i)}const a=t[e];let s=r,o=n;for(El(t,r,e),i(t[n],a)>0&&El(t,r,n);s0)for(var r=new Array(i),n=0,i,a;n=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),K0.hasOwnProperty(e)?{space:K0[e],local:t}:t}function NI(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===X0&&e.documentElement.namespaceURI===X0?e.createElement(t):e.createElementNS(r,t)}}function BI(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Mu(t){var e=Al(t);return(e.local?BI:NI)(e)}function DI(){}function Lu(t){return t==null?DI:function(){return this.querySelector(t)}}function OI(t){typeof t!="function"&&(t=Lu(t));for(var e=this._groups,r=e.length,n=new Array(r),i=0;i=1))throw new Error("invalid cell size");return s=Math.floor(Math.log(x)/Math.LN2),b()},f.thresholds=function(x){return arguments.length?(h=typeof x=="function"?x:Array.isArray(x)?Ia(Mv.call(x)):Ia(x),f):h},f.bandwidth=function(x){if(!arguments.length)return Math.sqrt(a*(a+1));if(!((x=+x)>=0))throw new Error("invalid bandwidth");return a=(Math.sqrt(4*x*x+1)-1)/2,b()},f}const Zi=11102230246251565e-32,Pr=134217729,wF=(3+8*Zi)*Zi;function Wd(t,e,r,n,i){let a,s,o,l,u=e[0],h=n[0],d=0,f=0;h>u==h>-u?(a=u,u=e[++d]):(a=h,h=n[++f]);let p=0;if(du&&(u=v),B>h&&(h=B),this._ids[L]=L}const d=(o+u)/2,f=(l+h)/2;let p=1/0,m,_,y;for(let L=0;L0&&(_=L,p=v)}let k=e[2*_],T=e[2*_+1],C=1/0;for(let L=0;Lw&&(L[v++]=D,w=this._dists[D])}this.hull=L.subarray(0,v),this.triangles=new Uint32Array(0),this.halfedges=new Uint32Array(0);return}if(sh(b,x,k,T,M,S)<0){const L=_,v=k,B=T;_=y,k=M,T=S,y=L,M=v,S=B}const R=IF(b,x,k,T,M,S);this._cx=R.x,this._cy=R.y;for(let L=0;L0&&Math.abs(D-v)<=Nv&&Math.abs(N-B)<=Nv||(v=D,B=N,w===m||w===_||w===y))continue;let z=0;for(let $=0,lt=this._hashKey(D,N);$=(d=(o+u)/2))?o=d:u=d,(y=r>=(f=(l+h)/2))?l=f:h=f,i=a,!(a=a[b=y<<1|_]))return i[b]=s,t;if(p=+t._x.call(null,a.data),m=+t._y.call(null,a.data),e===p&&r===m)return s.next=a,i?i[b]=s:t._root=s,t;do i=i?i[b]=new Array(4):t._root=new Array(4),(_=e>=(d=(o+u)/2))?o=d:u=d,(y=r>=(f=(l+h)/2))?l=f:h=f;while((b=y<<1|_)===(x=(m>=f)<<1|p>=d));return i[x]=a,i[b]=s,t}function xP(t){var e,r,n=t.length,i,a,s=new Array(n),o=new Array(n),l=1/0,u=1/0,h=-1/0,d=-1/0;for(r=0;r=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)}function fh(t,e){if((r=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var r,n=t.slice(0,r);return[n.length>1?n[0]+n.slice(2):n,+t.slice(r+1)]}function Eo(t){return t=fh(Math.abs(t)),t?t[1]:NaN}function tq(t,e){return function(r,n){for(var i=r.length,a=[],s=0,o=t[0],l=0;i>0&&o>0&&(l+o+1>n&&(o=Math.max(1,n-l)),a.push(r.substring(i-=o,i+o)),!((l+=o+1)>n));)o=t[s=(s+1)%t.length];return a.reverse().join(e)}}function eq(t){return function(e){return e.replace(/[0-9]/g,function(r){return t[+r]})}}var rq=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Co(t){if(!(e=rq.exec(t)))throw new Error("invalid format: "+t);var e;return new dh({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}Co.prototype=dh.prototype;function dh(t){this.fill=t.fill===void 0?" ":t.fill+"",this.align=t.align===void 0?">":t.align+"",this.sign=t.sign===void 0?"-":t.sign+"",this.symbol=t.symbol===void 0?"":t.symbol+"",this.zero=!!t.zero,this.width=t.width===void 0?void 0:+t.width,this.comma=!!t.comma,this.precision=t.precision===void 0?void 0:+t.precision,this.trim=!!t.trim,this.type=t.type===void 0?"":t.type+""}dh.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function nq(t){t:for(var e=t.length,r=1,n=-1,i;rnr&&(nr=t)):t>Ss?Cn(tr,t)>Cn(tr,nr)&&(nr=t):Cn(t,nr)>Cn(tr,nr)&&(tr=t)}else Ba.push(Qi=[tr=t,nr=t]);e 0;){if(u=oo(s,o,r),u===l)return n[i]=s,n[a]=o,e(n);if(u>0)s=Math.floor(s/u)*u,o=Math.ceil(o/u)*u;else if(u<0)s=Math.ceil(s*u)/u,o=Math.floor(o*u)/u;else break;l=u}return t},t}function sp(){var t=ap();return t.copy=function(){return fc(t,sp())},On.apply(t,arguments),Oa(t)}function Hx(t){var e;function r(n){return n==null||isNaN(n=+n)?e:n}return r.invert=r,r.domain=r.range=function(n){return arguments.length?(t=Array.from(n,nf),r):t.slice()},r.unknown=function(n){return arguments.length?(e=n,r):e},r.copy=function(){return Hx(t).unknown(e)},t=arguments.length?Array.from(t,nf):[0,1],Oa(r)}function Gx(t,e){t=t.slice();var r=0,n=t.length-1,i=t[r],a=t[n],s;return aMath.pow(t,e)}function $z(t){return t===Math.E?Math.log:t===10&&Math.log10||t===2&&Math.log2||(t=Math.log(t),e=>Math.log(e)/t)}function Xx(t){return(e,r)=>-t(-e,r)}function op(t){const e=t(jx,$x),r=e.domain;let n=10,i,a;function s(){return i=$z(n),a=jz(n),r()[0]<0?(i=Xx(i),a=Xx(a),t(Wz,Hz)):t(jx,$x),e}return e.base=function(o){return arguments.length?(n=+o,s()):n},e.domain=function(o){return arguments.length?(r(o),s()):r()},e.ticks=o=>{const l=r();let u=l[0],h=l[l.length-1];const d=h0){for(;f<=p;++f)for(m=1;m53)return null;"w"in U||(U.w=1),"Z"in U?(j=Ep(mc(U.y,0,1)),P=j.getUTCDay(),j=P>4||P===0?yc.ceil(j):yc(j),j=gc.offset(j,(U.V-1)*7),U.y=j.getUTCFullYear(),U.m=j.getUTCMonth(),U.d=j.getUTCDate()+(U.w+6)%7):(j=Tp(mc(U.y,0,1)),P=j.getDay(),j=P>4||P===0?pc.ceil(j):pc(j),j=dc.offset(j,(U.V-1)*7),U.y=j.getFullYear(),U.m=j.getMonth(),U.d=j.getDate()+(U.w+6)%7)}else("W"in U||"U"in U)&&("w"in U||(U.w="u"in U?U.u%7:"W"in U?1:0),P="Z"in U?Ep(mc(U.y,0,1)).getUTCDay():Tp(mc(U.y,0,1)).getDay(),U.m=0,U.d="W"in U?(U.w+6)%7+U.W*7-(P+5)%7:U.w+U.U*7-(P+6)%7);return"Z"in U?(U.H+=U.Z/100|0,U.M+=U.Z%100,Ep(U)):Tp(U)}}function R(V,Q,q,U){for(var F=0,j=Q.length,P=q.length,et,at;F=0;)r[e]=e;return r}function oH(t,e){return t[e]}function lH(t){const e=[];return e.key=t,e}function cH(){var t=xe([]),e=Vo,r=qo,n=oH;function i(a){var s=Array.from(t.apply(this,arguments),lH),o,l=s.length,u=-1,h;for(const d of a)for(o=0,++u;o
/gi,eG=t=>Rf.test(t),rG=t=>t.split(Rf),nG=t=>t.replace(/#br#/g,"
"),Uk=t=>t.replace(Rf,"#br#"),iG=t=>{let e="";return t&&(e=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,e=e.replaceAll(/\(/g,"\\("),e=e.replaceAll(/\)/g,"\\)")),e},Mr=t=>!(t===!1||["false","null","0"].includes(String(t).trim().toLowerCase())),ja=function(t){let e=t;return t.indexOf("~")!==-1?(e=e.replace(/~([^~].*)/,"<$1"),e=e.replace(/~([^~]*)$/,">$1"),ja(e)):e},pe={getRows:JH,sanitizeText:ai,sanitizeTextOrArray:tG,hasBreaks:eG,splitBreaks:rG,lineBreakRegex:Rf,removeScript:zk,getUrl:iG,evaluate:Mr},If={min:{r:0,g:0,b:0,s:0,l:0,a:0},max:{r:255,g:255,b:255,h:360,s:100,l:100,a:1},clamp:{r:t=>t>=255?255:t<0?0:t,g:t=>t>=255?255:t<0?0:t,b:t=>t>=255?255:t<0?0:t,h:t=>t%360,s:t=>t>=100?100:t<0?0:t,l:t=>t>=100?100:t<0?0:t,a:t=>t>=1?1:t<0?0:t},toLinear:t=>{const e=t/255;return t>.03928?Math.pow((e+.055)/1.055,2.4):e/12.92},hue2rgb:(t,e,r)=>(r<0&&(r+=1),r>1&&(r-=1),r<1/6?t+(e-t)*6*r:r<1/2?e:r<2/3?t+(e-t)*(2/3-r)*6:t),hsl2rgb:({h:t,s:e,l:r},n)=>{if(!e)return r*2.55;t/=360,e/=100,r/=100;const i=r<.5?r*(1+e):r+e-r*e,a=2*r-i;switch(n){case"r":return If.hue2rgb(a,i,t+1/3)*255;case"g":return If.hue2rgb(a,i,t)*255;case"b":return If.hue2rgb(a,i,t-1/3)*255}},rgb2hsl:({r:t,g:e,b:r},n)=>{t/=255,e/=255,r/=255;const i=Math.max(t,e,r),a=Math.min(t,e,r),s=(i+a)/2;if(n==="l")return s*100;if(i===a)return 0;const o=i-a,l=s>.5?o/(2-i-a):o/(i+a);if(n==="s")return l*100;switch(i){case t:return((e-r)/o+(e