├── .env
├── .gitignore
├── README.md
├── config-overrides.js
├── package.json
├── public
├── config.js
├── favicon.ico
├── iframe.html
├── index.html
├── robots.txt
└── static
│ └── font
│ ├── FZBeiWeiKaiShu-S19S_8a9c28a.woff
│ ├── FZBeiWeiKaiShu-Z15T_d19ba06.woff
│ ├── FZBoYaSongS-GB_73c639f.woff
│ ├── FZBoYaSongT-GB_f66865d.woff
│ ├── FZChaoCuHei-M10S_ac7ca24.woff
│ ├── FZCuHeiSongS-B-GB_c9c9631.woff
│ ├── FZCuYuan-M03S_4f8023c.woff
│ ├── FZCuYuan-M03T_5ef09db.woff
│ ├── FZFangSong-Z02S_5536860.woff
│ ├── FZHanZhenGuangBiaoS-GB_b3e184d.woff
│ ├── FZHei-B01S_d83f0dd.woff
│ ├── FZHei-B01T_a57116f.woff
│ ├── FZJianZhi-M23S_16251fa.woff
│ ├── FZJingLeiS-R-GB_0bccc2a.woff
│ ├── FZKai-Z03S_10cd3cc.woff
│ ├── FZLanTingHei-DB-GBK_685283f.woff
│ ├── FZLanTingHeiS-H-GB_b8a39cd.woff
│ ├── FZLanTingHeiS-R-GB_d3b0303.woff
│ ├── FZLanTingHeiT-B-GB_bf40be3.woff
│ ├── FZLanTingHeiT-DB-GBK_ad5ffd8.woff
│ ├── FZLanTingHeiT-EL-GB_273f895.woff
│ ├── FZLanTingHeiT-R-GB_0a8eb2d.woff
│ ├── FZLanTingKanHei-R-GBK_767962f.woff
│ ├── FZMiaoWuS-GB_70ba06f.woff
│ ├── FZNewXiuLi-Z11T_552e119.woff
│ ├── FZShouJinShu-S10S_62b4ce5.woff
│ ├── FZShuSong-Z01S_9270257.woff
│ ├── FZTeCuGuangHuiS-R-GB_8b24d1e.woff
│ ├── FZTieJinLiShu-S17S_ec1ee6d.woff
│ ├── FZYingBiKaiShu-S15S_1daee58.woff
│ ├── FZYingBiKaiShu-S15T_43da3a0.woff
│ ├── FZYingBiXingShu-S16S_2235873.woff
│ ├── FZZJ-LJDFONT_68fc050.woff
│ ├── FZZhengHeiS-EB-GB_c4a4f3b.woff
│ ├── FZZhengHeiS-R-GB_0189d78.woff
│ ├── FZZhunYuan-M02S_dcec285.woff
│ ├── FZZhunYuan-M02T_1a44311.woff
│ ├── FZZongYi-M05S_1abdabb.woff
│ ├── HappyZcool_a5e333f.woff
│ ├── NotoSansSC-Black_22de1ba.woff
│ ├── NotoSansSC-Bold_f586a36.woff
│ ├── NotoSansSC-DemiLight_290d222.woff
│ ├── NotoSansSC-Light_aba6b6d.woff
│ ├── NotoSansSC-Medium_16ba8f1.woff
│ ├── NotoSansSC-Regular_5fc590d.woff
│ ├── NotoSansSC-Thin_cc42e5e.woff
│ ├── iconfont_8b24bbe.eot
│ ├── iconfont_c13423f.woff2
│ ├── iconfont_db8e36d.svg
│ ├── iconfont_f88e236.woff
│ ├── iconfont_fbd5055.ttf
│ └── zcool-gdh_9a4f1b1.woff
├── src
├── App.js
├── __mocks__
│ ├── echarts.js
│ └── index.js
├── api
│ └── index.js
├── assets
│ ├── TB143rolQL0gK0jSZFxXXXWHVXa-160-116.png
│ ├── TB19PPllUz1gK0jSZLeXXb9kVXa-332-144.png
│ ├── TB1IhfqlUY1gK0jSZFCXXcwqXXa-334-144.png
│ ├── TB1cjHmlQP2gK0jSZPxXXacQpXa-332-144.png
│ ├── TB1d4nolO_1gK0jSZFqXXcpaXXa-332-144.png
│ ├── TB1hHjllFT7gK0jSZFpXXaTkpXa-332-144.png
│ ├── TB1qFDllFT7gK0jSZFpXXaTkpXa-332-144.png
│ ├── attrtable.png
│ ├── banner.jpg
│ ├── banner_video.mp4
│ ├── bar-alien.png
│ ├── bar-bothway.png
│ ├── bar-contrast.png
│ ├── bar-crosswise.png
│ ├── bar-heap.png
│ ├── bar-series.png
│ ├── bar.png
│ ├── barpolar.png
│ ├── bg.jpg
│ ├── border-box.png
│ ├── bubble.png
│ ├── calendar-heatmap.png
│ ├── cartesian-heatmap.png
│ ├── china-map.png
│ ├── earth-night.png
│ ├── funnel.png
│ ├── gauge.png
│ ├── kline.png
│ ├── line-bar.png
│ ├── line-middle.png
│ ├── line.png
│ ├── liquid-fill.png
│ ├── map-province.png
│ ├── pie-nested.png
│ ├── pie-play.png
│ ├── pie-rose.png
│ ├── pie.png
│ ├── radar.png
│ ├── sankey.png
│ ├── scatter.png
│ ├── stack-ba.png
│ ├── step-line.png
│ ├── tableplay.png
│ ├── treemap_07eb65e.png
│ └── word-cloud.png
├── components
│ ├── auto-breadcrumb
│ │ └── index.js
│ ├── auto-container
│ │ ├── index.js
│ │ └── style.less
│ ├── auto-loading
│ │ ├── index.js
│ │ └── style.less
│ ├── iconfont
│ │ └── index.js
│ ├── iframe
│ │ ├── index.js
│ │ └── style.less
│ ├── index.js
│ ├── monaco-editor
│ │ └── index.js
│ ├── scrollbar
│ │ └── index.js
│ ├── sketch-ruler
│ │ ├── canvas-ruler.js
│ │ ├── index.js
│ │ ├── line.js
│ │ ├── ruler-context-menu.js
│ │ ├── ruler-wrapper.js
│ │ ├── styles.js
│ │ └── utils.js
│ ├── typing
│ │ ├── core.js
│ │ └── index.js
│ ├── vcharts
│ │ ├── core.js
│ │ └── index.js
│ └── watermark
│ │ ├── core.js
│ │ └── index.js
├── index.js
├── layouts
│ ├── config.js
│ ├── content.js
│ ├── header.js
│ ├── index.js
│ └── sider.js
├── packages
│ ├── border
│ │ ├── index.js
│ │ └── src
│ │ │ ├── border1.js
│ │ │ ├── border2.js
│ │ │ ├── border3.js
│ │ │ ├── border4.js
│ │ │ ├── border5.js
│ │ │ ├── border6.js
│ │ │ ├── border7.js
│ │ │ ├── border8.js
│ │ │ └── border9.js
│ ├── checkbox
│ │ ├── group.js
│ │ └── index.js
│ ├── color-picker
│ │ └── index.js
│ ├── constants.js
│ ├── data-source
│ │ └── index.js
│ ├── date-picker
│ │ ├── index.js
│ │ └── range.js
│ ├── decoration
│ │ ├── index.js
│ │ └── src
│ │ │ ├── decoration1.js
│ │ │ ├── decoration2.js
│ │ │ ├── decoration3.js
│ │ │ └── decoration4.js
│ ├── dialog
│ │ ├── index.js
│ │ └── schema
│ │ │ ├── bar.js
│ │ │ ├── default.js
│ │ │ ├── index.js
│ │ │ ├── line.js
│ │ │ ├── map.js
│ │ │ ├── other.js
│ │ │ └── pie.js
│ ├── html
│ │ └── index.js
│ ├── index.js
│ ├── input
│ │ └── index.js
│ ├── map
│ │ └── index.js
│ ├── materials.js
│ ├── number
│ │ └── index.js
│ ├── radio
│ │ └── index.js
│ ├── select
│ │ ├── compoents
│ │ │ ├── bar.js
│ │ │ ├── default.js
│ │ │ ├── index.js
│ │ │ ├── line.js
│ │ │ ├── map.js
│ │ │ ├── other.js
│ │ │ └── pie.js
│ │ ├── dependence-item.js
│ │ ├── drill-down-item.js
│ │ ├── index.js
│ │ └── multi.js
│ ├── slider
│ │ └── index.js
│ ├── switch
│ │ └── index.js
│ ├── textarea
│ │ └── index.js
│ ├── upload
│ │ ├── crop.js
│ │ ├── excel.js
│ │ ├── index.js
│ │ └── util.js
│ ├── vcharts
│ │ ├── index.js
│ │ ├── options
│ │ │ ├── bar.js
│ │ │ ├── barpolar.js
│ │ │ ├── funnel.js
│ │ │ ├── gauge.js
│ │ │ ├── heatmap.js
│ │ │ ├── line.js
│ │ │ ├── liquidfill.js
│ │ │ ├── map.js
│ │ │ ├── mapcity.js
│ │ │ ├── pie.js
│ │ │ ├── radar.js
│ │ │ ├── sankey.js
│ │ │ ├── scatter.js
│ │ │ ├── treemap.js
│ │ │ └── wordcloud.js
│ │ ├── src
│ │ │ ├── bar.js
│ │ │ ├── barpolar.js
│ │ │ ├── funnel.js
│ │ │ ├── gauge.js
│ │ │ ├── heatmap.js
│ │ │ ├── line.js
│ │ │ ├── liquidfill.js
│ │ │ ├── map.js
│ │ │ ├── mapcity.js
│ │ │ ├── pie.js
│ │ │ ├── radar.js
│ │ │ ├── sankey.js
│ │ │ ├── scatter.js
│ │ │ ├── treemap.js
│ │ │ └── wordcloud.js
│ │ ├── theme.js
│ │ └── util.js
│ └── vshape
│ │ ├── countdown.js
│ │ ├── digital-flop.js
│ │ ├── indicator.js
│ │ ├── rank-panel.js
│ │ └── scroll-panel.js
├── pages
│ ├── 404.js
│ ├── account
│ │ └── index.js
│ ├── dashboard
│ │ ├── factory.js
│ │ ├── hooks.js
│ │ ├── iframe.js
│ │ ├── index.js
│ │ └── sketchRuler.js
│ ├── design-report
│ │ ├── data
│ │ │ ├── bar.js
│ │ │ ├── datav.js
│ │ │ ├── default.js
│ │ │ ├── index.js
│ │ │ ├── line.js
│ │ │ ├── map.js
│ │ │ └── pie.js
│ │ ├── index.js
│ │ ├── preview.js
│ │ ├── schema
│ │ │ ├── bar.js
│ │ │ ├── datav.js
│ │ │ ├── default.js
│ │ │ ├── index.js
│ │ │ ├── line.js
│ │ │ ├── page.js
│ │ │ └── pie.js
│ │ └── src
│ │ │ ├── aside.js
│ │ │ ├── header.js
│ │ │ ├── page.js
│ │ │ ├── setting.js
│ │ │ └── wrapper.js
│ └── design-screen
│ │ ├── bigscreen
│ │ └── configs
│ │ │ ├── datav.js
│ │ │ ├── other.js
│ │ │ └── pie.js
│ │ ├── data
│ │ ├── bar.js
│ │ ├── datav.js
│ │ ├── default.js
│ │ ├── index.js
│ │ ├── line.js
│ │ ├── map.js
│ │ ├── other.js
│ │ └── pie.js
│ │ ├── index.js
│ │ ├── preview.js
│ │ ├── schema
│ │ ├── bar.js
│ │ ├── datav.js
│ │ ├── default.js
│ │ ├── form.json
│ │ ├── index.js
│ │ ├── line.js
│ │ ├── map.js
│ │ ├── other.js
│ │ ├── page.js
│ │ └── pie.js
│ │ └── src
│ │ ├── aside.js
│ │ ├── header.js
│ │ ├── page.js
│ │ ├── setting.js
│ │ └── wrapper.js
├── polyfills.js
├── renderer
│ ├── common
│ │ ├── constants.js
│ │ ├── context.js
│ │ └── hooks.js
│ ├── factory
│ │ ├── asField.js
│ │ ├── atom.less
│ │ ├── factory.js
│ │ ├── getField.js
│ │ ├── index.js
│ │ ├── parser.js
│ │ ├── resolve.js
│ │ └── subFieldGenerator.js
│ ├── index.js
│ ├── src
│ │ ├── core-grid.js
│ │ ├── core-resolve.js
│ │ ├── core.js
│ │ ├── datav.less
│ │ ├── generator.js
│ │ ├── parser-grid.js
│ │ ├── parser.js
│ │ └── renderer.less
│ └── utils.js
├── router
│ ├── history.js
│ ├── index.js
│ └── router-map.js
├── store
│ ├── index.js
│ └── modules
│ │ ├── app.js
│ │ ├── component.js
│ │ └── index.js
├── styles
│ ├── account.less
│ ├── antd-design.less
│ ├── format.less
│ └── layout.less
└── utils
│ ├── RAFrame.js
│ ├── helper.js
│ ├── http.js
│ ├── index.js
│ └── storage.js
└── yarn.lock
/.env:
--------------------------------------------------------------------------------
1 | ## 请求地址 base api 本地开发需proxy
2 | REACT_APP_API=https://www.mockjs.com/api
3 | ## 端口号
4 | PORT=9527
5 | ## 版本号
6 | VERSION=2.0.1
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # production
7 | /dist
8 |
9 | # misc
10 | .DS_Store
11 | .env.local
12 | .env.development.local
13 | .env.test.local
14 | .env.production.local
15 |
16 | npm-debug.log*
17 | yarn-debug.log*
18 | yarn-error.log*
19 |
20 | # Editor directories and files
21 | .idea*
22 | .vscode
23 | *.suo
24 | *.ntvs*
25 | *.njsproj
26 | *.sln
27 | *.sw*
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # data-visual
2 |
3 | #### 介绍
4 |
5 | 🎉 基于 reactjs 开发的可视化大屏设计器项目 [演示demo](https://wuli-admin.gitee.io/react-wuli-admin/#/dashboard)
6 |
7 | ### 设计目标
8 | 类似阿里系的DataV、华为系的DLV、腾讯系的RayData。data-visual能保证自身足够的简单和易用、利于开发可视化项目基础应用搭建。
9 |
--------------------------------------------------------------------------------
/config-overrides.js:
--------------------------------------------------------------------------------
1 | /* 脚手架基础配置项 */
2 | const path = require("path");
3 | const paths = require("react-scripts/config/paths");
4 | const {
5 | override,
6 | disableEsLint,
7 | adjustWorkbox,
8 | addLessLoader,
9 | fixBabelImports,
10 | addWebpackAlias,
11 | overrideDevServer
12 | } = require("customize-cra");
13 | const { getThemeVariables } = require("antd/dist/theme");
14 | const CompressionWebpackPlugin = require("compression-webpack-plugin");
15 |
16 | const resolve = (dir) => path.join(__dirname, dir);
17 |
18 | // 打包自定义配置
19 | const buildCustomize = () => (config) => {
20 | if (config.mode === "development") {
21 | console.log("evn is development, skip build path change...");
22 | } else if (config.mode === "production") {
23 | console.log("evn is production, change build path...");
24 | // 关闭sourceMap
25 | config.devtool = false;
26 | // 配置打包后的文件位置修改path目录
27 | paths.appBuild = path.join(path.dirname(paths.appBuild), "dist");
28 | config.output.path = path.join(path.dirname(config.output.path), "./dist");
29 | // 添加js打包gzip配置
30 | config.plugins.push(
31 | new CompressionWebpackPlugin({
32 | test: /\.js$|\.css$/,
33 | threshold: 1024
34 | })
35 | );
36 | }
37 |
38 | return config;
39 | };
40 |
41 | // 跨域配置
42 | const devServerConfig = () => (config) => {
43 | return {
44 | ...config,
45 | proxy: {
46 | "/api": {
47 | target: process.env.REACT_APP_API,
48 | changeOrigin: true,
49 | pathRewrite: {
50 | "^/api": "/"
51 | }
52 | }
53 | }
54 | };
55 | };
56 |
57 | module.exports = {
58 | webpack: override(
59 | // do stuff with the webpack config...
60 | disableEsLint(),
61 | // 按需加载
62 | fixBabelImports("import", {
63 | libraryName: "antd",
64 | libraryDirectory: "es",
65 | style: true
66 | }),
67 | // 配置别名
68 | addWebpackAlias({
69 | "@": resolve("src"),
70 | "~components": resolve("src/components"),
71 | "~packages": resolve("src/packages"),
72 | "~renderer": resolve("src/renderer")
73 | }),
74 | adjustWorkbox((wb) =>
75 | Object.assign(wb, {
76 | skipWaiting: true,
77 | exclude: (wb.exclude || []).concat("index.html")
78 | })
79 | ),
80 | addLessLoader({
81 | localIdentName: "[local]--[hash:base64:5]",
82 | modifyVars: getThemeVariables({
83 | dark: true, // 开启暗黑模式
84 | compact: true // 开启紧凑模式
85 | }),
86 | javascriptEnabled: true
87 | }),
88 | buildCustomize()
89 | ),
90 | devServer: overrideDevServer(devServerConfig())
91 | };
92 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "datav-pro",
3 | "version": "1.0.0",
4 | "private": true,
5 | "homepage": ".",
6 | "dependencies": {
7 | "@testing-library/jest-dom": "^4.2.4",
8 | "@testing-library/react": "^9.3.2",
9 | "@testing-library/user-event": "^7.1.2",
10 | "antd": "^4.6.2",
11 | "antd-img-crop": "^3.9.0",
12 | "axios": "^0.19.2",
13 | "copy-text-to-clipboard": "^2.2.0",
14 | "crypto-js": "^4.0.0",
15 | "echarts": "^4.8.0",
16 | "echarts-liquidfill": "^2.0.6",
17 | "echarts-wordcloud": "^1.1.3",
18 | "mockjs": "^1.1.0",
19 | "rc-color-picker": "^1.2.6",
20 | "react": "^16.13.1",
21 | "react-custom-scrollbars": "^4.2.1",
22 | "react-dom": "^16.13.1",
23 | "react-grid-layout": "^1.0.0",
24 | "react-redux": "^7.2.0",
25 | "react-rnd": "^10.1.10",
26 | "react-router-dom": "^5.1.2",
27 | "react-scripts": "3.4.1",
28 | "redux": "^4.0.5",
29 | "redux-persist": "^6.0.0",
30 | "redux-thunk": "^2.3.0",
31 | "size-sensor": "^1.0.1",
32 | "xlsx": "^0.16.9"
33 | },
34 | "scripts": {
35 | "start": "react-app-rewired start",
36 | "build": "react-app-rewired build",
37 | "test": "react-app-rewired test",
38 | "clean": "npm cache clean --force"
39 | },
40 | "eslintConfig": {
41 | "extends": "react-app"
42 | },
43 | "browserslist": {
44 | "production": [
45 | ">0.2%",
46 | "not dead",
47 | "not op_mini all",
48 | "ie 11"
49 | ],
50 | "development": [
51 | "last 1 chrome version",
52 | "last 1 firefox version",
53 | "last 1 safari version",
54 | "ie 11"
55 | ]
56 | },
57 | "devDependencies": {
58 | "@monaco-editor/react": "^3.3.1",
59 | "babel-plugin-import": "^1.13.0",
60 | "compression-webpack-plugin": "^4.0.0",
61 | "customize-cra": "^0.9.1",
62 | "less": "^3.11.1",
63 | "less-loader": "5.0.0",
64 | "react-app-polyfill": "^1.0.6",
65 | "react-app-rewired": "^2.1.6",
66 | "styled-components": "^5.1.1"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/public/config.js:
--------------------------------------------------------------------------------
1 | window.appConfig = {
2 | // 后端接口地址
3 | APP_SERVER_API: "https://www.mock-api.com/api"
4 | };
5 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/favicon.ico
--------------------------------------------------------------------------------
/public/iframe.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | iframe Window
6 |
12 |
13 |
14 | Hello there, i'm an iframe. 通信见控制台
15 |
16 |
25 |
26 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | DataV Pro 实验室
14 |
15 |
16 |
17 |
18 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/static/font/FZBeiWeiKaiShu-S19S_8a9c28a.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZBeiWeiKaiShu-S19S_8a9c28a.woff
--------------------------------------------------------------------------------
/public/static/font/FZBeiWeiKaiShu-Z15T_d19ba06.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZBeiWeiKaiShu-Z15T_d19ba06.woff
--------------------------------------------------------------------------------
/public/static/font/FZBoYaSongS-GB_73c639f.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZBoYaSongS-GB_73c639f.woff
--------------------------------------------------------------------------------
/public/static/font/FZBoYaSongT-GB_f66865d.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZBoYaSongT-GB_f66865d.woff
--------------------------------------------------------------------------------
/public/static/font/FZChaoCuHei-M10S_ac7ca24.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZChaoCuHei-M10S_ac7ca24.woff
--------------------------------------------------------------------------------
/public/static/font/FZCuHeiSongS-B-GB_c9c9631.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZCuHeiSongS-B-GB_c9c9631.woff
--------------------------------------------------------------------------------
/public/static/font/FZCuYuan-M03S_4f8023c.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZCuYuan-M03S_4f8023c.woff
--------------------------------------------------------------------------------
/public/static/font/FZCuYuan-M03T_5ef09db.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZCuYuan-M03T_5ef09db.woff
--------------------------------------------------------------------------------
/public/static/font/FZFangSong-Z02S_5536860.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZFangSong-Z02S_5536860.woff
--------------------------------------------------------------------------------
/public/static/font/FZHanZhenGuangBiaoS-GB_b3e184d.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZHanZhenGuangBiaoS-GB_b3e184d.woff
--------------------------------------------------------------------------------
/public/static/font/FZHei-B01S_d83f0dd.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZHei-B01S_d83f0dd.woff
--------------------------------------------------------------------------------
/public/static/font/FZHei-B01T_a57116f.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZHei-B01T_a57116f.woff
--------------------------------------------------------------------------------
/public/static/font/FZJianZhi-M23S_16251fa.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZJianZhi-M23S_16251fa.woff
--------------------------------------------------------------------------------
/public/static/font/FZJingLeiS-R-GB_0bccc2a.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZJingLeiS-R-GB_0bccc2a.woff
--------------------------------------------------------------------------------
/public/static/font/FZKai-Z03S_10cd3cc.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZKai-Z03S_10cd3cc.woff
--------------------------------------------------------------------------------
/public/static/font/FZLanTingHei-DB-GBK_685283f.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZLanTingHei-DB-GBK_685283f.woff
--------------------------------------------------------------------------------
/public/static/font/FZLanTingHeiS-H-GB_b8a39cd.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZLanTingHeiS-H-GB_b8a39cd.woff
--------------------------------------------------------------------------------
/public/static/font/FZLanTingHeiS-R-GB_d3b0303.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZLanTingHeiS-R-GB_d3b0303.woff
--------------------------------------------------------------------------------
/public/static/font/FZLanTingHeiT-B-GB_bf40be3.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZLanTingHeiT-B-GB_bf40be3.woff
--------------------------------------------------------------------------------
/public/static/font/FZLanTingHeiT-DB-GBK_ad5ffd8.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZLanTingHeiT-DB-GBK_ad5ffd8.woff
--------------------------------------------------------------------------------
/public/static/font/FZLanTingHeiT-EL-GB_273f895.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZLanTingHeiT-EL-GB_273f895.woff
--------------------------------------------------------------------------------
/public/static/font/FZLanTingHeiT-R-GB_0a8eb2d.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZLanTingHeiT-R-GB_0a8eb2d.woff
--------------------------------------------------------------------------------
/public/static/font/FZLanTingKanHei-R-GBK_767962f.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZLanTingKanHei-R-GBK_767962f.woff
--------------------------------------------------------------------------------
/public/static/font/FZMiaoWuS-GB_70ba06f.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZMiaoWuS-GB_70ba06f.woff
--------------------------------------------------------------------------------
/public/static/font/FZNewXiuLi-Z11T_552e119.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZNewXiuLi-Z11T_552e119.woff
--------------------------------------------------------------------------------
/public/static/font/FZShouJinShu-S10S_62b4ce5.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZShouJinShu-S10S_62b4ce5.woff
--------------------------------------------------------------------------------
/public/static/font/FZShuSong-Z01S_9270257.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZShuSong-Z01S_9270257.woff
--------------------------------------------------------------------------------
/public/static/font/FZTeCuGuangHuiS-R-GB_8b24d1e.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZTeCuGuangHuiS-R-GB_8b24d1e.woff
--------------------------------------------------------------------------------
/public/static/font/FZTieJinLiShu-S17S_ec1ee6d.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZTieJinLiShu-S17S_ec1ee6d.woff
--------------------------------------------------------------------------------
/public/static/font/FZYingBiKaiShu-S15S_1daee58.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZYingBiKaiShu-S15S_1daee58.woff
--------------------------------------------------------------------------------
/public/static/font/FZYingBiKaiShu-S15T_43da3a0.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZYingBiKaiShu-S15T_43da3a0.woff
--------------------------------------------------------------------------------
/public/static/font/FZYingBiXingShu-S16S_2235873.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZYingBiXingShu-S16S_2235873.woff
--------------------------------------------------------------------------------
/public/static/font/FZZJ-LJDFONT_68fc050.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZZJ-LJDFONT_68fc050.woff
--------------------------------------------------------------------------------
/public/static/font/FZZhengHeiS-EB-GB_c4a4f3b.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZZhengHeiS-EB-GB_c4a4f3b.woff
--------------------------------------------------------------------------------
/public/static/font/FZZhengHeiS-R-GB_0189d78.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZZhengHeiS-R-GB_0189d78.woff
--------------------------------------------------------------------------------
/public/static/font/FZZhunYuan-M02S_dcec285.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZZhunYuan-M02S_dcec285.woff
--------------------------------------------------------------------------------
/public/static/font/FZZhunYuan-M02T_1a44311.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZZhunYuan-M02T_1a44311.woff
--------------------------------------------------------------------------------
/public/static/font/FZZongYi-M05S_1abdabb.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/FZZongYi-M05S_1abdabb.woff
--------------------------------------------------------------------------------
/public/static/font/HappyZcool_a5e333f.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/HappyZcool_a5e333f.woff
--------------------------------------------------------------------------------
/public/static/font/NotoSansSC-Black_22de1ba.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/NotoSansSC-Black_22de1ba.woff
--------------------------------------------------------------------------------
/public/static/font/NotoSansSC-Bold_f586a36.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/NotoSansSC-Bold_f586a36.woff
--------------------------------------------------------------------------------
/public/static/font/NotoSansSC-DemiLight_290d222.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/NotoSansSC-DemiLight_290d222.woff
--------------------------------------------------------------------------------
/public/static/font/NotoSansSC-Light_aba6b6d.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/NotoSansSC-Light_aba6b6d.woff
--------------------------------------------------------------------------------
/public/static/font/NotoSansSC-Medium_16ba8f1.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/NotoSansSC-Medium_16ba8f1.woff
--------------------------------------------------------------------------------
/public/static/font/NotoSansSC-Regular_5fc590d.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/NotoSansSC-Regular_5fc590d.woff
--------------------------------------------------------------------------------
/public/static/font/NotoSansSC-Thin_cc42e5e.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/NotoSansSC-Thin_cc42e5e.woff
--------------------------------------------------------------------------------
/public/static/font/iconfont_8b24bbe.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/iconfont_8b24bbe.eot
--------------------------------------------------------------------------------
/public/static/font/iconfont_c13423f.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/iconfont_c13423f.woff2
--------------------------------------------------------------------------------
/public/static/font/iconfont_f88e236.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/iconfont_f88e236.woff
--------------------------------------------------------------------------------
/public/static/font/iconfont_fbd5055.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/iconfont_fbd5055.ttf
--------------------------------------------------------------------------------
/public/static/font/zcool-gdh_9a4f1b1.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/public/static/font/zcool-gdh_9a4f1b1.woff
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react";
2 | import { ConfigProvider } from "antd";
3 | import zhCN from "antd/es/locale/zh_CN";
4 | import { Provider } from "react-redux";
5 | import { PersistGate } from "redux-persist/es/integration/react";
6 |
7 | import store, { persistor } from "./store";
8 | import Router from "./router";
9 |
10 | import "./styles/format.less";
11 |
12 | class App extends PureComponent {
13 | render() {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/src/api/index.js:
--------------------------------------------------------------------------------
1 | import fetch from "@/utils/http";
2 |
3 | /**
4 | * @description 账户登录
5 | */
6 | export function accountIn(param) {
7 | return fetch({
8 | url: "/user/login",
9 | method: "post",
10 | data: param
11 | });
12 | }
13 |
14 | export function echartBarAPI() {
15 | return fetch({
16 | url: "/echart/bar",
17 | method: "get"
18 | });
19 | }
20 |
21 | export function echartPieAPI() {
22 | return fetch({
23 | url: "/echart/pie",
24 | method: "get"
25 | });
26 | }
27 |
28 | export function echartScatterAPI() {
29 | return fetch({
30 | url: "/echart/scatter",
31 | method: "get"
32 | });
33 | }
34 |
35 | export function echartRadarAPI() {
36 | return fetch({
37 | url: "/echart/radar",
38 | method: "get"
39 | });
40 | }
41 |
42 | export function dataVScreen() {
43 | return fetch({
44 | url: "/datav/screen",
45 | method: "post"
46 | });
47 | }
48 |
49 | export function dataVGrid() {
50 | return fetch({
51 | url: "/datav/grid",
52 | method: "post"
53 | });
54 | }
55 |
56 | export function dataVApiList() {
57 | return fetch({
58 | url: "/datav/api",
59 | method: "get"
60 | });
61 | }
62 |
63 | export function dataVSqlList() {
64 | return fetch({
65 | url: "/datav/sql",
66 | method: "get"
67 | });
68 | }
69 |
70 | export function dataVTableList() {
71 | return fetch({
72 | url: "/component/table",
73 | method: "get"
74 | });
75 | }
76 |
77 | export function dataVTables() {
78 | return fetch({
79 | url: "/component/tables",
80 | method: "get"
81 | });
82 | }
83 |
--------------------------------------------------------------------------------
/src/assets/TB143rolQL0gK0jSZFxXXXWHVXa-160-116.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/TB143rolQL0gK0jSZFxXXXWHVXa-160-116.png
--------------------------------------------------------------------------------
/src/assets/TB19PPllUz1gK0jSZLeXXb9kVXa-332-144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/TB19PPllUz1gK0jSZLeXXb9kVXa-332-144.png
--------------------------------------------------------------------------------
/src/assets/TB1IhfqlUY1gK0jSZFCXXcwqXXa-334-144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/TB1IhfqlUY1gK0jSZFCXXcwqXXa-334-144.png
--------------------------------------------------------------------------------
/src/assets/TB1cjHmlQP2gK0jSZPxXXacQpXa-332-144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/TB1cjHmlQP2gK0jSZPxXXacQpXa-332-144.png
--------------------------------------------------------------------------------
/src/assets/TB1d4nolO_1gK0jSZFqXXcpaXXa-332-144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/TB1d4nolO_1gK0jSZFqXXcpaXXa-332-144.png
--------------------------------------------------------------------------------
/src/assets/TB1hHjllFT7gK0jSZFpXXaTkpXa-332-144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/TB1hHjllFT7gK0jSZFpXXaTkpXa-332-144.png
--------------------------------------------------------------------------------
/src/assets/TB1qFDllFT7gK0jSZFpXXaTkpXa-332-144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/TB1qFDllFT7gK0jSZFpXXaTkpXa-332-144.png
--------------------------------------------------------------------------------
/src/assets/attrtable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/attrtable.png
--------------------------------------------------------------------------------
/src/assets/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/banner.jpg
--------------------------------------------------------------------------------
/src/assets/banner_video.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/banner_video.mp4
--------------------------------------------------------------------------------
/src/assets/bar-alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/bar-alien.png
--------------------------------------------------------------------------------
/src/assets/bar-bothway.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/bar-bothway.png
--------------------------------------------------------------------------------
/src/assets/bar-contrast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/bar-contrast.png
--------------------------------------------------------------------------------
/src/assets/bar-crosswise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/bar-crosswise.png
--------------------------------------------------------------------------------
/src/assets/bar-heap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/bar-heap.png
--------------------------------------------------------------------------------
/src/assets/bar-series.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/bar-series.png
--------------------------------------------------------------------------------
/src/assets/bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/bar.png
--------------------------------------------------------------------------------
/src/assets/barpolar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/barpolar.png
--------------------------------------------------------------------------------
/src/assets/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/bg.jpg
--------------------------------------------------------------------------------
/src/assets/border-box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/border-box.png
--------------------------------------------------------------------------------
/src/assets/bubble.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/bubble.png
--------------------------------------------------------------------------------
/src/assets/calendar-heatmap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/calendar-heatmap.png
--------------------------------------------------------------------------------
/src/assets/cartesian-heatmap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/cartesian-heatmap.png
--------------------------------------------------------------------------------
/src/assets/china-map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/china-map.png
--------------------------------------------------------------------------------
/src/assets/earth-night.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/earth-night.png
--------------------------------------------------------------------------------
/src/assets/funnel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/funnel.png
--------------------------------------------------------------------------------
/src/assets/gauge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/gauge.png
--------------------------------------------------------------------------------
/src/assets/kline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/kline.png
--------------------------------------------------------------------------------
/src/assets/line-bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/line-bar.png
--------------------------------------------------------------------------------
/src/assets/line-middle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/line-middle.png
--------------------------------------------------------------------------------
/src/assets/line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/line.png
--------------------------------------------------------------------------------
/src/assets/liquid-fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/liquid-fill.png
--------------------------------------------------------------------------------
/src/assets/map-province.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/map-province.png
--------------------------------------------------------------------------------
/src/assets/pie-nested.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/pie-nested.png
--------------------------------------------------------------------------------
/src/assets/pie-play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/pie-play.png
--------------------------------------------------------------------------------
/src/assets/pie-rose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/pie-rose.png
--------------------------------------------------------------------------------
/src/assets/pie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/pie.png
--------------------------------------------------------------------------------
/src/assets/radar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/radar.png
--------------------------------------------------------------------------------
/src/assets/sankey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/sankey.png
--------------------------------------------------------------------------------
/src/assets/scatter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/scatter.png
--------------------------------------------------------------------------------
/src/assets/stack-ba.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/stack-ba.png
--------------------------------------------------------------------------------
/src/assets/step-line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/step-line.png
--------------------------------------------------------------------------------
/src/assets/tableplay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/tableplay.png
--------------------------------------------------------------------------------
/src/assets/treemap_07eb65e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/treemap_07eb65e.png
--------------------------------------------------------------------------------
/src/assets/word-cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arraycto/data-visual/d48dee0fe2b6e683a2548a5dc6961097d7b141e8/src/assets/word-cloud.png
--------------------------------------------------------------------------------
/src/components/auto-breadcrumb/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { withRouter } from "react-router-dom";
3 | import { connect } from "react-redux";
4 |
5 | import { Breadcrumb } from "antd";
6 | import menuList from "@/layouts/config";
7 |
8 | /**
9 | * 根据当前浏览器地址栏的路由地址,在menuConfig中查找路由跳转的路径
10 | * 如路由地址为/dashboard,则查找到的路径为[{title: "首页",...}]
11 | */
12 | const getPath = (menuList, pathname) => {
13 | let temppath = [];
14 | try {
15 | function getNodePath(node) {
16 | temppath.push(node);
17 | // 找到符合条件的节点,通过throw终止掉递归
18 | if (node.path === pathname) {
19 | throw new Error("GOT IT!");
20 | }
21 | if (node.children && node.children.length > 0) {
22 | for (let i = 0; i < node.children.length; i++) {
23 | getNodePath(node.children[i]);
24 | }
25 | // 当前节点的子节点遍历完依旧没找到,则删除路径中的该节点
26 | temppath.pop();
27 | } else {
28 | // 找到叶子节点时,删除路径当中的该叶子节点
29 | temppath.pop();
30 | }
31 | }
32 | for (let i = 0; i < menuList.length; i++) {
33 | getNodePath(menuList[i]);
34 | }
35 | } catch (e) {
36 | return temppath;
37 | }
38 | };
39 |
40 | const autoBreadcrumb = (props) => {
41 | const { location, dispatch } = props;
42 | const { pathname } = location;
43 | const [curPath, setCurPath] = useState(null);
44 |
45 | useEffect(() => {
46 | let path = getPath(menuList, pathname);
47 | const first = path && path[0];
48 |
49 | if (first && first.title.trim() !== "可视化设计器") {
50 | path = [{ title: "可视化设计器", path: "/dashboard" }].concat(path);
51 | }
52 | setCurPath(path);
53 | }, [pathname]);
54 |
55 | return (
56 |
57 | {curPath &&
58 | curPath.map((item) =>
59 | item.title === "可视化设计器" ? (
60 | {
64 | dispatch({ type: "ROUTER_PATH", data: item.path });
65 | }}
66 | >
67 | {item.title}
68 |
69 | ) : (
70 | {item.title}
71 | )
72 | )}
73 |
74 | );
75 | };
76 |
77 | export default connect()(withRouter(autoBreadcrumb));
78 |
--------------------------------------------------------------------------------
/src/components/auto-container/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useLayoutEffect, forwardRef } from "react";
2 | import PropTypes from "prop-types";
3 | import { useAutoResize } from "~renderer/common/hooks";
4 |
5 | import "./style.less";
6 | import { useEffect } from "react";
7 |
8 | /**
9 | * 根据屏幕尺寸缩放
10 | * @param zoom scaleX(等比例缩放高度铺满)、scaleY(等比例缩放宽度铺满)、cover(全屏铺满),
11 | * @param style 屏幕尺寸配置
12 | * par
13 | */
14 | const FullScreenContainer = forwardRef(({ children, className, style, zoom = "cover" }, ref) => {
15 | const { domRef } = useAutoResize(ref);
16 | const [scale, setScale] = useState("");
17 | const { width, height } = style;
18 | const { clientHeight, clientWidth } = document.body;
19 |
20 | useEffect(() => {
21 | if (zoom === "scaleY") {
22 | setScale(`scale(${clientHeight / height})`);
23 | } else if (zoom === "scaleX") {
24 | setScale(`scale(${clientWidth / width})`);
25 | } else {
26 | setScale(`scale(${clientWidth / width}, ${clientHeight / height})`);
27 | }
28 | });
29 |
30 | useLayoutEffect(() => {
31 | Object.assign(domRef.current.style, {
32 | width: `${width}px`,
33 | height: `${height}px`,
34 | transform: scale
35 | });
36 | if (zoom === "scaleY") {
37 | const clientLeft = +((clientWidth - (width * clientHeight) / height) / 2).toFixed(3);
38 | domRef.current.style.left = `${clientLeft}px`;
39 | }
40 | });
41 |
42 | return (
43 |
44 | {children}
45 |
46 | );
47 | });
48 |
49 | FullScreenContainer.propTypes = {
50 | children: PropTypes.node,
51 | style: PropTypes.object
52 | };
53 |
54 | export default FullScreenContainer;
55 |
--------------------------------------------------------------------------------
/src/components/auto-container/style.less:
--------------------------------------------------------------------------------
1 | #gc-fullscreen-container {
2 | position: absolute;
3 | top: 0;
4 | left: 0;
5 | transform-origin: left top;
6 | background-repeat: no-repeat;
7 | overflow: hidden;
8 | ::-webkit-scrollbar,
9 | .preview-wrapper ::-webkit-scrollbar {
10 | width: 0;
11 | height: 0;
12 | color: transparent;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/auto-loading/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Spin } from "antd";
3 | import "./style.less";
4 |
5 | const AutoLoading = () => {
6 | return (
7 |
8 |
9 |
10 | );
11 | };
12 |
13 | export default AutoLoading;
14 |
--------------------------------------------------------------------------------
/src/components/auto-loading/style.less:
--------------------------------------------------------------------------------
1 | .loading-container {
2 | position: absolute;
3 | top: 50%;
4 | left: 50%;
5 | text-align: center;
6 | margin: -20px 0 0 -20px;
7 | width: 40px;
8 | height: 40px;
9 | }
--------------------------------------------------------------------------------
/src/components/iconfont/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createFromIconfontCN } from "@ant-design/icons";
3 |
4 | const Iconfont = (props) => {
5 | const { antd = false, type, ...rest } = props;
6 | const IconFonts = createFromIconfontCN({
7 | scriptUrl: ["//at.alicdn.com/t/font_908589_jybcb6d47pq.js"]
8 | });
9 | if (antd) {
10 | const Icons = require("@ant-design/icons")[type];
11 | return ;
12 | } else {
13 | return ;
14 | }
15 | };
16 |
17 | export default Iconfont;
18 |
--------------------------------------------------------------------------------
/src/components/iframe/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, forwardRef } from "react";
2 | import { Spin } from "antd";
3 | import { converLayout } from "~renderer/utils";
4 | import { onEvent, offEvent } from "@/utils";
5 |
6 | import "./style.less";
7 |
8 | const VIframe = forwardRef((props, ref) => {
9 | let {
10 | className = "",
11 | style = {
12 | width: "100%",
13 | height: "100%"
14 | },
15 | frameBorder = 0,
16 | src = "",
17 | data,
18 | events,
19 | onAction = () => {}
20 | } = props;
21 | const [loading, setLoading] = useState(true);
22 | const [tempStyle, setTempStyle] = useState({});
23 |
24 | useEffect(() => {
25 | onEvent(window, "message", onMessage);
26 |
27 | postMessage("update", data);
28 | return () => {
29 | setLoading(false);
30 | offEvent(window, "message", onMessage);
31 | };
32 | }, []);
33 |
34 | const onMessage = (e) => {
35 | if (!e.data || e.data === "" || !events) {
36 | return;
37 | }
38 |
39 | const [prefix, type] = e.data.type.split(":");
40 |
41 | if (prefix !== "iframe" || !type) {
42 | return;
43 | }
44 |
45 | if (type === "resize" && e.data.data) {
46 | setTempStyle({
47 | width: converLayout(e.data.data.width),
48 | height: converLayout(e.data.data.height)
49 | });
50 | } else {
51 | const action = events[type];
52 | action && onAction(e, action, Object.assign(data, e.data.data));
53 | }
54 | };
55 |
56 | const postMessage = (type, data) => {
57 | ref.current.contentWindow?.postMessage(
58 | {
59 | type: `iframe:${type}`,
60 | data
61 | },
62 | "*"
63 | );
64 | };
65 |
66 | const onLoad = () => {
67 | setLoading(false);
68 | src && postMessage("init", data);
69 | };
70 |
71 | // TODO:组件通知iframe reload
72 | const reload = (query) => {
73 | if (query) {
74 | return receive(query);
75 | }
76 |
77 | if (src) {
78 | // const enCodeUrl = decodeURIComponent(src); enCodeUrl.startsWith("http") ? enCodeUrl : `http://${enCodeUrl}`;
79 | ref.current.src = src;
80 | }
81 | };
82 |
83 | // 当别的组件把数据发给 iframe 里面的时候执行。
84 | const receive = (values) => {
85 | if (src) {
86 | ref.current.src = src;
87 | postMessage("receive", Object.assign(data, values));
88 | }
89 | };
90 |
91 | return (
92 |
93 |
101 |
102 | );
103 | });
104 |
105 | export default VIframe;
106 |
--------------------------------------------------------------------------------
/src/components/iframe/style.less:
--------------------------------------------------------------------------------
1 | .gc-iframe {
2 | position: absolute;
3 | top: 0;
4 | left: 0;
5 | bottom: 0;
6 | width: 100%;
7 | z-index: 9;
8 | .ant-spin-container {
9 | width: 100%;
10 | height: 100%;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | /* @remove-on-es-build-begin */
2 | const ENV = process.env.NODE_ENV;
3 | if (
4 | ENV !== "production" &&
5 | typeof console !== "undefined" &&
6 | console.warn && // eslint-disable-line no-console
7 | typeof window !== "undefined"
8 | ) {
9 | // eslint-disable-next-line no-console
10 | console.warn(
11 | "You are using a whole package of antd, " +
12 | "please use https://www.npmjs.com/package/babel-plugin-import to reduce app bundle size."
13 | );
14 | }
15 | /* @remove-on-es-build-end */
16 | export { default as AutoBreadcrumb } from "./auto-breadcrumb";
17 |
18 | export { default as AutonContainer } from "./auto-container";
19 |
20 | export { default as IconFont } from "./iconfont";
21 |
22 | export { default as IFrame } from "./iframe";
23 |
24 | export { default as AutoLoading } from "./auto-loading";
25 |
26 | export { default as MonacoEditor } from "./monaco-editor";
27 |
28 | export { default as Scrollbar } from "./scrollbar";
29 |
30 | export { default as SketchRuler } from "./sketch-ruler";
31 |
32 | export { default as Typing } from "./typing";
33 |
34 | export { default as Vcharts } from "./vcharts";
35 |
36 | export { default as watermark } from "./watermark";
37 |
--------------------------------------------------------------------------------
/src/components/monaco-editor/index.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState, useEffect, useCallback, useImperativeHandle, forwardRef } from "react";
2 | import MonacoEditor, { monaco } from "@monaco-editor/react";
3 | import { isLooselyNumber, isCssLength } from "~renderer/utils";
4 |
5 | import Loading from "../auto-loading";
6 |
7 | const CodePanel = (props, ref) => {
8 | const editorRef = useRef();
9 | const [code, setCode] = useState("// try to write code somewhere 😈 \n");
10 | let { value, width, height, readOnly = false, language = "json" } = props;
11 |
12 | const calcUtil = useCallback(
13 | (item) => {
14 | return isLooselyNumber(item) ? Number(item) : isCssLength(item) ? item : "100%";
15 | },
16 | [width, height]
17 | );
18 |
19 | // you can configure the locales
20 | monaco.config({ "vs/nls": { availableLanguages: { "*": "zh-cn" } } });
21 |
22 | useImperativeHandle(ref, () => ({
23 | getValue: () => JSON.parse(editorRef.current.getValue())
24 | }));
25 |
26 | useEffect(() => {
27 | try {
28 | setCode(JSON.stringify(value, null, 4));
29 | } catch (e) {
30 | // ignore
31 | }
32 | }, [value, language]);
33 |
34 | return (
35 | }
41 | value={code}
42 | options={{
43 | contextmenu: false,
44 | wrappingIndent: "deepIndent",
45 | readOnly: readOnly,
46 | automaticLayout: true,
47 | autoIndent: true,
48 | formatOnType: true,
49 | formatOnPaste: true,
50 | scrollBeyondLastLine: false,
51 | renderControlCharacters: false,
52 | minimap: {
53 | enabled: false
54 | }
55 | }}
56 | editorDidMount={(ev, editor) => {
57 | editorRef.current = editor;
58 | }}
59 | />
60 | );
61 | };
62 |
63 | export default forwardRef(CodePanel);
64 |
--------------------------------------------------------------------------------
/src/components/scrollbar/index.js:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from "react";
2 | import { Scrollbars } from "react-custom-scrollbars";
3 |
4 | import { useAutoResize } from "~renderer/common/hooks";
5 |
6 | const renderThumb = ({ style, ...props }) => {
7 | const thumbStyle = {
8 | paddingLeft: 10,
9 | backgroundColor: "rgba(255, 255, 255, 0.14)"
10 | };
11 | return ;
12 | };
13 |
14 | const VScrollbar = forwardRef((props, ref) => {
15 | const { h, hideHorizontal = false } = props;
16 | const { height, domRef } = useAutoResize(ref);
17 |
18 | if (hideHorizontal) {
19 | return (
20 |
21 |
29 | {props.children}
30 |
31 |
32 | );
33 | }
34 |
35 | return (
36 |
37 |
46 | {props.children}
47 |
48 |
49 | );
50 | });
51 |
52 | export default VScrollbar;
53 |
--------------------------------------------------------------------------------
/src/components/sketch-ruler/line.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react";
2 | import PropTypes from "prop-types";
3 |
4 | export default class Line extends PureComponent {
5 | constructor(props) {
6 | super(props);
7 | this.state = {
8 | value: props.value
9 | };
10 | }
11 | handleDown = (e) => {
12 | const { vertical, index, scale, onMouseDown, onRelease } = this.props;
13 | const { value: startValue } = this.state;
14 | const startD = vertical ? e.clientY : e.clientX;
15 | onMouseDown();
16 | const onMove = (e) => {
17 | const currentD = vertical ? e.clientY : e.clientX;
18 | const newValue = Math.round(startValue + (currentD - startD) / scale);
19 | this.setState({ value: newValue });
20 | };
21 | const onEnd = () => {
22 | onRelease(this.state.value, index);
23 | document.removeEventListener("mousemove", onMove);
24 | document.removeEventListener("mouseup", onEnd);
25 | };
26 | document.addEventListener("mousemove", onMove);
27 | document.addEventListener("mouseup", onEnd);
28 | };
29 | handleRemove = () => {
30 | const { index, onRemove } = this.props;
31 | onRemove(index);
32 | };
33 |
34 | render() {
35 | const { vertical, start, scale } = this.props;
36 | const { value } = this.state;
37 | const offset = (value - start) * scale;
38 | if (offset < 0) return null;
39 | const lineStyle = vertical ? { top: offset } : { left: offset };
40 |
41 | return (
42 |
43 |
44 |
45 | ×
46 |
47 | {value}
48 |
49 |
50 | );
51 | }
52 | }
53 |
54 | Line.propTypes = {
55 | index: PropTypes.number,
56 | start: PropTypes.number,
57 | vertical: PropTypes.bool,
58 | scale: PropTypes.number,
59 | value: PropTypes.number,
60 | onRemove: PropTypes.func,
61 | onMouseDown: PropTypes.func,
62 | onRelease: PropTypes.func
63 | };
64 |
--------------------------------------------------------------------------------
/src/components/typing/core.js:
--------------------------------------------------------------------------------
1 | class TypingCore {
2 | constructor(opts) {
3 | this.opts = opts || {};
4 | this.source = opts.source;
5 | this.output = opts.output;
6 | this.delay = opts.delay || 120;
7 | this.chain = {
8 | parent: null,
9 | dom: this.output,
10 | val: []
11 | };
12 | if (!(typeof this.opts.done === "function")) this.opts.done = function () {};
13 | }
14 |
15 | init() {
16 | //初始化函数
17 | this.chain.val = this.convert(this.source, this.chain.val);
18 | }
19 |
20 | convert(dom, arr) {
21 | //将dom节点的子节点转换成数组,
22 | let children = Array.from(dom.childNodes);
23 | for (let i = 0; i < children.length; i++) {
24 | let node = children[i];
25 | if (node.nodeType === 3) {
26 | arr = arr.concat(node.nodeValue.split("")); //将字符串转换成字符串数组,后面打印时才会一个一个的打印
27 | } else if (node.nodeType === 1) {
28 | let val = [];
29 | val = this.convert(node, val);
30 | arr.push({
31 | dom: node,
32 | val: val
33 | });
34 | }
35 | }
36 | return arr;
37 | }
38 |
39 | print(dom, val, callback) {
40 | setTimeout(function () {
41 | dom.appendChild(document.createTextNode(val));
42 | callback();
43 | }, this.delay);
44 | }
45 |
46 | play(ele) {
47 | // 当打印最后一个字符时,动画完毕,执行done
48 | if (!ele.val.length) {
49 | if (ele.parent) this.play(ele.parent);
50 | else this.opts.done();
51 | return;
52 | }
53 | let current = ele.val.shift(); // 获取第一个元素,同时删除数组中的第一个元素
54 | if (typeof current === "string") {
55 | this.print(ele.dom, current, () => {
56 | this.play(ele); // 继续打印下一个字符
57 | });
58 | } else {
59 | let dom = current.dom.cloneNode(); // 克隆节点,不克隆节点的子节点,所以不用加参数true
60 | ele.dom.appendChild(dom);
61 | this.play({
62 | parent: ele,
63 | dom,
64 | val: current.val
65 | });
66 | }
67 | }
68 |
69 | start() {
70 | this.init();
71 | this.play(this.chain);
72 | }
73 | }
74 |
75 | export default TypingCore;
76 |
--------------------------------------------------------------------------------
/src/components/typing/index.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useEffect } from "react";
2 | import Typing from "./core";
3 |
4 | const TypingPrint = (props) => {
5 | const { source, delay = 30 } = props;
6 |
7 | const sourceEl = useRef();
8 | const outputEl = useRef();
9 |
10 | useEffect(() => {
11 | const typing = new Typing({
12 | source: sourceEl.current,
13 | output: outputEl.current,
14 | delay: delay
15 | });
16 | typing.start();
17 | }, [delay]);
18 |
19 | return (
20 | <>
21 |
22 |
23 | >
24 | );
25 | };
26 |
27 | export default TypingPrint;
28 |
--------------------------------------------------------------------------------
/src/components/vcharts/index.js:
--------------------------------------------------------------------------------
1 | import echarts from "echarts";
2 | import VEchartsCore from "./core";
3 | import "~packages/vcharts/theme";
4 |
5 | // export the Component the echarts Object.
6 | export default class VEcharts extends VEchartsCore {
7 | constructor(props) {
8 | super(props);
9 | this.echartsLib = echarts;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * create in 2020-05-11 by Aaron
3 | */
4 | import "./polyfills.js";
5 | import React from "react";
6 | import ReactDOM from "react-dom";
7 |
8 | import App from "./App";
9 |
10 | const UI_ROOT_ID = "datav-ui-root";
11 |
12 | /**
13 | * If you don't want to use mock-server
14 | * you want to use MockJs for mock api
15 | * you can execute: import 'mock'
16 | *
17 | * Currently MockJs will be used in the production environment,
18 | * please remove it before going online! ! !
19 | */
20 | if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") {
21 | require("./__mocks__");
22 | }
23 |
24 | ReactDOM.render(, document.getElementById(UI_ROOT_ID));
25 |
--------------------------------------------------------------------------------
/src/layouts/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 系统默认菜单配置项
3 | */
4 | export default [
5 | {
6 | icon: "wp-hot",
7 | path: "/dashboard",
8 | title: "可视化设计器",
9 | roles: ["admin", "editor", "guest"]
10 | },
11 | {
12 | icon: "wp-box",
13 | path: "/factory",
14 | title: "schema组件",
15 | roles: ["admin", "editor", "guest"]
16 | },
17 | {
18 | icon: "wp-discount",
19 | path: "/sketch-ruler",
20 | title: "测量工具尺",
21 | roles: ["admin", "editor", "guest"]
22 | },
23 | {
24 | icon: "wp-remind1",
25 | path: "/utils",
26 | title: "自定义 hooks",
27 | roles: ["admin", "editor"]
28 | },
29 | {
30 | icon: "wp-data",
31 | path: "/iframe",
32 | title: "iframe通信",
33 | roles: ["admin", "editor"]
34 | }
35 | ];
36 |
--------------------------------------------------------------------------------
/src/layouts/content.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import React, { Suspense } from "react";
3 | import { Redirect, withRouter, Route, Switch } from "react-router-dom";
4 | import { Layout } from "antd";
5 | import store from "@/store";
6 | import { AutoLoading, Scrollbar } from "~components";
7 | import routerList from "@/router/router-map";
8 |
9 | import { useDocumentTitle } from "~renderer/common/hooks";
10 | import { getPageTitle } from "@/utils/helper";
11 | import menuList from "./config";
12 |
13 | const LayoutContent = (props) => {
14 | const { location } = props;
15 | const { userInfo } = store.getState().app;
16 | const pageTiltle = getPageTitle(menuList, location.pathname);
17 | useDocumentTitle(pageTiltle);
18 |
19 | // Query whether you have permission to enter this page
20 | const hasPermission = (route) => {
21 | return userInfo.role === "admin" || !route.roles || route.roles.includes(userInfo.role);
22 | };
23 |
24 | return (
25 |
26 | }>
27 |
28 |
29 |
30 | {routerList.map((route) => {
31 | return hasPermission(route) && ;
32 | })}
33 |
34 |
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default withRouter(LayoutContent);
42 |
--------------------------------------------------------------------------------
/src/layouts/header.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Layout, Avatar, Popover } from "antd";
3 | import { withRouter } from "react-router-dom";
4 | import { connect } from "react-redux";
5 |
6 | import { AutoBreadcrumb, IconFont } from "~components";
7 |
8 | import storage from "@/utils/storage";
9 |
10 | const LayoutHeader = (props) => {
11 | const { history, dispatch, userInfo, collapsed } = props;
12 |
13 | // 随机头像
14 | const ColorList = ["#2db7f5", "#19be6b", "#ff9900", "#ed4014", "#e8eaec"];
15 | const mid = Number(userInfo.name) || "0";
16 |
17 | const handleToggle = () => {
18 | dispatch({ type: "MENU_SIDEBAR", data: !collapsed });
19 | };
20 |
21 | const handleSignOut = () => {
22 | storage.clearAll();
23 | history.replace({
24 | pathname: "/account"
25 | });
26 | };
27 |
28 | const content = (
29 |
30 |
31 | 退出登录
32 |
33 | );
34 |
35 | return (
36 |
37 |
38 | {collapsed ? (
39 |
40 | ) : (
41 |
42 | )}
43 |
44 |
45 |
46 |
54 | {userInfo.name[0]}
55 |
56 |
57 |
58 | {userInfo.name}
59 |
60 |
61 |
62 |
63 |
64 | );
65 | };
66 |
67 | export default connect((state) => ({
68 | userInfo: state.app.userInfo,
69 | collapsed: state.app.sidebarOpened
70 | }))(withRouter(LayoutHeader));
71 |
--------------------------------------------------------------------------------
/src/layouts/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { BackTop, Layout } from "antd";
3 | import LayoutHeader from "./header";
4 | import LayoutSider from "./sider";
5 | import LayoutContent from "./content";
6 |
7 | import "../styles/layout.less";
8 |
9 | function BaseLayout() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 | document.querySelector("#gc-layout")} />
17 | DataV Pro ©2020 Created by Aaron
18 |
19 |
20 | );
21 | }
22 |
23 | export default BaseLayout;
24 |
--------------------------------------------------------------------------------
/src/packages/border/index.js:
--------------------------------------------------------------------------------
1 | export { default as BorderBox1 } from "./src/border1";
2 |
3 | export { default as BorderBox2 } from "./src/border2";
4 |
5 | export { default as BorderBox3 } from "./src/border3";
6 |
7 | export { default as BorderBox4 } from "./src/border4";
8 |
9 | export { default as BorderBox5 } from "./src/border5";
10 |
11 | export { default as BorderBox6 } from "./src/border6";
12 |
13 | export { default as BorderBox7 } from "./src/border7";
14 |
15 | export { default as BorderBox8 } from "./src/border8";
16 |
17 | export { default as BorderBox9 } from "./src/border9";
18 |
--------------------------------------------------------------------------------
/src/packages/border/src/border2.js:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from "react";
2 | import PropTypes from "prop-types";
3 | import classnames from "classnames";
4 | import { useAutoResize } from "~renderer/common/hooks";
5 |
6 | const BorderBox = forwardRef(({ children, reverse = false, style, backgroundColor = "transparent" }, ref) => {
7 | const { width, height, domRef } = useAutoResize(ref);
8 |
9 | const mergedColor = ["red", "rgba(0,0,255,0.8)"];
10 |
11 | const classNames = classnames("svg-container", {
12 | "svg-reverse": reverse
13 | });
14 |
15 | return (
16 |
17 |
46 |
47 |
{children}
48 |
49 | );
50 | });
51 |
52 | BorderBox.propTypes = {
53 | children: PropTypes.node,
54 | reverse: PropTypes.bool,
55 | style: PropTypes.object,
56 | color: PropTypes.array,
57 | backgroundColor: PropTypes.string
58 | };
59 |
60 | BorderBox.defaultProps = {
61 | reverse: false,
62 | backgroundColor: "transparent"
63 | };
64 |
65 | export default BorderBox;
66 |
--------------------------------------------------------------------------------
/src/packages/border/src/border3.js:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from "react";
2 | import PropTypes from "prop-types";
3 | import classnames from "classnames";
4 | import { useAutoResize } from "~renderer/common/hooks";
5 |
6 | const BorderBox = forwardRef(({ children, reverse = false, style, backgroundColor = "transparent" }, ref) => {
7 | const { width, height, domRef } = useAutoResize(ref);
8 |
9 | const mergedColor = ["#1370fb", "#1370fb"];
10 | const classNames = classnames("svg-container", {
11 | "svg-reverse": reverse
12 | });
13 |
14 | return (
15 |
16 |
48 |
49 |
{children}
50 |
51 | );
52 | });
53 |
54 | BorderBox.propTypes = {
55 | children: PropTypes.node,
56 | reverse: PropTypes.bool,
57 | style: PropTypes.object,
58 | color: PropTypes.array,
59 | backgroundColor: PropTypes.string
60 | };
61 |
62 | BorderBox.defaultProps = {
63 | reverse: false,
64 | backgroundColor: "transparent"
65 | };
66 |
67 | export default BorderBox;
68 |
--------------------------------------------------------------------------------
/src/packages/border/src/border4.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useMemo, forwardRef } from "react";
2 | import PropTypes from "prop-types";
3 | import { useAutoResize } from "~renderer/common/hooks";
4 | import { uuid } from "@/utils";
5 |
6 | const BorderBox = forwardRef(
7 | ({ children, style, waitTime = 3, backgroundColor = "transparent", reverse = false }, ref) => {
8 | const { width, height, domRef } = useAutoResize(ref);
9 |
10 | const [{ path, gradient, mask }] = useState(() => {
11 | const id = uuid();
12 |
13 | return {
14 | path: `border-box-8-path-${id}`,
15 | gradient: `border-box-8-gradient-${id}`,
16 | mask: `border-box-8-mask-${id}`
17 | };
18 | });
19 |
20 | const pathD = useMemo(
21 | () =>
22 | reverse
23 | ? `M 2.5, 2.5 L 2.5, ${height - 2.5} L ${width - 2.5}, ${height - 2.5} L ${width - 2.5}, 2.5 L 2.5, 2.5`
24 | : `M2.5, 2.5 L${width - 2.5}, 2.5 L${width - 2.5}, ${height - 2.5} L2.5, ${height - 2.5} L2.5, 2.5`,
25 | [width, height, reverse]
26 | );
27 |
28 | const mergedColor = ["#235fa7", "#4fd2dd"];
29 |
30 | const length = useMemo(() => (width + height - 5) * 2, [width, height]);
31 |
32 | return (
33 |
34 |
63 |
64 |
{children}
65 |
66 | );
67 | }
68 | );
69 |
70 | BorderBox.propTypes = {
71 | children: PropTypes.node,
72 | style: PropTypes.object,
73 | waitTime: PropTypes.number,
74 | backgroundColor: PropTypes.string,
75 | reverse: PropTypes.bool
76 | };
77 |
78 | export default BorderBox;
79 |
--------------------------------------------------------------------------------
/src/packages/border/src/border6.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo, forwardRef } from "react";
2 | import PropTypes from "prop-types";
3 | import { useAutoResize } from "~renderer/common/hooks";
4 |
5 | const border = ["left-top", "right-top", "left-bottom", "right-bottom"];
6 |
7 | const BorderBox = forwardRef(({ children, style, backgroundColor = "transparent" }, ref) => {
8 | const { width, height, domRef } = useAutoResize(ref);
9 |
10 | const mergedColor = ["#1d48c4", "#d3e1f8"];
11 |
12 | const styles = useMemo(
13 | () => ({
14 | boxShadow: `inset 0 0 25px 3px ${mergedColor[0]}`,
15 | ...style
16 | }),
17 | [style]
18 | );
19 |
20 | return (
21 |
22 |
31 |
32 | {border.map((borderName) => (
33 |
36 | ))}
37 |
{children}
38 |
39 | );
40 | });
41 |
42 | BorderBox.propTypes = {
43 | children: PropTypes.node,
44 | style: PropTypes.object,
45 | backgroundColor: PropTypes.string
46 | };
47 |
48 | export default BorderBox;
49 |
--------------------------------------------------------------------------------
/src/packages/border/src/border9.js:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from "react";
2 | import PropTypes from "prop-types";
3 | import { useAutoResize } from "~renderer/common/hooks";
4 |
5 | const BorderBox = forwardRef(({ children, style, backgroundColor = "transparent" }, ref) => {
6 | const { width, height, domRef } = useAutoResize(ref);
7 |
8 | const mergedColor = ["#6586ec", "#2cf7fe"];
9 |
10 | return (
11 |
12 |
41 |
{children}
42 |
43 | );
44 | });
45 |
46 | BorderBox.propTypes = {
47 | children: PropTypes.node,
48 | style: PropTypes.object,
49 | backgroundColor: PropTypes.string
50 | };
51 |
52 | export default BorderBox;
53 |
--------------------------------------------------------------------------------
/src/packages/checkbox/group.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Checkbox } from "antd";
3 |
4 | const VCheckboxes = (p) => {
5 | const { enum: enums, enumNames } = p.schema || {};
6 | const _value = p.value && Array.isArray(p.value) ? p.value : [];
7 |
8 | return (
9 | p.onChange(p.name, values)}
13 | >
14 | {(enums || [true, false]).map((val, index) => (
15 |
16 |
22 |
23 | ))}
24 |
25 | );
26 | };
27 |
28 | export default VCheckboxes;
29 |
--------------------------------------------------------------------------------
/src/packages/checkbox/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Checkbox } from "antd";
3 |
4 | const VCheckbox = (p) => {
5 | return (
6 | p.onChange(p.name, e.target.checked)}
10 | checked={p.value}
11 | />
12 | );
13 | };
14 |
15 | export default VCheckbox;
16 |
--------------------------------------------------------------------------------
/src/packages/color-picker/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import React, { useMemo, useState } from "react";
3 | import ColorPicker from "rc-color-picker";
4 | import { Input } from "antd";
5 | import "rc-color-picker/assets/index.css";
6 |
7 | import { HexToRgb, RgbToHex, isRgba } from "@/utils";
8 |
9 | const VColor = (props) => {
10 | const [color, setColor] = useState("");
11 | const [alpha, setAlpha] = useState(100);
12 | const { format } = props.schema;
13 |
14 | const onPickerChange = (e) => {
15 | if (props.disabled || props.readonly) return;
16 | const rgbaValue = HexToRgb(e.color, e.alpha);
17 | props.onChange(props.name, rgbaValue);
18 | };
19 |
20 | const onInputChange = (e) => {
21 | const value = e.target.value ?? "";
22 | setAlpha(100);
23 | props.onChange(props.name, value);
24 | };
25 |
26 | const curColor = useMemo(() => {
27 | if (!props.value || props.value === "transparent") {
28 | setAlpha(100);
29 | return "";
30 | }
31 | if (isRgba(props.value)) {
32 | const { opacity, color } = RgbToHex(props.value);
33 | setColor(color);
34 | setAlpha(opacity);
35 | }
36 | return props.value;
37 | }, [props.value]);
38 |
39 | return (
40 |
57 | }
58 | />
59 | );
60 | };
61 |
62 | export default VColor;
63 |
--------------------------------------------------------------------------------
/src/packages/date-picker/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import moment from "moment";
3 | import { DatePicker, TimePicker } from "antd";
4 | import locale from "antd/es/date-picker/locale/zh_CN";
5 | import "moment/locale/zh-cn";
6 |
7 | import { getFormat } from "~renderer/utils";
8 |
9 | const dateHoc = (p, onChange, DateComponent) => {
10 | const style = p.invalid ? { borderColor: "#f5222d" } : {};
11 | const { format = "dateTime" } = p.schema;
12 | const dateFormat = getFormat(format);
13 | let defaultObj = {};
14 | if (p.value) {
15 | defaultObj.value = moment(p.value, dateFormat);
16 | } else {
17 | defaultObj.value = "";
18 | }
19 |
20 | const dateParams = {
21 | ...p.options,
22 | ...defaultObj,
23 | style: { width: "100%", ...style },
24 | disabled: p.disabled || p.readonly,
25 | onChange
26 | };
27 |
28 | if (format === "dateTime") {
29 | dateParams.showTime = true;
30 | }
31 |
32 | return ;
33 | };
34 |
35 | const VDatePicker = (p) => {
36 | const { format = "dateTime" } = p.schema;
37 | const onChange = (value, string) => p.onChange(p.name, string);
38 | const DateComponent = format === "time" ? TimePicker : DatePicker;
39 | return dateHoc(p, onChange, DateComponent);
40 | };
41 |
42 | export default VDatePicker;
43 |
--------------------------------------------------------------------------------
/src/packages/date-picker/range.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { DatePicker } from "antd";
3 | import locale from "antd/es/date-picker/locale/zh_CN";
4 | import moment from "moment";
5 | import "moment/locale/zh-cn";
6 |
7 | import { getFormat } from "~renderer/utils";
8 |
9 | const rangeHoc = (p, onChange, RangeComponent) => {
10 | const { format = "dateTime" } = p.schema;
11 | const dateFormat = getFormat(format);
12 | let defaultObj = {};
13 | if (p.value && Array.isArray(p.value) && p.value[0] && p.value[1]) {
14 | defaultObj = {
15 | defaultValue: [moment(p.value[0], dateFormat), moment(p.value[1], dateFormat)]
16 | };
17 | }
18 | const datePrams = {
19 | ...p.options,
20 | ...defaultObj,
21 | style: { width: "100%" },
22 | showTime: format === "dateTime",
23 | disabled: p.disabled || p.readonly,
24 | onChange
25 | };
26 | return ;
27 | };
28 |
29 | const { RangePicker } = DatePicker;
30 |
31 | const VdateRange = (p) => {
32 | const onChange = (value, string) => p.onChange(p.name, string);
33 | return rangeHoc(p, onChange, RangePicker);
34 | };
35 |
36 | export default VdateRange;
37 |
--------------------------------------------------------------------------------
/src/packages/decoration/index.js:
--------------------------------------------------------------------------------
1 | export { default as Decoration1 } from "./src/decoration1";
2 |
3 | export { default as Decoration2 } from "./src/decoration2";
4 |
5 | export { default as Decoration3 } from "./src/decoration3";
6 |
7 | export { default as Decoration4 } from "./src/decoration4";
8 |
--------------------------------------------------------------------------------
/src/packages/decoration/src/decoration3.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo, forwardRef } from "react";
2 | import PropTypes from "prop-types";
3 | import { useAutoResize } from "~renderer/common/hooks";
4 |
5 | const defaultColor = ["#7acaec", "transparent"];
6 | const pointSideLength = 7;
7 | const svgWH = [300, 35];
8 | const rowNum = 2;
9 | const rowPoints = 25;
10 | const halfPointSideLength = pointSideLength / 2;
11 |
12 | function getPoints() {
13 | const [w, h] = svgWH;
14 |
15 | const horizontalGap = w / (rowPoints + 1);
16 | const verticalGap = h / (rowNum + 1);
17 |
18 | let points = new Array(rowNum)
19 | .fill(0)
20 | .map((foo, i) => new Array(rowPoints).fill(0).map((foo, j) => [horizontalGap * (j + 1), verticalGap * (i + 1)]));
21 |
22 | return points.reduce((all, item) => [...all, ...item], []);
23 | }
24 |
25 | const Decoration = forwardRef(({ style }, ref) => {
26 | const { width, height, domRef } = useAutoResize(ref);
27 |
28 | function calcSVGData() {
29 | return {
30 | points: getPoints(),
31 | svgScale: [width / svgWH[0], height / svgWH[1]]
32 | };
33 | }
34 |
35 | const { svgScale, points } = useMemo(calcSVGData, [width, height]);
36 |
37 | return (
38 |
39 |
65 |
66 | );
67 | });
68 |
69 | Decoration.propTypes = {
70 | className: PropTypes.string,
71 | style: PropTypes.object,
72 | color: PropTypes.array
73 | };
74 |
75 | export default Decoration;
76 |
--------------------------------------------------------------------------------
/src/packages/dialog/schema/index.js:
--------------------------------------------------------------------------------
1 | import BAR_SCHEMA from "./bar";
2 | import LINE_SCHEMA from "./line";
3 | import MAP_SCHEMA from "./map";
4 | import OTHER_SCHEMA from "./other";
5 |
6 | // if you should all configs.
7 | const screenToSchema = [BAR_SCHEMA, LINE_SCHEMA, MAP_SCHEMA, OTHER_SCHEMA].flat(1);
8 | export default screenToSchema;
9 |
--------------------------------------------------------------------------------
/src/packages/html/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const VHtml = ({ value, defaultValue }) => {
4 | let __html = "";
5 | try {
6 | __html = value ? value : defaultValue;
7 | if (typeof __html !== "string") {
8 | __html = "";
9 | }
10 | } catch (error) {}
11 | return ;
12 | };
13 |
14 | export default VHtml;
15 |
--------------------------------------------------------------------------------
/src/packages/index.js:
--------------------------------------------------------------------------------
1 | import checkbox from "./checkbox";
2 | import checkboxes from "./checkbox/group";
3 | import color from "./color-picker";
4 | import date from "./date-picker";
5 | import dateRange from "./date-picker/range";
6 | import input from "./input";
7 | import map from "./map";
8 | import multiSelect from "./select/multi";
9 | import number from "./number";
10 | import radio from "./radio";
11 | import select from "./select";
12 | import slider from "./slider";
13 | import switch1 from "./switch";
14 | import textarea from "./textarea";
15 | import upload from "./upload";
16 | import uploadCrop from "./upload/crop";
17 | import uploadExcel from "./upload/excel";
18 |
19 | import dataSource from "./data-source";
20 | import drillDownSelect from "./select/drill-down-item";
21 | import dependenceSelect from "./select/dependence-item";
22 |
23 | const widgets = {
24 | checkbox,
25 | checkboxes, // checkbox多选
26 | color,
27 | date,
28 | dateRange,
29 | input,
30 | map,
31 | multiSelect, // 下拉多选
32 | number,
33 | radio,
34 | select,
35 | slider, // 带滚条的number
36 | switch: switch1,
37 | textarea,
38 | upload,
39 | uploadCrop,
40 | uploadExcel,
41 | dataSource,
42 | drillDownSelect,
43 | dependenceSelect
44 | };
45 |
46 | // 组件映射关系
47 | const mapping = {
48 | default: "input",
49 | string: "input",
50 | boolean: "checkbox",
51 | integer: "number",
52 | number: "number",
53 | object: "map",
54 | "string:upload": "upload",
55 | "string:crop": "uploadCrop",
56 | "string:excel": "uploadExcel",
57 | "string:date": "date",
58 | "string:dateTime": "date",
59 | "string:time": "date",
60 | "string:textarea": "textarea",
61 | "string:color": "color",
62 | "string:image": "input",
63 | "range:date": "dateRange",
64 | "range:dateTime": "dateRange",
65 | "*?enum": "select",
66 | "array?enum": "checkboxes",
67 | "*?readonly": "text"
68 | };
69 |
70 | export { widgets, mapping };
71 |
--------------------------------------------------------------------------------
/src/packages/input/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { PictureOutlined } from "@ant-design/icons";
3 | import { Input, Popover } from "antd";
4 |
5 | const defaultImg = "https://img.alicdn.com/tfs/TB14tSiKhTpK1RjSZFKXXa2wXXa-354-330.png";
6 |
7 | const previewContent = (format, value) => {
8 | return format === "image" ?
: null;
9 | };
10 |
11 | const previewNode = (format, value) => {
12 | if (format !== "image") {
13 | return null;
14 | }
15 | return (
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | const VInput = (p) => {
23 | const { options = {}, invalid, schema = {} } = p;
24 | const style = invalid ? { borderColor: "#ff4d4f", boxShadow: "0 0 0 2px rgba(255,77,79,.2)" } : {};
25 | const { format = "text", maxLength } = schema;
26 | const type = format === "image" ? "text" : format;
27 | const handleChange = (e) => p.onChange(p.name, e.target.value);
28 | let suffix = undefined;
29 | try {
30 | const _value = p.value || "";
31 | suffix = options.suffix;
32 | if (!suffix && maxLength) {
33 | suffix = (
34 | maxLength ? { fontSize: 12, color: "#ff4d4f" } : { fontSize: 12, color: "#999" }}>
35 | {_value.length + " / " + maxLength}
36 |
37 | );
38 | }
39 | } catch (error) {}
40 | const config = {
41 | ...options,
42 | maxLength,
43 | suffix
44 | };
45 | return (
46 |
55 | );
56 | };
57 |
58 | export default VInput;
59 |
--------------------------------------------------------------------------------
/src/packages/map/index.js:
--------------------------------------------------------------------------------
1 | import React, { memo, useState } from "react";
2 | import { Modal, Drawer } from "antd";
3 |
4 | import { toRawType } from "@/utils/helper";
5 |
6 | const SubComponent = memo((p) => {
7 | return (
8 |
9 | {Object.keys(p.value).map((name) => {
10 | return p.getSubField({
11 | name,
12 | value: p.value[name],
13 | onChange(key, val, objValue) {
14 | let value = { ...p.value, [key]: val };
15 | // 第三个参数,允许object里的一个子控件改动整个object的值
16 | if (objValue) {
17 | value = objValue;
18 | }
19 | p.onChange(p.name, value);
20 | },
21 | rootValue: p.value
22 | });
23 | })}
24 |
25 | );
26 | });
27 |
28 | const MapWithModal = (props) => {
29 | const { options = {}, schema } = props || {};
30 | const [show, setShow] = useState(false);
31 | const toggle = () => setShow((o) => !o);
32 | // 模态框
33 | if (options && options.modal) {
34 | const config = toRawType(options.modal) === "object" ? options.modal : {};
35 | const { text } = config;
36 | return (
37 | <>
38 | {text && toRawType(text) === "string" ? "+ " + text : "+ 配置"}
39 |
48 |
49 |
50 | >
51 | );
52 | }
53 | // 抽屉
54 | if (options && options.drawer) {
55 | const config = toRawType(options.drawer) === "object" ? options.drawer : {};
56 | const { text } = config;
57 | return (
58 | <>
59 | {text && toRawType(text) === "string" ? "+ " + text : "+ 配置"}
60 |
61 |
62 |
63 | >
64 | );
65 | }
66 |
67 | return ;
68 | };
69 |
70 | export default MapWithModal;
71 |
--------------------------------------------------------------------------------
/src/packages/materials.js:
--------------------------------------------------------------------------------
1 | import { Vbar, Vline, Vpie, Vmap, VmapCity, Vwordcloud } from "./vcharts";
2 | import { Decoration1, Decoration2, Decoration3, Decoration4 } from "./decoration";
3 | import ScrollPanel from "./vshape/scroll-panel";
4 | import ScrollRankPanel from "./vshape/rank-panel";
5 | import DigitalFlop from "./vshape/digital-flop";
6 | import Countdown from "./vshape/countdown";
7 | import Indicator from "./vshape/indicator";
8 | import {
9 | BorderBox1,
10 | BorderBox2,
11 | BorderBox3,
12 | BorderBox4,
13 | BorderBox5,
14 | BorderBox6,
15 | BorderBox7,
16 | BorderBox8,
17 | BorderBox9
18 | } from "./border";
19 |
20 | /**
21 | * 物料元件市场
22 | */
23 | export default {
24 | bar: Vbar,
25 | barCrosswise: Vbar,
26 | barSeries: Vbar,
27 | barHeap: Vbar,
28 | barContrast: Vbar,
29 | barBothway: Vbar,
30 | barAlien: Vbar,
31 | line: Vline,
32 | stepLine: Vline,
33 | lineMiddle: Vline,
34 | lineBar: Vline,
35 | pie: Vpie,
36 | pieNested: Vpie,
37 | pieRose: Vpie,
38 | pieDouble: Vpie,
39 | piePlay: Vpie,
40 | chinaMap: Vmap,
41 | mapCity: VmapCity,
42 | wordCloud: Vwordcloud,
43 | decoration1: Decoration1,
44 | decoration2: Decoration2,
45 | decoration3: Decoration3,
46 | decoration4: Decoration4,
47 | scrollPanel: ScrollPanel,
48 | rankPanel: ScrollRankPanel,
49 | digitalFlop: DigitalFlop,
50 | countdown: Countdown,
51 | indicators: Indicator,
52 | border1: BorderBox1,
53 | border2: BorderBox2,
54 | border3: BorderBox3,
55 | border4: BorderBox4,
56 | border5: BorderBox5,
57 | border6: BorderBox6,
58 | border7: BorderBox7,
59 | border8: BorderBox8,
60 | border9: BorderBox9
61 | };
62 |
--------------------------------------------------------------------------------
/src/packages/number/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { InputNumber } from "antd";
3 |
4 | const numberHoc = (NumberComponent) => (props) => {
5 | const style = props.invalid ? { borderColor: "#f5222d" } : {};
6 | const { max, min, step } = props.schema;
7 | let obj = {};
8 | if (max || max === 0) {
9 | obj = { max };
10 | }
11 |
12 | if (min || min === 0) {
13 | obj = { ...obj, min };
14 | }
15 |
16 | if (step) {
17 | obj = { ...obj, step };
18 | }
19 |
20 | const onChange = (value) => {
21 | props.onChange(props.name, value);
22 | };
23 |
24 | return (
25 |
33 | );
34 | };
35 |
36 | export default numberHoc(InputNumber);
37 |
--------------------------------------------------------------------------------
/src/packages/radio/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Radio } from "antd";
3 |
4 | const radioHoc = (props, onChange, RadioComponent) => {
5 | const { enum: enums, enumNames } = props.schema || {};
6 |
7 | return (
8 |
9 | {(enums || [true, false]).map((val, index) => (
10 |
11 |
17 |
18 | ))}
19 |
20 | );
21 | };
22 |
23 | const VRadio = (p) => {
24 | const { optionType = "default" } = p.options;
25 | const onChange = (e) => p.onChange(p.name, e.target.value);
26 | const RadioComponent = optionType === "button" ? Radio.Button : Radio;
27 | return radioHoc(p, onChange, RadioComponent);
28 | };
29 |
30 | export default VRadio;
31 |
--------------------------------------------------------------------------------
/src/packages/select/compoents/default.js:
--------------------------------------------------------------------------------
1 | // 公共基础配置项抽离
2 | const BASE_CONF = {
3 | width: 8,
4 | height: 300,
5 | background: "",
6 | remark: "",
7 | isCustomStyle: false,
8 | borderRadius: "",
9 | borderColor: "",
10 | borderWidth: "",
11 | borderStyle: "solid",
12 | shadowOffset: 0,
13 | shadowColor: "",
14 | shadowWidth: 0,
15 | animateType: "",
16 | animateTime: "",
17 | animateSpeed: "",
18 | animateRepeat: "",
19 | drillDownOpen: false,
20 | drillDown: [],
21 | isRefresh: false,
22 | refreshTime: 1800
23 | };
24 |
25 | // 公共数据配置项抽离
26 | const BASE_CONF_DATA = {
27 | dataType: "json",
28 | dataSqlId: "",
29 | dataModals: {},
30 | dataApiId: ""
31 | };
32 |
33 | export { BASE_CONF, BASE_CONF_DATA };
34 |
--------------------------------------------------------------------------------
/src/packages/select/compoents/index.js:
--------------------------------------------------------------------------------
1 | import BAR_CONF from "./bar";
2 | import LINE_CONF from "./line";
3 | import MAP_CONF from "./map";
4 | import OTHER_CONF from "./other";
5 |
6 | // if you should all configs.
7 | const condition = [BAR_CONF, LINE_CONF, MAP_CONF, OTHER_CONF].flat(1);
8 |
9 | export default condition;
10 |
--------------------------------------------------------------------------------
/src/packages/select/compoents/other.js:
--------------------------------------------------------------------------------
1 | import { BASE_CONF, BASE_CONF_DATA } from "./default.js";
2 |
3 | export default [
4 | {
5 | name: "词云图",
6 | type: "wordCloud",
7 | data: {
8 | title: "词云图",
9 | ...BASE_CONF,
10 | config: {
11 | unit: "",
12 | backgroundColor: "#E3F7FF",
13 | fontPadding: 8,
14 | fontRotate: true,
15 | minFontSize: 12,
16 | maxFontSize: 35,
17 | shape: "circle",
18 | gridSize: 5,
19 | rotationStep: true
20 | },
21 | dataConfig: {
22 | ...BASE_CONF_DATA,
23 | data: {
24 | series: [
25 | { name: "龙头镇", value: "111" },
26 | { name: "大埔镇", value: "222" },
27 | { name: "太平镇", value: "458" },
28 | { name: "沙埔镇", value: "445" },
29 | { name: "东泉镇", value: "456" },
30 | { name: "凤山镇", value: "647" },
31 | { name: "六塘镇", value: "189" },
32 | { name: "冲脉镇", value: "864" },
33 | { name: "寨隆镇", value: "652" }
34 | ]
35 | }
36 | }
37 | }
38 | }
39 | ];
40 |
--------------------------------------------------------------------------------
/src/packages/select/dependence-item.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useCallback } from "react";
2 | import { Select } from "antd";
3 | import { useEditorStore } from "@/renderer/common/hooks";
4 |
5 | const dependenceSelectHoc = (SelectComponent) => ({ name, value, onChange }) => {
6 | const { state } = useEditorStore();
7 | const options = state.components.filter((v) => v.uniqueId !== state.selected && v.data.dependence);
8 |
9 | const rootValue = useMemo(() => {
10 | return value;
11 | }, [value]);
12 |
13 | // TODO: 联动参数处理
14 | const onDependenceChange = useCallback(
15 | (value) => {
16 | onChange(name, value);
17 | },
18 | [onChange]
19 | );
20 |
21 | return (
22 |
30 | {options.length > 0 &&
31 | options.map((item, index) => {
32 | return (
33 |
34 | {item.name}
35 |
36 | );
37 | })}
38 |
39 | );
40 | };
41 |
42 | export default dependenceSelectHoc(Select);
43 |
--------------------------------------------------------------------------------
/src/packages/select/drill-down-item.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from "react";
2 | import { Select } from "antd";
3 | import { cloneDeep } from "lodash";
4 | import collections from "./compoents";
5 | import { guid } from "@/utils";
6 |
7 | const drillDownSelectHoc = (SelectComponent) => (props) => {
8 | const { name, value, onChange, displayName } = props;
9 | const drillDownList = useRef(collections).current;
10 |
11 | // TODO: 下钻参数处理 redux存储
12 | const onDrillChange = (val) => {
13 | let result;
14 | const values = drillDownList.filter((o) => o.type === val).map((v) => ((v["uniqueId"] = guid()), v));
15 | result = cloneDeep(values);
16 | onChange(name, result);
17 | };
18 |
19 | useEffect(() => {
20 | console.log("displayName", displayName);
21 | }, []);
22 |
23 | return (
24 | 0 ? value[0].type : undefined}
29 | onChange={onDrillChange}
30 | >
31 | {drillDownList.map((item) => {
32 | return (
33 |
34 | {item.name}
35 |
36 | );
37 | })}
38 |
39 | );
40 | };
41 |
42 | export default drillDownSelectHoc(Select);
43 |
--------------------------------------------------------------------------------
/src/packages/select/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Select } from "antd";
3 | import { isEmpty } from "@/utils/helper";
4 |
5 | const selectHoc = (SelectComponent) => (props) => {
6 | const { Option } = SelectComponent;
7 | const style = props.invalid ? { borderColor: "#f5222d" } : {};
8 | const { enum: enums, enumNames } = props.schema || {};
9 | const _values = isEmpty(props.value) ? undefined : props.value;
10 | const onChange = (value) => props.onChange(props.name, value ?? "");
11 |
12 | return (
13 |
20 | {(enums || []).map((val, index) => {
21 | let option = enumNames ? enumNames[index] : val;
22 | const isHtml = typeof option === "string" && option[0] === "<";
23 | if (isHtml) {
24 | option = ;
25 | }
26 | return (
27 |
30 | );
31 | })}
32 |
33 | );
34 | };
35 |
36 | export default selectHoc(Select);
37 |
--------------------------------------------------------------------------------
/src/packages/select/multi.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Select } from "antd";
3 |
4 | const multiSelectHoc = (SelectComponent) => (props) => {
5 | const { Option } = SelectComponent;
6 | const style = props.invalid ? { borderColor: "#f5222d" } : {};
7 | const { enum: enums, enumNames } = props.schema || {};
8 | const _value = props.value && Array.isArray(props.value) ? props.value : [];
9 | const onChange = (value) => props.onChange(props.name, value);
10 |
11 | return (
12 |
20 | {(enums || []).map((val, index) => (
21 |
29 | ))}
30 |
31 | );
32 | };
33 |
34 | export default multiSelectHoc(Select);
35 |
--------------------------------------------------------------------------------
/src/packages/slider/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { InputNumber, Slider } from "antd";
3 | import { isEmpty } from "@/utils/helper";
4 |
5 | const SliderWithNumber = (props) => {
6 | const style = props.invalid ? { borderColor: "#f5222d" } : {};
7 | const { max, min, step, disabled = false, readonly = false } = props.schema;
8 | let setting = {};
9 | if (max || max === 0) {
10 | setting = { max };
11 | }
12 |
13 | if (min || min === 0) {
14 | setting = { ...setting, min };
15 | }
16 |
17 | if (step) {
18 | setting = { ...setting, step };
19 | }
20 |
21 | let hideNumber = false;
22 | if (props.options && props.options.hideNumber) {
23 | hideNumber = true;
24 | }
25 |
26 | const renderNumber = readonly ? (
27 | {isEmpty(props.value) ? "-" : props.value}
28 | ) : (
29 |
38 | );
39 |
40 | const onChange = (value) => {
41 | props.onChange(props.name, value);
42 | };
43 |
44 | return (
45 |
46 |
53 | {hideNumber ? null : renderNumber}
54 |
55 | );
56 | };
57 |
58 | export default SliderWithNumber;
59 |
--------------------------------------------------------------------------------
/src/packages/switch/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Switch } from "antd";
3 |
4 | const VSwitch = (p) => {
5 | return (
6 | p.onChange(p.name, checked)}
11 | />
12 | );
13 | };
14 |
15 | export default VSwitch;
16 |
--------------------------------------------------------------------------------
/src/packages/textarea/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Input } from "antd";
3 |
4 | const { TextArea } = Input;
5 |
6 | const VTextArea = (p) => {
7 | const { options, invalid, schema = {} } = p;
8 | const { maxLength } = schema;
9 | const style = invalid ? { borderColor: "#ff4d4f", boxShadow: "0 0 0 2px rgba(255,77,79,.2)" } : {};
10 | const defaultUi = { rows: 3 };
11 | const ui = { ...defaultUi, ...options };
12 | const onChange = (e) => p.onChange(p.name, e.target.value);
13 |
14 | const _value = p.value || "";
15 | return (
16 |
17 |
18 | {maxLength ? (
19 | maxLength ? "#ff4d4f" : "#999"
26 | }}
27 | >
28 | {_value.length + " / " + maxLength}
29 |
30 | ) : null}
31 |
32 | );
33 | };
34 |
35 | export default VTextArea;
36 |
--------------------------------------------------------------------------------
/src/packages/upload/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { Upload, message, Button } from "antd";
3 | import { UploadOutlined } from "@ant-design/icons";
4 | import { isEmpty, getBase64 } from "@/utils/helper";
5 |
6 | const VUpload = (p) => {
7 | const [fileList, setFileList] = useState([]);
8 | // 获取到文件名
9 | const fileName = (name) => {
10 | let pos = name.lastIndexOf("/");
11 | return name.substring(pos + 1);
12 | };
13 |
14 | useEffect(() => {
15 | // 实际上传地址
16 | if (isEmpty(p.value)) return;
17 | setFileList([
18 | {
19 | uid: "1",
20 | status: "done",
21 | name: fileName(p.value),
22 | url: p.value
23 | }
24 | ]);
25 | }, []);
26 |
27 | const props = {
28 | name: "file",
29 | action: p.action,
30 | enctype: "multipart/form-data",
31 | withCredentials: true,
32 | listType: "picture",
33 | type: "file",
34 | fileList: fileList,
35 | onChange: async (info) => {
36 | // 转化为base64格式数据,真实地址为info.file.response.url
37 | if (!info.file.url) {
38 | info.file.url = await getBase64(info.file.originFileObj);
39 | }
40 | if (info.file.status === "done") {
41 | message.success(`${info.file.name} 文件上传成功`);
42 | setFileList([
43 | {
44 | uid: "1",
45 | status: "done",
46 | name: info.file.name,
47 | url: info.file.url
48 | }
49 | ]);
50 | p.onChange(p.name, info.file.url);
51 | } else if (info.file.status === "error") {
52 | message.error(`${info.file.name} 文件上传失败`);
53 | setFileList([]);
54 | p.onChange(p.name, "");
55 | }
56 | },
57 | onRemove() {
58 | setFileList([]);
59 | p.onChange(p.name, "");
60 | }
61 | };
62 |
63 | return (
64 |
65 |
66 |
69 |
70 |
71 | );
72 | };
73 |
74 | export default VUpload;
75 |
--------------------------------------------------------------------------------
/src/packages/vcharts/index.js:
--------------------------------------------------------------------------------
1 | export { default as Vbar } from "./src/bar";
2 |
3 | export { default as Vline } from "./src/line";
4 |
5 | export { default as Vpie } from "./src/pie";
6 |
7 | export { default as Vmap } from "./src/map";
8 |
9 | export { default as VmapCity } from "./src/mapcity";
10 |
11 | export { default as Vwordcloud } from "./src/wordcloud";
12 |
--------------------------------------------------------------------------------
/src/packages/vcharts/options/barpolar.js:
--------------------------------------------------------------------------------
1 | import { DEFAULT_COLORS } from "~packages/constants";
2 |
3 | export function getOption(option, data) {
4 | const { categories = [], series = [] } = data;
5 | const {
6 | pointerLineColor = "#19d4ae",
7 | pointerLine = true,
8 | angleAxis = false,
9 | splitLine = false,
10 | barWidth = "auto",
11 | stack = false,
12 | unit = ""
13 | } = option;
14 |
15 | return {
16 | color: DEFAULT_COLORS,
17 | grid: {
18 | top: 20,
19 | left: 15,
20 | right: 20,
21 | bottom: 30,
22 | containLabel: true
23 | },
24 | tooltip: {
25 | trigger: "axis",
26 | axisPointer: {
27 | type: pointerLine ? "line" : "none",
28 | lineStyle: {
29 | color: pointerLineColor
30 | }
31 | },
32 | axisTick: {
33 | show: false,
34 | alignWithLabel: true
35 | },
36 | formatter: function (params) {
37 | return [params[0].marker + params[0].name + ":" + (params[0].data || 0) + unit].join("");
38 | }
39 | },
40 | angleAxis: {
41 | show: angleAxis
42 | },
43 | polar: {
44 | center: ["50%", "50%"]
45 | },
46 | radiusAxis: {
47 | type: "category",
48 | data: categories,
49 | z: 1,
50 | splitLine: {
51 | show: splitLine,
52 | lineStyle: {
53 | color: "#19d4ae",
54 | width: 1,
55 | type: "solid"
56 | }
57 | }
58 | },
59 | series: series.map((item) => {
60 | return {
61 | type: "bar",
62 | barWidth: barWidth,
63 | coordinateSystem: "polar",
64 | stack: stack ? "something" : "",
65 | data: item.data
66 | };
67 | })
68 | };
69 | }
70 |
--------------------------------------------------------------------------------
/src/packages/vcharts/options/funnel.js:
--------------------------------------------------------------------------------
1 | import { DEFAULT_COLORS } from "~packages/constants";
2 |
3 | export function getOption(option, data) {
4 | const { series = [] } = data;
5 | const { sortType = "descending", funnelWidth = "100%", unit = "" } = option;
6 |
7 | return {
8 | color: DEFAULT_COLORS,
9 | tooltip: {
10 | trigger: "item",
11 | formatter: function (params) {
12 | return [params.marker + params.name + " " + (params.value || 0) + unit + "(" + params.percent + "%" + ")"].join(
13 | ""
14 | );
15 | }
16 | },
17 | series: [
18 | {
19 | type: "funnel",
20 | x: 0,
21 | y: 60,
22 | y2: 60,
23 | top: 15,
24 | bottom: 20,
25 | width: "100%",
26 | min: 0,
27 | max: 100,
28 | minSize: "0%",
29 | maxSize: funnelWidth,
30 | sort: sortType,
31 | gap: 0,
32 | data: series,
33 | roseType: true,
34 | label: {
35 | normal: {
36 | formatter: function (params) {
37 | return params.name + ": " + params.value;
38 | },
39 | position: "center"
40 | }
41 | }
42 | }
43 | ]
44 | };
45 | }
46 |
--------------------------------------------------------------------------------
/src/packages/vcharts/options/gauge.js:
--------------------------------------------------------------------------------
1 | import echarts from "echarts";
2 |
3 | export function getOption(option, data) {
4 | const { series } = data;
5 | const {
6 | titleColor = "#ffffff",
7 | detailColor = "#ffffff",
8 | axisLabelColor = "#ffffff",
9 | axisLabelShow = true,
10 | titleFontSize = 12,
11 | axisLabelFont = 12,
12 | indexFontSize = 12
13 | } = option;
14 |
15 | const colorStyles = [
16 | [
17 | 1,
18 | new echarts.graphic.LinearGradient(0, 0, 1, 0, [
19 | {
20 | offset: 0,
21 | color: "#5CF9FE"
22 | },
23 | {
24 | offset: 0.17,
25 | color: "#468EFD"
26 | },
27 | {
28 | offset: 0.9,
29 | color: "#468EFD"
30 | },
31 | {
32 | offset: 1,
33 | color: "#5CF9FE"
34 | }
35 | ])
36 | ]
37 | ];
38 |
39 | return {
40 | color: DEFAULT_COLORS,
41 | tooltip: {
42 | formatter: "{a}
{b} : {c}%"
43 | },
44 | series: [
45 | {
46 | type: "gauge",
47 | radius: "100%",
48 | min: series.min || 0,
49 | max: series.max || 100,
50 | title: {
51 | color: titleColor,
52 | fontSize: titleFontSize,
53 | fontStyle: "italic"
54 | },
55 | axisLabel: {
56 | show: axisLabelShow,
57 | color: axisLabelColor,
58 | fontSize: axisLabelFont
59 | },
60 | axisLine: {
61 | show: true,
62 | lineStyle: {
63 | color: colorStyles,
64 | width: 25,
65 | shadowOffsetX: 0,
66 | shadowOffsetY: 0,
67 | opacity: 1
68 | }
69 | },
70 | detail: {
71 | fontSize: indexFontSize,
72 | color: detailColor,
73 | formatter: "{value}"
74 | },
75 | data: [{ ...series }]
76 | }
77 | ]
78 | };
79 | }
80 |
--------------------------------------------------------------------------------
/src/packages/vcharts/options/heatmap.js:
--------------------------------------------------------------------------------
1 | import { DEFAULT_COLORS } from "~packages/constants";
2 |
3 | /**
4 | * 将数字取整为10的倍数
5 | * @param {Number} num 需要取整的值
6 | * @param {Boolean} ceil 是否向上取整
7 | * @param {Number} prec 需要用0占位的数量
8 | */
9 | function formatInt(num, prec = 1, ceil = true) {
10 | const len = String(num).length;
11 | if (len <= prec) {
12 | return num;
13 | }
14 |
15 | const mult = Math.pow(10, prec);
16 | return ceil ? Math.ceil(num / mult) * mult : Math.floor(num / mult) * mult;
17 | }
18 |
19 | export function getOption(option, data) {
20 | const { series } = data;
21 | let seriesData = [],
22 | max,
23 | min;
24 | if (series.data) {
25 | let maxData = [];
26 | series.data.forEach((e, index) => {
27 | maxData = maxData.concat(series.data[index][2]);
28 | });
29 | let newData = Array.from(new Set(maxData));
30 | max = Math.max(...newData);
31 | min = Math.min(...newData);
32 | max = formatInt(max);
33 | seriesData = series.data.map(function (item) {
34 | return [item[1], item[0], item[2] || "-"];
35 | });
36 | }
37 | return {
38 | tooltip: {
39 | trigger: "item"
40 | },
41 | grid: {
42 | top: 20,
43 | left: 15,
44 | right: 20,
45 | bottom: 30,
46 | containLabel: true
47 | },
48 | animation: false,
49 | xAxis: {
50 | type: "category",
51 | data: series.hours,
52 | splitArea: {
53 | show: true
54 | }
55 | },
56 | yAxis: {
57 | type: "category",
58 | data: series.days,
59 | splitArea: {
60 | show: true
61 | }
62 | },
63 | visualMap: {
64 | min: min ? min : 0,
65 | max: max ? max : 10,
66 | type: "piecewise",
67 | orient: "horizontal",
68 | inRange: {
69 | color: DEFAULT_COLORS
70 | },
71 | left: "center",
72 | bottom: 0
73 | },
74 | series: [
75 | {
76 | type: "heatmap",
77 | data: seriesData,
78 | label: {
79 | show: true
80 | }
81 | }
82 | ]
83 | };
84 | }
85 |
--------------------------------------------------------------------------------
/src/packages/vcharts/options/liquidfill.js:
--------------------------------------------------------------------------------
1 | import { DEFAULT_COLORS } from "~packages/constants";
2 |
3 | export function getOption(option, data) {
4 | const { series } = data;
5 | const {
6 | numberFill = 4,
7 | waveAnimation,
8 | backgroundColor,
9 | shape,
10 | borderHied,
11 | borderPadding,
12 | borderColor,
13 | borderWidth,
14 | colorRipple,
15 | sumfontSize,
16 | unit = ""
17 | } = option;
18 |
19 | let seriesData = [];
20 |
21 | for (let i = 0; i < numberFill; i++) {
22 | if (1 - 0.15 * i > 0) {
23 | let o = i === 0 ? 1 : 1 - 0.15 * i;
24 | seriesData.push(series * o);
25 | }
26 | }
27 |
28 | return {
29 | series: [
30 | {
31 | type: "liquidFill",
32 | radius: "85%",
33 | center: ["50%", "50%"],
34 | waveAnimation: waveAnimation,
35 | backgroundStyle: {
36 | color: backgroundColor
37 | },
38 | data: seriesData,
39 | shape: shape,
40 | outline: {
41 | show: borderHied,
42 | borderDistance: borderPadding,
43 | itemStyle: {
44 | borderColor: borderColor,
45 | borderWidth: borderWidth,
46 | shadowBlur: "none"
47 | }
48 | },
49 | color: colorRipple ? [colorRipple] : DEFAULT_COLORS,
50 | label: {
51 | normal: {
52 | textStyle: {
53 | fontSize: sumfontSize
54 | }
55 | }
56 | }
57 | }
58 | ]
59 | };
60 | }
61 |
--------------------------------------------------------------------------------
/src/packages/vcharts/options/radar.js:
--------------------------------------------------------------------------------
1 | import { DEFAULT_COLORS } from "~packages/constants";
2 |
3 | export function getOption(option, data) {
4 | const { indicators, series } = data;
5 | const { stack = false, legendShow = false } = option;
6 |
7 | return {
8 | color: DEFAULT_COLORS,
9 | tooltip: {
10 | trigger: "item"
11 | },
12 | legend: {
13 | show: legendShow,
14 | type: "scroll",
15 | bottom: 15,
16 | data: series.map((item) => item.name)
17 | },
18 | radar: {
19 | radius: "60%",
20 | indicator: indicators
21 | },
22 | series: [
23 | {
24 | type: "radar",
25 | areaStyle: stack
26 | ? {
27 | normal: {
28 | shadowBlur: 13,
29 | shadowColor: "rgba(0,0,0,.2)",
30 | shadowOffsetX: 0,
31 | shadowOffsetY: 10,
32 | opacity: 1
33 | }
34 | }
35 | : undefined,
36 | data: series
37 | }
38 | ]
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/src/packages/vcharts/options/sankey.js:
--------------------------------------------------------------------------------
1 | import { DEFAULT_COLORS } from "~packages/constants";
2 |
3 | export function getOption(option, data) {
4 | const { series } = data;
5 | const { sortType, textColor = "#19d4ae", textSize = 14, curveness, unit = "" } = option;
6 |
7 | return {
8 | color: DEFAULT_COLORS,
9 | tooltip: {
10 | trigger: "item",
11 | formatter: function (params) {
12 | if (params.data.name) {
13 | return [params.marker + "节点:" + params.data.name + "(" + (params.data.value || 0) + unit + ")"].join("");
14 | } else {
15 | return [params.data.source + " vs " + params.data.target + "(" + (params.data.value || 0) + unit + ")"].join(
16 | ""
17 | );
18 | }
19 | }
20 | },
21 | series: [
22 | {
23 | type: "sankey",
24 | bottom: "10%",
25 | data: series.data,
26 | links: series.links,
27 | orient: sortType,
28 | label: {
29 | color: textColor,
30 | fontSize: textSize
31 | },
32 | lineStyle: {
33 | color: "source",
34 | curveness: curveness * 0.1
35 | }
36 | }
37 | ]
38 | };
39 | }
40 |
--------------------------------------------------------------------------------
/src/packages/vcharts/options/scatter.js:
--------------------------------------------------------------------------------
1 | import { DEFAULT_COLORS } from "~packages/constants";
2 |
3 | export function getOption(option, data) {
4 | const { series } = data;
5 | const { symbol = "circle", unit = "" } = option;
6 |
7 | return {
8 | color: DEFAULT_COLORS,
9 | grid: {
10 | top: 20,
11 | left: 15,
12 | right: 20,
13 | bottom: 30,
14 | containLabel: true
15 | },
16 | tooltip: {
17 | position: "top",
18 | formatter: function (params) {
19 | return [params.marker + params.seriesName + ":" + (params.value || 0) + unit].join("");
20 | }
21 | },
22 | legend: {
23 | show: true,
24 | bottom: 0,
25 | data: series.map((item) => item.name)
26 | },
27 | xAxis: {
28 | splitLine: {
29 | lineStyle: {
30 | type: "dashed"
31 | }
32 | }
33 | },
34 | yAxis: {
35 | splitLine: {
36 | lineStyle: {
37 | type: "dashed"
38 | }
39 | },
40 | scale: true
41 | },
42 | series: series.map((item, index) => {
43 | return {
44 | name: item.name,
45 | type: "scatter",
46 | symbolSize: function (data) {
47 | return data / 4e2;
48 | },
49 | animationDelay: function (idx) {
50 | return idx * 5;
51 | },
52 | symbol: symbol,
53 | data: item.data[index].data
54 | };
55 | })
56 | };
57 | }
58 |
--------------------------------------------------------------------------------
/src/packages/vcharts/options/treemap.js:
--------------------------------------------------------------------------------
1 | import { DEFAULT_COLORS } from "~packages/constants";
2 |
3 | export function getOption(option, data) {
4 | const { series } = data;
5 | const {
6 | borderWidth = 1,
7 | borderColor = "#fff",
8 | hoverTextColor = "rgba(92,121,255,0.8)",
9 | hoverBackColor = "rgba(255,255,255,0.8)",
10 | textColor = "#fff",
11 | textSize = 14,
12 | unit = ""
13 | } = option;
14 |
15 | return {
16 | color: DEFAULT_COLORS,
17 | grid: {
18 | top: 30,
19 | left: 15,
20 | right: 20,
21 | bottom: 30,
22 | containLabel: true
23 | },
24 | tooltip: {
25 | formatter: function (params) {
26 | return [params.marker + params.data.name + ":" + (params.data.value || 0) + unit].join("");
27 | }
28 | },
29 | series: [
30 | {
31 | type: "treemap",
32 | left: "center",
33 | width: "90%",
34 | height: "90%",
35 | breadcrumb: {
36 | show: false
37 | },
38 | itemStyle: {
39 | normal: {
40 | label: {
41 | show: true,
42 | formatter: "{b}"
43 | },
44 | borderWidth: borderWidth,
45 | borderColor: borderColor,
46 | strokeWidth: 2,
47 | strokeColor: "rgba(57, 111, 255)"
48 | },
49 | emphasis: {
50 | label: {
51 | show: true,
52 | color: hoverTextColor
53 | },
54 | color: hoverBackColor
55 | }
56 | },
57 | label: {
58 | normal: {
59 | color: textColor,
60 | fontSize: textSize
61 | }
62 | },
63 | data: series
64 | }
65 | ]
66 | };
67 | }
68 |
--------------------------------------------------------------------------------
/src/packages/vcharts/options/wordcloud.js:
--------------------------------------------------------------------------------
1 | export function getOption(option, data) {
2 | const { series = [] } = data;
3 | const { minFontSize, maxFontSize, rotationStep, gridSize, shape, unit = "" } = option;
4 |
5 | return {
6 | tooltip: {
7 | formatter: function (params) {
8 | return [params.marker + params.data.name + ":" + (params.data.value || 0) + unit].join("");
9 | }
10 | },
11 | series: [
12 | {
13 | type: "wordCloud",
14 | gridSize: gridSize,
15 | shape: shape,
16 | sizeRange: [minFontSize, maxFontSize],
17 | rotationRange: [-45, 90],
18 | rotationStep: rotationStep ? 45 : 180,
19 | textRotation: [0, 45, 90, -45],
20 | left: "center",
21 | top: "center",
22 | right: null,
23 | bottom: null,
24 | width: "90%",
25 | height: "80%",
26 | drawOutOfBound: true,
27 | textStyle: {
28 | normal: {
29 | color: function () {
30 | return (
31 | "rgb(" +
32 | [
33 | Math.round(Math.random() * 200 + 55),
34 | Math.round(Math.random() * 200 + 55),
35 | Math.round(Math.random() * 200 + 55)
36 | ].join(",") +
37 | ")"
38 | );
39 | }
40 | },
41 | emphasis: {
42 | shadowBlur: 10,
43 | shadowColor: "#2ac"
44 | }
45 | },
46 | data: series
47 | }
48 | ]
49 | };
50 | }
51 |
--------------------------------------------------------------------------------
/src/packages/vcharts/src/funnel.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { Vcharts } from "~components";
4 | import SubDialog from "../../dialog";
5 | import { getOption } from "../options/funnel";
6 |
7 | const GeneratorFunnel = ({ value, options, onChange, mode, dirlldownQuery, dispatch }) => {
8 | const [dataSource, setDataSource] = useState({});
9 | const [stauts, setStauts] = useState(false);
10 | const [visible, setVisible] = useState(false);
11 | const { dataConfig, isRefresh, drillDownOpen } = value;
12 |
13 | useEffect(() => {
14 | setDataSource(dataConfig.data);
15 | }, [dataConfig.data]);
16 |
17 | useEffect(() => {
18 | setStauts(drillDownOpen);
19 | }, [drillDownOpen]);
20 |
21 | const subDialogTpl = useMemo(() => {
22 | return visible && value.drillDown && value.drillDown.length > 0 ? (
23 | {
28 | e.stopPropagation();
29 | // TODO: 解锁图层
30 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: false }, 0);
31 | // TODO: 下钻过滤参数条件
32 | dirlldownQuery.pop();
33 | dispatch({
34 | type: "SET_DIRLLDOWN_QUERY",
35 | data: dirlldownQuery
36 | });
37 |
38 | setVisible((o) => !o);
39 | }}
40 | />
41 | ) : null;
42 | }, [visible, onChange]);
43 |
44 | if (drillDownOpen) {
45 | return (
46 | <>
47 | {subDialogTpl}
48 | {
55 | if (value.drillDown.length === 0) return;
56 | // TODO: 锁住图层
57 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: true }, 0);
58 | let dirlldowns = dirlldownQuery.concat([
59 | {
60 | fireKey: "",
61 | item: {
62 | name: param.name,
63 | value: param.value,
64 | category: param.seriesName,
65 | _default: param.name
66 | }
67 | }
68 | ]);
69 | dispatch({
70 | type: "SET_DIRLLDOWN_QUERY",
71 | data: dirlldowns
72 | });
73 |
74 | setVisible(true);
75 | }
76 | }}
77 | />
78 | >
79 | );
80 | }
81 |
82 | return (
83 |
89 | );
90 | };
91 |
92 | export default connect((state) => ({
93 | mode: state.component.mode,
94 | dirlldownQuery: state.component.dirlldownQuery
95 | }))(GeneratorFunnel);
96 |
--------------------------------------------------------------------------------
/src/packages/vcharts/src/gauge.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { Vcharts } from "~components";
4 | import SubDialog from "../../dialog";
5 | import { getOption } from "../options/gauge";
6 |
7 | const GeneratorGaugel = ({ value, options, onChange, mode, dirlldownQuery, dispatch }) => {
8 | const [dataSource, setDataSource] = useState({});
9 | const [stauts, setStauts] = useState(false);
10 | const [visible, setVisible] = useState(false);
11 | const { dataConfig, isRefresh, drillDownOpen } = value;
12 |
13 | useEffect(() => {
14 | setDataSource(dataConfig.data);
15 | }, [dataConfig.data]);
16 |
17 | useEffect(() => {
18 | setStauts(drillDownOpen);
19 | }, [drillDownOpen]);
20 |
21 | const subDialogTpl = useMemo(() => {
22 | return visible && value.drillDown && value.drillDown.length > 0 ? (
23 | {
28 | e.stopPropagation();
29 | // TODO: 解锁图层
30 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: false }, 0);
31 | // TODO: 下钻过滤参数条件
32 | dirlldownQuery.pop();
33 | dispatch({
34 | type: "SET_DIRLLDOWN_QUERY",
35 | data: dirlldownQuery
36 | });
37 |
38 | setVisible((o) => !o);
39 | }}
40 | />
41 | ) : null;
42 | }, [visible, onChange]);
43 |
44 | if (drillDownOpen) {
45 | return (
46 | <>
47 | {subDialogTpl}
48 | {
55 | if (value.drillDown.length === 0) return;
56 | // TODO: 锁住图层
57 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: true }, 0);
58 | let dirlldowns = dirlldownQuery.concat([
59 | {
60 | fireKey: "",
61 | item: {
62 | name: param.name,
63 | value: param.value,
64 | category: param.seriesName,
65 | _default: param.name
66 | }
67 | }
68 | ]);
69 | dispatch({
70 | type: "SET_DIRLLDOWN_QUERY",
71 | data: dirlldowns
72 | });
73 |
74 | setVisible(true);
75 | }
76 | }}
77 | />
78 | >
79 | );
80 | }
81 |
82 | return (
83 |
89 | );
90 | };
91 |
92 | export default connect((state) => ({
93 | mode: state.component.mode,
94 | dirlldownQuery: state.component.dirlldownQuery
95 | }))(GeneratorGaugel);
96 |
--------------------------------------------------------------------------------
/src/packages/vcharts/src/heatmap.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { Vcharts } from "~components";
4 | import SubDialog from "../../dialog";
5 | import { getOption } from "../options/heatmap";
6 |
7 | const GeneratorHeatmap = ({ value, options, onChange, mode, dirlldownQuery, dispatch }) => {
8 | const [dataSource, setDataSource] = useState({});
9 | const [stauts, setStauts] = useState(false);
10 | const [visible, setVisible] = useState(false);
11 | const { dataConfig, isRefresh, drillDownOpen } = value;
12 |
13 | useEffect(() => {
14 | setDataSource(dataConfig.data);
15 | }, [dataConfig.data]);
16 |
17 | useEffect(() => {
18 | setStauts(drillDownOpen);
19 | }, [drillDownOpen]);
20 |
21 | const subDialogTpl = useMemo(() => {
22 | return visible && value.drillDown && value.drillDown.length > 0 ? (
23 | {
28 | e.stopPropagation();
29 | // TODO: 解锁图层
30 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: false }, 0);
31 | // TODO: 下钻过滤参数条件
32 | dirlldownQuery.pop();
33 | dispatch({
34 | type: "SET_DIRLLDOWN_QUERY",
35 | data: dirlldownQuery
36 | });
37 |
38 | setVisible((o) => !o);
39 | }}
40 | />
41 | ) : null;
42 | }, [visible, onChange]);
43 |
44 | if (drillDownOpen) {
45 | return (
46 | <>
47 | {subDialogTpl}
48 | {
55 | if (value.drillDown.length === 0) return;
56 | // TODO: 锁住图层
57 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: true }, 0);
58 | let dirlldowns = dirlldownQuery.concat([
59 | {
60 | fireKey: "",
61 | item: {
62 | name: param.name,
63 | value: param.value,
64 | category: param.seriesName,
65 | _default: param.name
66 | }
67 | }
68 | ]);
69 | dispatch({
70 | type: "SET_DIRLLDOWN_QUERY",
71 | data: dirlldowns
72 | });
73 |
74 | setVisible(true);
75 | }
76 | }}
77 | />
78 | >
79 | );
80 | }
81 |
82 | return (
83 |
89 | );
90 | };
91 |
92 | export default connect((state) => ({
93 | mode: state.component.mode,
94 | dirlldownQuery: state.component.dirlldownQuery
95 | }))(GeneratorHeatmap);
96 |
--------------------------------------------------------------------------------
/src/packages/vcharts/src/line.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { Vcharts } from "~components";
4 | import SubDialog from "../../dialog";
5 | import { getOption } from "../options/line";
6 |
7 | const GeneratorLine = ({ value, options, onChange, mode, dirlldownQuery, dispatch }) => {
8 | const [dataSource, setDataSource] = useState({});
9 | const [stauts, setStauts] = useState(false);
10 | const [visible, setVisible] = useState(false);
11 | const { dataConfig, isRefresh, drillDownOpen } = value;
12 |
13 | useEffect(() => {
14 | setDataSource(dataConfig.data);
15 | }, [dataConfig.data]);
16 |
17 | useEffect(() => {
18 | setStauts(drillDownOpen);
19 | }, [drillDownOpen]);
20 |
21 | const subDialogTpl = useMemo(() => {
22 | return visible && value.drillDown && value.drillDown.length > 0 ? (
23 | {
28 | e.stopPropagation();
29 | // TODO: 解锁图层
30 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: false }, 0);
31 | // TODO: 下钻过滤参数条件
32 | dirlldownQuery.pop();
33 | dispatch({
34 | type: "SET_DIRLLDOWN_QUERY",
35 | data: dirlldownQuery
36 | });
37 |
38 | setVisible((o) => !o);
39 | }}
40 | />
41 | ) : null;
42 | }, [visible, onChange]);
43 |
44 | if (drillDownOpen) {
45 | return (
46 | <>
47 | {subDialogTpl}
48 | {
55 | if (value.drillDown.length === 0) return;
56 | // TODO: 锁住图层
57 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: true }, 0);
58 | let dirlldowns = dirlldownQuery.concat([
59 | {
60 | fireKey: "",
61 | item: {
62 | name: param.name,
63 | value: param.value,
64 | category: param.seriesName,
65 | _default: param.name
66 | }
67 | }
68 | ]);
69 | dispatch({
70 | type: "SET_DIRLLDOWN_QUERY",
71 | data: dirlldowns
72 | });
73 |
74 | setVisible(true);
75 | }
76 | }}
77 | />
78 | >
79 | );
80 | }
81 |
82 | return (
83 |
89 | );
90 | };
91 |
92 | export default connect((state) => ({
93 | mode: state.component.mode,
94 | dirlldownQuery: state.component.dirlldownQuery
95 | }))(GeneratorLine);
96 |
--------------------------------------------------------------------------------
/src/packages/vcharts/src/map.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { Vcharts } from "~components";
4 | import SubDialog from "../../dialog";
5 | import { getOption } from "../options/map";
6 |
7 | const GeneratorMap = ({ value, options, onChange, mode, dirlldownQuery, dispatch }) => {
8 | const [dataSource, setDataSource] = useState({});
9 | const [stauts, setStauts] = useState(false);
10 | const [visible, setVisible] = useState(false);
11 | const { dataConfig, isRefresh, drillDownOpen } = value;
12 |
13 | useEffect(() => {
14 | setDataSource(dataConfig.data);
15 | }, [dataConfig.data]);
16 |
17 | useEffect(() => {
18 | setStauts(drillDownOpen);
19 | }, [drillDownOpen]);
20 |
21 | const subDialogTpl = useMemo(() => {
22 | return visible && value.drillDown && value.drillDown.length > 0 ? (
23 | {
28 | e.stopPropagation();
29 | // TODO: 解锁图层
30 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: false }, 0);
31 | // TODO: 下钻过滤参数条件
32 | dirlldownQuery.pop();
33 | dispatch({
34 | type: "SET_DIRLLDOWN_QUERY",
35 | data: dirlldownQuery
36 | });
37 |
38 | setVisible((o) => !o);
39 | }}
40 | />
41 | ) : null;
42 | }, [visible, onChange]);
43 |
44 | if (drillDownOpen) {
45 | return (
46 | <>
47 | {subDialogTpl}
48 | {
54 | if (value.drillDown.length === 0) return;
55 | // TODO: 锁住图层
56 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: true }, 0);
57 | let dirlldowns = dirlldownQuery.concat([
58 | {
59 | fireKey: "",
60 | item: {
61 | name: param.name,
62 | value: param.value,
63 | category: param.seriesName,
64 | _default: param.name
65 | }
66 | }
67 | ]);
68 | dispatch({
69 | type: "SET_DIRLLDOWN_QUERY",
70 | data: dirlldowns
71 | });
72 |
73 | setVisible(true);
74 | }
75 | }}
76 | />
77 | >
78 | );
79 | }
80 |
81 | return (
82 |
83 | );
84 | };
85 |
86 | export default connect((state) => ({
87 | mode: state.component.mode,
88 | dirlldownQuery: state.component.dirlldownQuery
89 | }))(GeneratorMap);
90 |
--------------------------------------------------------------------------------
/src/packages/vcharts/src/mapcity.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { Vcharts } from "~components";
4 | import SubDialog from "../../dialog";
5 | import { getOption } from "../options/mapcity";
6 |
7 | const GeneratorMapcity = ({ value, options, onChange, mode, dirlldownQuery, dispatch }) => {
8 | const [dataSource, setDataSource] = useState({});
9 | const [stauts, setStauts] = useState(false);
10 | const [visible, setVisible] = useState(false);
11 | const { dataConfig, isRefresh, drillDownOpen } = value;
12 |
13 | useEffect(() => {
14 | setDataSource(dataConfig.data);
15 | }, [dataConfig.data]);
16 |
17 | useEffect(() => {
18 | setStauts(drillDownOpen);
19 | }, [drillDownOpen]);
20 |
21 | const subDialogTpl = useMemo(() => {
22 | return visible && value.drillDown && value.drillDown.length > 0 ? (
23 | {
28 | e.stopPropagation();
29 | // TODO: 解锁图层
30 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: false }, 0);
31 | // TODO: 下钻过滤参数条件
32 | dirlldownQuery.pop();
33 | dispatch({
34 | type: "SET_DIRLLDOWN_QUERY",
35 | data: dirlldownQuery
36 | });
37 |
38 | setVisible((o) => !o);
39 | }}
40 | />
41 | ) : null;
42 | }, [visible, onChange]);
43 |
44 | if (drillDownOpen) {
45 | return (
46 | <>
47 | {subDialogTpl}
48 | {
54 | if (value.drillDown.length === 0) return;
55 | // TODO: 锁住图层
56 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: true }, 0);
57 | let dirlldowns = dirlldownQuery.concat([
58 | {
59 | fireKey: "",
60 | item: {
61 | name: param.name,
62 | value: param.value,
63 | category: param.seriesName,
64 | _default: param.name
65 | }
66 | }
67 | ]);
68 | dispatch({
69 | type: "SET_DIRLLDOWN_QUERY",
70 | data: dirlldowns
71 | });
72 |
73 | setVisible(true);
74 | }
75 | }}
76 | />
77 | >
78 | );
79 | }
80 |
81 | return (
82 |
83 | );
84 | };
85 |
86 | export default connect((state) => ({
87 | mode: state.component.mode,
88 | dirlldownQuery: state.component.dirlldownQuery
89 | }))(GeneratorMapcity);
90 |
--------------------------------------------------------------------------------
/src/packages/vcharts/src/radar.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { Vcharts } from "~components";
4 | import SubDialog from "../../dialog";
5 | import { getOption } from "../options/radar";
6 |
7 | const GeneratorRadar = ({ value, options, onChange, mode, dirlldownQuery, dispatch }) => {
8 | const [dataSource, setDataSource] = useState({});
9 | const [stauts, setStauts] = useState(false);
10 | const [visible, setVisible] = useState(false);
11 | const { dataConfig, isRefresh, drillDownOpen } = value;
12 |
13 | useEffect(() => {
14 | setDataSource(dataConfig.data);
15 | }, [dataConfig.data]);
16 |
17 | useEffect(() => {
18 | setStauts(drillDownOpen);
19 | }, [drillDownOpen]);
20 |
21 | const subDialogTpl = useMemo(() => {
22 | return visible && value.drillDown && value.drillDown.length > 0 ? (
23 | {
28 | e.stopPropagation();
29 | // TODO: 解锁图层
30 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: false }, 0);
31 | // TODO: 下钻过滤参数条件
32 | dirlldownQuery.pop();
33 | dispatch({
34 | type: "SET_DIRLLDOWN_QUERY",
35 | data: dirlldownQuery
36 | });
37 |
38 | setVisible((o) => !o);
39 | }}
40 | />
41 | ) : null;
42 | }, [visible, onChange]);
43 |
44 | if (drillDownOpen) {
45 | return (
46 | <>
47 | {subDialogTpl}
48 | {
55 | if (value.drillDown.length === 0) return;
56 | // TODO: 锁住图层
57 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: true }, 0);
58 | let dirlldowns = dirlldownQuery.concat([
59 | {
60 | fireKey: "",
61 | item: {
62 | name: param.name,
63 | value: param.value,
64 | category: param.seriesName,
65 | _default: param.name
66 | }
67 | }
68 | ]);
69 | dispatch({
70 | type: "SET_DIRLLDOWN_QUERY",
71 | data: dirlldowns
72 | });
73 |
74 | setVisible(true);
75 | }
76 | }}
77 | />
78 | >
79 | );
80 | }
81 |
82 | return (
83 |
89 | );
90 | };
91 |
92 | export default connect((state) => ({
93 | mode: state.component.mode,
94 | dirlldownQuery: state.component.dirlldownQuery
95 | }))(GeneratorRadar);
96 |
--------------------------------------------------------------------------------
/src/packages/vcharts/src/sankey.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { Vcharts } from "~components";
4 | import SubDialog from "../../dialog";
5 | import { getOption } from "../options/sankey";
6 |
7 | const GeneratorSankey = ({ value, options, onChange, mode, dirlldownQuery, dispatch }) => {
8 | const [dataSource, setDataSource] = useState({});
9 | const [stauts, setStauts] = useState(false);
10 | const [visible, setVisible] = useState(false);
11 | const { dataConfig, isRefresh, drillDownOpen } = value;
12 |
13 | useEffect(() => {
14 | setDataSource(dataConfig.data);
15 | }, [dataConfig.data]);
16 |
17 | useEffect(() => {
18 | setStauts(drillDownOpen);
19 | }, [drillDownOpen]);
20 |
21 | const subDialogTpl = useMemo(() => {
22 | return visible && value.drillDown && value.drillDown.length > 0 ? (
23 | {
28 | e.stopPropagation();
29 | // TODO: 解锁图层
30 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: false }, 0);
31 | // TODO: 下钻过滤参数条件
32 | dirlldownQuery.pop();
33 | dispatch({
34 | type: "SET_DIRLLDOWN_QUERY",
35 | data: dirlldownQuery
36 | });
37 |
38 | setVisible((o) => !o);
39 | }}
40 | />
41 | ) : null;
42 | }, [visible, onChange]);
43 |
44 | if (drillDownOpen) {
45 | return (
46 | <>
47 | {subDialogTpl}
48 | {
55 | if (value.drillDown.length === 0) return;
56 | // TODO: 锁住图层
57 | mode === "development" && value.drillDown[0].drillDownLevel === 0 && onChange({ isLock: true }, 0);
58 | let dirlldowns = dirlldownQuery.concat([
59 | {
60 | fireKey: "",
61 | item: {
62 | name: param.name,
63 | value: param.value,
64 | category: param.seriesName,
65 | _default: param.name
66 | }
67 | }
68 | ]);
69 | dispatch({
70 | type: "SET_DIRLLDOWN_QUERY",
71 | data: dirlldowns
72 | });
73 |
74 | setVisible(true);
75 | }
76 | }}
77 | />
78 | >
79 | );
80 | }
81 |
82 | return (
83 |
89 | );
90 | };
91 |
92 | export default connect((state) => ({
93 | mode: state.component.mode,
94 | dirlldownQuery: state.component.dirlldownQuery
95 | }))(GeneratorSankey);
96 |
--------------------------------------------------------------------------------
/src/packages/vcharts/theme.js:
--------------------------------------------------------------------------------
1 | import echarts from "echarts";
2 |
3 | /**
4 | * 默认配置常量
5 | */
6 | const DEFAULT_COLORS = [
7 | "#19d4ae",
8 | "#5ab1ef",
9 | "#fa6e86",
10 | "#ffb980",
11 | "#0067a6",
12 | "#c4b4e4",
13 | "#d87a80",
14 | "#9cbbff",
15 | "#d9d0c7",
16 | "#87a997",
17 | "#d49ea2",
18 | "#5b4947",
19 | "#7ba3a8"
20 | ];
21 |
22 | const CONTRAST_COLOR = "#eee";
23 |
24 | const axisCommon = () => {
25 | return {
26 | axisLine: {
27 | lineStyle: {
28 | color: CONTRAST_COLOR
29 | }
30 | },
31 | axisTick: {
32 | lineStyle: {
33 | color: CONTRAST_COLOR
34 | }
35 | },
36 | axisLabel: {
37 | textStyle: {
38 | color: CONTRAST_COLOR
39 | }
40 | },
41 | splitLine: {
42 | lineStyle: {
43 | type: "dashed",
44 | color: "#aaa"
45 | }
46 | },
47 | splitArea: {
48 | areaStyle: {
49 | color: CONTRAST_COLOR
50 | }
51 | }
52 | };
53 | };
54 |
55 | const DarkTheme = {
56 | color: DEFAULT_COLORS,
57 | backgroundColor: "transparent",
58 | tooltip: {
59 | axisPointer: {
60 | lineStyle: {
61 | color: CONTRAST_COLOR
62 | },
63 | crossStyle: {
64 | color: CONTRAST_COLOR
65 | },
66 | label: {
67 | color: "#000"
68 | }
69 | }
70 | },
71 | legend: {
72 | textStyle: {
73 | color: CONTRAST_COLOR
74 | }
75 | },
76 | title: {
77 | textStyle: {
78 | color: CONTRAST_COLOR
79 | }
80 | },
81 | toolbox: {
82 | iconStyle: {
83 | normal: {
84 | borderColor: CONTRAST_COLOR
85 | }
86 | }
87 | },
88 | dataZoom: {
89 | dataBackgroundColor: "#eee", // Data background color
90 | fillerColor: "rgba(200,200,200,0.2)", // Fill the color
91 | handleColor: "#dd6b66" // Handle color
92 | },
93 | timeline: {
94 | itemStyle: {
95 | color: DEFAULT_COLORS[1]
96 | },
97 | lineStyle: {
98 | color: CONTRAST_COLOR
99 | },
100 | controlStyle: {
101 | color: CONTRAST_COLOR,
102 | borderColor: CONTRAST_COLOR
103 | },
104 | label: {
105 | color: CONTRAST_COLOR
106 | }
107 | },
108 | timeAxis: axisCommon(),
109 | logAxis: axisCommon(),
110 | valueAxis: axisCommon(),
111 | categoryAxis: axisCommon(),
112 | line: {
113 | symbol: "circle"
114 | },
115 | graph: {
116 | color: DEFAULT_COLORS
117 | },
118 | gauge: {
119 | axisLine: {
120 | lineStyle: {
121 | color: [
122 | [0.2, "#dd6b66"],
123 | [0.8, "#759aa0"],
124 | [1, "#ea7e53"]
125 | ],
126 | width: 8
127 | }
128 | }
129 | },
130 | candlestick: {
131 | itemStyle: {
132 | color: "#FD1050",
133 | color0: "#0CF49B",
134 | borderColor: "#FD1050",
135 | borderColor0: "#0CF49B"
136 | }
137 | }
138 | };
139 |
140 | DarkTheme.categoryAxis.splitLine.show = false;
141 |
142 | // register theme object
143 | echarts.registerTheme("dark", DarkTheme);
144 |
--------------------------------------------------------------------------------
/src/packages/vcharts/util.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @param [parma][数据]
3 | * @param [unit][单位]
4 | * @returns {*} 统一返回数据带单位
5 | */
6 | export const tooltipFormatter = (parma, unit = "") => {
7 | let obj = parma[0].name + "
";
8 | for (const item of parma) {
9 | obj = `${obj}${item.marker}${item.seriesName}: ${item.value}${unit}
`;
10 | }
11 | return obj;
12 | };
13 |
--------------------------------------------------------------------------------
/src/packages/vshape/countdown.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from "react";
2 | import { Statistic } from "antd";
3 |
4 | const { Countdown } = Statistic;
5 | const deadline = Date.now() + 1000 * 60 * 60 * 24 * 2 + 1000 * 30; // Moment is also OK
6 |
7 | const VCountdown = ({ options, schema }) => {
8 | const {
9 | prefix = "",
10 | suffix = "",
11 | name = "",
12 | fontFamily = "Microsoft Yahei",
13 | fontSize = 16,
14 | color = "",
15 | precision = 0,
16 | format = "HH:mm:ss"
17 | } = options;
18 | const { data } = schema;
19 |
20 | let option = useMemo(() => {
21 | return {
22 | title: name,
23 | prefix,
24 | suffix,
25 | precision,
26 | format,
27 | valueStyle: { fontFamily, fontSize, color },
28 | value: data.value || deadline
29 | };
30 | }, [options]);
31 |
32 | return ;
33 | };
34 |
35 | export default VCountdown;
36 |
--------------------------------------------------------------------------------
/src/packages/vshape/indicator.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from "react";
2 | import { Statistic, Row, Col } from "antd";
3 |
4 | /**
5 | * 标题自定义
6 | * @param {name} 标题
7 | * @param {style} 样式
8 | */
9 | const TitleName = ({ name, style }) => {
10 | const { fontSize = 16, fontFamily = "Microsoft Yahei", color = "" } = style;
11 |
12 | return (
13 |
20 | {name}
21 |
22 | );
23 | };
24 |
25 | const VIndicator = ({ options, schema }) => {
26 | const {
27 | columns = 0,
28 | cardPadding = 10,
29 | cardRadius = 10,
30 | subFontSize = 14,
31 | prefix = "",
32 | suffix = "",
33 | fontFamily = "Microsoft Yahei",
34 | fontSize = 16,
35 | color = "",
36 | subColor = "",
37 | precision = 0,
38 | cardBackground
39 | } = options;
40 | const { data } = schema;
41 |
42 | let option = useMemo(() => {
43 | return {
44 | prefix,
45 | suffix,
46 | precision,
47 | valueStyle: { fontFamily, fontSize, color }
48 | };
49 | }, [options]);
50 |
51 | const colCount = columns > 0 && columns <= data.data.length ? columns : data.data.length;
52 |
53 | return (
54 |
55 | {data.data.map((m, i) => {
56 | return (
57 |
58 |
65 |
74 | }
75 | value={m.value}
76 | {...option}
77 | />
78 |
79 |
80 | );
81 | })}
82 |
83 | );
84 | };
85 |
86 | export default VIndicator;
87 |
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button, Result } from "antd";
3 | import { withRouter } from "react-router-dom";
4 |
5 | const notFound = (props) => {
6 | const { history } = props;
7 | return (
8 | history.replace("/dashboard")}>
14 | 返回首页
15 |
16 | }
17 | />
18 | );
19 | };
20 |
21 | export default withRouter(notFound);
22 |
--------------------------------------------------------------------------------
/src/pages/dashboard/iframe.js:
--------------------------------------------------------------------------------
1 | import React, { useRef } from "react";
2 | import { IFrame } from "~components";
3 |
4 | const IFrameBus = () => {
5 | const refs = useRef(null);
6 | return (
7 |
8 |
27 | );
28 | };
29 |
30 | export default IFrameBus;
31 |
--------------------------------------------------------------------------------
/src/pages/dashboard/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "react-router-dom";
3 | import { Typography, Divider } from "antd";
4 |
5 | import { Typing } from "~components";
6 |
7 | const { Title, Paragraph } = Typography;
8 |
9 | const Dashboard = () => {
10 | return (
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 | DataV Pro 数据可视化 V2
21 |
22 |
27 |
28 |
29 | 专业级大数据可视化
30 |
31 |
35 |
36 | 多种数据源支持
37 |
38 |
39 |
40 | 图形化编辑界面
41 |
42 |
43 |
44 | 灵活部署和发布
45 |
46 |
50 |
51 |
52 |
53 |
54 | -
55 |
56 | 体验大屏版本v2
57 |
58 |
59 | -
60 |
61 | 体验报表版本v2
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | );
70 | };
71 |
72 | export default Dashboard;
73 |
--------------------------------------------------------------------------------
/src/pages/design-report/data/default.js:
--------------------------------------------------------------------------------
1 | // 公共基础配置项抽离
2 | const BASE_CONF = {
3 | width: 3,
4 | height: 25,
5 | left: 0,
6 | top: 0,
7 | titleAlign: "left",
8 | titleColor: "rgba(188, 201, 212, 1)",
9 | link: "",
10 | background: "",
11 | isLock: false,
12 | isHidden: false,
13 | remark: "",
14 | isCustomStyle: false,
15 | borderRadius: "",
16 | borderColor: "",
17 | borderWidth: "",
18 | borderStyle: "solid",
19 | shadowOffset: 0,
20 | shadowColor: "",
21 | shadowWidth: 0,
22 | animateType: "",
23 | animateTime: "",
24 | animateSpeed: "",
25 | animateRepeat: "",
26 | drillDownOpen: false,
27 | drillDown: [],
28 | dependenceOpen: false,
29 | dependence: []
30 | };
31 |
32 | // 公共数据配置项抽离
33 | const BASE_CONF_DATA = {
34 | dataType: "json",
35 | dataSqlId: "",
36 | dataModals: {},
37 | dataApiId: "",
38 | isRefresh: true,
39 | refreshTime: 1800
40 | };
41 |
42 | export { BASE_CONF, BASE_CONF_DATA };
43 |
--------------------------------------------------------------------------------
/src/pages/design-report/data/index.js:
--------------------------------------------------------------------------------
1 | import BAR_CONF from "./bar";
2 | import LINE_CONF from "./line";
3 | import MAP_CONF from "./map";
4 | import DATAV_CONF from "./datav";
5 |
6 | // if you should all configs.
7 | const condition = [BAR_CONF, BAR_CONF, LINE_CONF, DATAV_CONF].flat(1);
8 |
9 | export default {
10 | vbar: BAR_CONF,
11 | vline: LINE_CONF,
12 | vmap: MAP_CONF,
13 | vdatav: DATAV_CONF,
14 | collection: condition
15 | };
16 |
--------------------------------------------------------------------------------
/src/pages/design-report/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * core 编辑器渲染核心代码
3 | */
4 | import React, { useEffect } from "react";
5 | import { connect } from "react-redux";
6 |
7 | import { useStore, useDocumentTitle } from "~renderer/common/hooks";
8 | import { Ctx, Compose } from "~renderer/common/context";
9 | import { loadScript } from "@/utils/helper";
10 |
11 | import AxureLayoutAside from "./src/aside";
12 | import AxureLayoutHeader from "./src/header";
13 | import AxureLayoutContent from "./src/wrapper";
14 | import AxureLayoutField from "./src/setting";
15 |
16 | import { dataVGrid, dataVApiList, dataVSqlList } from "@/api";
17 | import { AxureGrid } from "~renderer";
18 |
19 | loadScript("https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css", "css");
20 |
21 | const AxureGridEditor = (props) => {
22 | useDocumentTitle("DataV Pro - 数据报表");
23 | const [state, setState] = useStore({
24 | tabsKey: "base",
25 | selected: "-",
26 | components: [],
27 | page: {
28 | name: "",
29 | remark: "",
30 | backgroundColor: "rgba(29, 33, 39, 1)",
31 | backgroundImage: "",
32 | backgroundBlur: 0,
33 | backgroundOpacity: 10
34 | },
35 | undo: [],
36 | redo: []
37 | });
38 |
39 | // 其他无关状态
40 | const [view, setView] = useStore({
41 | version: "2.0.0",
42 | layerCollapsed: false,
43 | settingCollapsed: false,
44 | visible: false
45 | });
46 |
47 | // 异步多接口请求
48 | const storageData = async () => {
49 | try {
50 | const results = await Promise.all([dataVApiList(), dataVSqlList()]).then((ret) => {
51 | return ret;
52 | });
53 | props.dispatch({ type: "DATAV_API_ENUM", data: results[0].data });
54 | props.dispatch({ type: "DATAV_SQL_ENUM", data: results[1].data });
55 | } catch (err) {
56 | console.warn(err);
57 | }
58 | };
59 |
60 | const fetchData = async () => {
61 | const { data } = await dataVGrid();
62 | setState({
63 | page: data.page,
64 | components: data.components
65 | });
66 | };
67 |
68 | useEffect(() => {
69 | fetchData();
70 | storageData();
71 | }, []);
72 |
73 | return (
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | );
89 | };
90 |
91 | export default connect((state) => state.component)(AxureGridEditor);
92 |
--------------------------------------------------------------------------------
/src/pages/design-report/preview.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import { AxureGridParser } from "~renderer";
3 |
4 | import { useDocumentTitle } from "~renderer/common/hooks";
5 | import storage from "@/utils/storage";
6 |
7 | const PanelPreview = () => {
8 | let schemaConfig = JSON.parse(storage.getSession("schema_grid_config") || {});
9 | const { backgroundColor, backgroundImage, backgroundOpacity, backgroundBlur } = schemaConfig.page;
10 | useDocumentTitle(`DataV Pro - ${schemaConfig.page.name || "未命名"}`);
11 |
12 | return (
13 |
14 |
26 |
27 |
28 | );
29 | };
30 |
31 | export default PanelPreview;
32 |
--------------------------------------------------------------------------------
/src/pages/design-report/schema/datav.js:
--------------------------------------------------------------------------------
1 | import { BASE_CONF } from "./default.js";
2 |
3 | export default [
4 | {
5 | materials: "decoration",
6 | fields: [
7 | {
8 | name: "基础",
9 | key: "base",
10 | schema: {
11 | type: "object",
12 | properties: { ...BASE_CONF }
13 | }
14 | }
15 | ]
16 | }
17 | ];
18 |
--------------------------------------------------------------------------------
/src/pages/design-report/schema/index.js:
--------------------------------------------------------------------------------
1 | import BAR_SCHEMA from "./bar";
2 | import LINE_SCHEMA from "./line";
3 | import DATAV_SCHEMA from "./datav";
4 |
5 | // if you should all configs.
6 | export const gridToSchema = [BAR_SCHEMA, LINE_SCHEMA, DATAV_SCHEMA].flat(1);
7 |
8 | export { default as pageSchema } from "./page";
9 |
--------------------------------------------------------------------------------
/src/pages/design-report/schema/page.js:
--------------------------------------------------------------------------------
1 | export default {
2 | type: "object",
3 | properties: {
4 | page: {
5 | type: "object",
6 | title: "控制面板",
7 | description: "大屏页面整体配置(大屏的删除、目录结构调整等,请在管理中心的『大屏管理』中进行!)",
8 | displayType: "column",
9 | properties: {
10 | name: {
11 | title: "报表名称",
12 | type: "string",
13 |
14 | options: { allowClear: true, placeholder: "请输入名称" }
15 | },
16 | remark: {
17 | title: "报表简介",
18 | type: "string",
19 | format: "textarea",
20 |
21 | options: { placeholder: "请输入简介" }
22 | },
23 | backgroundColor: {
24 | title: "背景颜色(背景图片透明时可见)",
25 | type: "string",
26 | component: "color",
27 | hidden: "{{rootValue.backgroundImage !== ''}}"
28 | },
29 | backgroundImage: {
30 | title: "背景图",
31 | type: "string",
32 | format: "upload",
33 | action: "https://www.mocky.io/v2/5cc8019d300000980a055e76"
34 | },
35 | backgroundBlur: {
36 | title: "背景图片模糊度",
37 | type: "number",
38 | component: "slider",
39 | min: 0,
40 | max: 20,
41 | options: {
42 | hideNumber: true
43 | }
44 | },
45 | backgroundOpacity: {
46 | title: "背景图片透明度",
47 | type: "number",
48 | component: "slider",
49 | min: 0,
50 | max: 10,
51 | options: {
52 | hideNumber: true
53 | }
54 | }
55 | }
56 | }
57 | }
58 | };
59 |
--------------------------------------------------------------------------------
/src/pages/design-report/src/page.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Renderers from "~renderer/factory";
3 | import { useEditorStore } from "~renderer/common/hooks";
4 |
5 | import { pageSchema } from "../schema";
6 |
7 | const PageSetting = () => {
8 | const { state, setState } = useEditorStore();
9 |
10 | const onValueChange = (value) => {
11 | setState({
12 | page: value.page
13 | });
14 | };
15 |
16 | return (
17 |
22 |
29 |
30 | );
31 | };
32 |
33 | export default PageSetting;
34 |
--------------------------------------------------------------------------------
/src/pages/design-report/src/setting.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { Tabs } from "antd";
3 | import Renderers from "~renderer/factory";
4 |
5 | import PageLayout from "./page";
6 | import { useEditorStore, useCompose } from "~renderer/common/hooks";
7 | import { getFieldConf, mergeField } from "~renderer/utils";
8 | import { gridToSchema } from "../schema";
9 |
10 | const { TabPane } = Tabs;
11 |
12 | const FieldSetConf = () => {
13 | const [field, setField] = useState([]);
14 | const { state, setState } = useEditorStore();
15 | const { view } = useCompose();
16 |
17 | useEffect(() => {
18 | if (state.components.length > 0 && state.selected !== "-") {
19 | try {
20 | // TODO: 获取物料组件配置项
21 | const currentField = getFieldConf(state.components, state.selected);
22 | const currentFieldSchema = gridToSchema.filter((o) => o.materials === currentField.type)[0].fields;
23 | setField([
24 | {
25 | displayName: currentField.type,
26 | value: currentField.data,
27 | fields: currentFieldSchema
28 | }
29 | ]);
30 | } catch (error) {}
31 | } else {
32 | setField([]);
33 | }
34 | return () => {
35 | setState({ tabsKey: "base" });
36 | };
37 | }, [state.selected]);
38 |
39 | const onValueChange = (value) => {
40 | let rootValue = { ...value, drillDownLevel: 0 };
41 |
42 | // 联动、下钻参数变更
43 | if (value.drillDownOpen) {
44 | rootValue = {
45 | ...rootValue,
46 | dependenceOpen: !value.drillDownOpen
47 | };
48 | }
49 |
50 | if (value.dependenceOpen) {
51 | rootValue = {
52 | ...rootValue,
53 | drillDownOpen: !value.dependenceOpen
54 | };
55 | }
56 |
57 | let result = mergeField(state.components, state.selected, rootValue, 0);
58 | setState({
59 | components: result
60 | });
61 | };
62 |
63 | return (
64 |
65 | {state.selected === "-" ? (
66 |
67 | ) : (
68 |
{
74 | setState({ tabsKey: key });
75 | }}
76 | >
77 | {field.map((m) => {
78 | return m.fields.map((item) => (
79 |
80 |
86 |
87 | ));
88 | })}
89 |
90 | )}
91 |
92 | );
93 | };
94 |
95 | export default FieldSetConf;
96 |
--------------------------------------------------------------------------------
/src/pages/design-report/src/wrapper.js:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from "react";
2 | import { Scrollbar } from "~components";
3 | import { useAutoResize } from "~renderer/common/hooks";
4 |
5 | /**
6 | * 设计器容器大小
7 | */
8 | const Wrapper = forwardRef((props, ref) => {
9 | const { backgroundColor, backgroundImage, backgroundBlur, backgroundOpacity } = props;
10 | const { domRef } = useAutoResize(ref);
11 |
12 | return (
13 |
14 |
26 |
{props.children}
27 |
28 | );
29 | });
30 |
31 | export default Wrapper;
32 |
--------------------------------------------------------------------------------
/src/pages/design-screen/data/default.js:
--------------------------------------------------------------------------------
1 | // 公共基础配置项抽离
2 | const BASE_CONF = {
3 | width: 400,
4 | height: 250,
5 | left: 15,
6 | top: 15,
7 | background: "",
8 | isLock: false,
9 | isHidden: false,
10 | remark: "",
11 | isCustomStyle: false,
12 | borderRadius: "",
13 | borderColor: "",
14 | borderWidth: "",
15 | borderStyle: "solid",
16 | shadowOffset: 0,
17 | shadowColor: "",
18 | shadowWidth: 0,
19 | animateType: "",
20 | animateTime: "",
21 | animateSpeed: "",
22 | animateRepeat: "",
23 | drillDownOpen: false,
24 | drillDown: [],
25 | dependenceOpen: false,
26 | dependence: [],
27 | isRefresh: false,
28 | refreshTime: 1800
29 | };
30 |
31 | // 公共数据配置项抽离
32 | const BASE_CONF_DATA = {
33 | dataType: "json",
34 | dataSqlId: "",
35 | dataModals: {},
36 | dataApiId: ""
37 | };
38 |
39 | export { BASE_CONF, BASE_CONF_DATA };
40 |
--------------------------------------------------------------------------------
/src/pages/design-screen/data/index.js:
--------------------------------------------------------------------------------
1 | import BAR_CONF from "./bar";
2 | import LINE_CONF from "./line";
3 | import PIE_CONF from "./pie";
4 | import MAP_CONF from "./map";
5 | import DATAV_CONF from "./datav";
6 | import OTHER_CONF from "./other";
7 |
8 | // if you should all configs.
9 | const condition = [BAR_CONF, LINE_CONF, PIE_CONF, MAP_CONF, OTHER_CONF, DATAV_CONF].flat(1);
10 |
11 | export default {
12 | vbar: BAR_CONF,
13 | vline: LINE_CONF,
14 | vpie: PIE_CONF,
15 | vmap: MAP_CONF,
16 | vdatav: DATAV_CONF,
17 | vother: OTHER_CONF,
18 | collection: condition
19 | };
20 |
--------------------------------------------------------------------------------
/src/pages/design-screen/data/other.js:
--------------------------------------------------------------------------------
1 | import { BASE_CONF, BASE_CONF_DATA } from "./default.js";
2 |
3 | export default [
4 | {
5 | name: "词云图",
6 | icon: "word-cloud",
7 | type: "wordCloud",
8 | data: {
9 | title: "词云图",
10 | ...BASE_CONF,
11 | config: {
12 | unit: "",
13 | backgroundColor: "#E3F7FF",
14 | fontPadding: 8,
15 | fontRotate: true,
16 | minFontSize: 12,
17 | maxFontSize: 35,
18 | shape: "circle",
19 | gridSize: 5,
20 | rotationStep: true
21 | },
22 | dataConfig: {
23 | ...BASE_CONF_DATA,
24 | data: {
25 | series: [
26 | { name: "龙头镇", value: "111" },
27 | { name: "大埔镇", value: "222" },
28 | { name: "太平镇", value: "458" },
29 | { name: "沙埔镇", value: "445" },
30 | { name: "东泉镇", value: "456" },
31 | { name: "凤山镇", value: "647" },
32 | { name: "六塘镇", value: "189" },
33 | { name: "冲脉镇", value: "864" },
34 | { name: "寨隆镇", value: "652" }
35 | ]
36 | }
37 | }
38 | }
39 | }
40 | ];
41 |
--------------------------------------------------------------------------------
/src/pages/design-screen/preview.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { connect } from "react-redux";
3 | import { AutonContainer } from "~components";
4 | import { AxureScreenParser } from "~renderer";
5 | import { DIMENSION } from "~renderer/common/constants";
6 | import { useDocumentTitle } from "~renderer/common/hooks";
7 |
8 | import storage from "@/utils/storage";
9 |
10 | const PanelPreview = (props) => {
11 | const [fullStyles, setFullStyles] = useState({});
12 | let schemaConfig = JSON.parse(storage.getSession("schema_screen_config") || {});
13 | const { pageSize, backgroundColor, backgroundImage, zoom, backgroundOpacity, backgroundBlur } = schemaConfig.page;
14 | useDocumentTitle(`DataV Pro - ${schemaConfig.page.name || "未命名"}`);
15 |
16 | useEffect(() => {
17 | props.dispatch({ type: "SET_MODE", data: "preview" });
18 | setFullStyles({
19 | position: "relative",
20 | width: DIMENSION[pageSize].width,
21 | height: DIMENSION[pageSize].height
22 | });
23 | }, []);
24 |
25 | return (
26 |
27 |
39 |
40 |
41 | );
42 | };
43 |
44 | export default connect((state) => state.component)(PanelPreview);
45 |
--------------------------------------------------------------------------------
/src/pages/design-screen/schema/index.js:
--------------------------------------------------------------------------------
1 | import BAR_SCHEMA from "./bar";
2 | import LINE_SCHEMA from "./line";
3 | import MAP_SCHEMA from "./map";
4 | import DATAV_SCHEMA from "./datav";
5 | import OTHER_SCHEMA from "./other";
6 |
7 | // if you should all configs.
8 | export const screenToSchema = [BAR_SCHEMA, LINE_SCHEMA, MAP_SCHEMA, OTHER_SCHEMA, DATAV_SCHEMA].flat(1);
9 |
10 | export { default as pageSchema } from "./page";
11 |
--------------------------------------------------------------------------------
/src/pages/design-screen/schema/page.js:
--------------------------------------------------------------------------------
1 | export default {
2 | type: "object",
3 | properties: {
4 | page: {
5 | type: "object",
6 | title: "控制面板",
7 | description: "大屏页面整体配置(大屏的删除、目录结构调整等,请在管理中心的『大屏管理』中进行!)",
8 | displayType: "column",
9 | properties: {
10 | name: {
11 | title: "大屏名称",
12 | type: "string",
13 | displayType: "row",
14 | options: { allowClear: true, placeholder: "请输入名称" }
15 | },
16 | remark: {
17 | title: "大屏简介",
18 | type: "string",
19 | format: "textarea",
20 | displayType: "row",
21 | options: { placeholder: "请输入简介" }
22 | },
23 | pageSize: {
24 | title: "屏幕大小",
25 | type: "string",
26 | enum: ["small", "middle", "large", "mobile"],
27 | enumNames: ["1366 x 768", "1440 x 960", "1920 x 1080", "750 x 1334"],
28 | displayType: "row",
29 | options: {
30 | placeholder: "请选择页面分辨率"
31 | }
32 | },
33 | zoom: {
34 | title: "缩放设置",
35 | type: "string",
36 | component: "radio",
37 | displayType: "row",
38 | enum: ["scaleY", "scaleX", "cover"],
39 | enumNames: ["等比例缩放高度铺满", "等比例缩放宽度铺满", "全屏铺满"]
40 | },
41 | backgroundColor: {
42 | title: "背景颜色(背景图片透明时可见)",
43 | type: "string",
44 | component: "color",
45 | hidden: "{{rootValue.backgroundImage !== ''}}"
46 | },
47 | backgroundImage: {
48 | title: "背景图",
49 | type: "string",
50 | format: "upload",
51 | action: "https://www.mocky.io/v2/5cc8019d300000980a055e76"
52 | },
53 | backgroundBlur: {
54 | title: "背景图片模糊度",
55 | type: "number",
56 | component: "slider",
57 | min: 0,
58 | max: 20,
59 | options: {
60 | hideNumber: true
61 | }
62 | },
63 | backgroundOpacity: {
64 | title: "背景图片透明度",
65 | type: "number",
66 | component: "slider",
67 | min: 0,
68 | max: 10,
69 | options: {
70 | hideNumber: true
71 | }
72 | }
73 | }
74 | }
75 | }
76 | };
77 |
--------------------------------------------------------------------------------
/src/pages/design-screen/src/page.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Renderers from "~renderer/factory";
3 | import { useEditorStore } from "~renderer/common/hooks";
4 |
5 | import { pageSchema } from "../schema";
6 |
7 | const PageSetting = () => {
8 | const { state, setState } = useEditorStore();
9 |
10 | const onValueChange = (value) => {
11 | setState({
12 | page: value.page
13 | });
14 | };
15 |
16 | return (
17 |
24 | );
25 | };
26 |
27 | export default PageSetting;
28 |
--------------------------------------------------------------------------------
/src/pages/design-screen/src/setting.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useCallback } from "react";
2 | import { Tabs } from "antd";
3 | import Renderers from "~renderer/factory";
4 |
5 | import PageLayout from "./page";
6 | import { useEditorStore, useCompose } from "~renderer/common/hooks";
7 | import { getFieldConf, mergeField, setLevelPath } from "~renderer/utils";
8 | import { screenToSchema } from "../schema";
9 |
10 | const { TabPane } = Tabs;
11 |
12 | const FieldSetConf = () => {
13 | const { state, setState } = useEditorStore();
14 | const { view } = useCompose();
15 |
16 | const field = useMemo(() => {
17 | if (state.selected !== "-") {
18 | try {
19 | // TODO: 获取物料组件配置项
20 | const currentField = getFieldConf(state.components, state.selected);
21 | const currentFieldSchema = screenToSchema.find((o) => o.materials === currentField.type).fields;
22 |
23 | return [
24 | {
25 | displayName: currentField.type,
26 | value: currentField.data,
27 | fields: currentFieldSchema
28 | }
29 | ];
30 | } catch (error) {}
31 | } else {
32 | return [];
33 | }
34 | }, [state.selected, state.tabsKey]);
35 |
36 | const onValueChange = (value) => {
37 | let rootValue = { ...value };
38 |
39 | // 联动、下钻参数变更
40 | if (value.drillDownOpen) {
41 | rootValue = {
42 | ...rootValue,
43 | dependenceOpen: !value.drillDownOpen
44 | };
45 | }
46 |
47 | if (value.dependenceOpen) {
48 | rootValue = {
49 | ...rootValue,
50 | drillDownOpen: !value.dependenceOpen
51 | };
52 | }
53 |
54 | let results = mergeField(state.components, state.selected, rootValue, 0);
55 | setLevelPath(results, null);
56 | setState({
57 | components: results
58 | });
59 | };
60 |
61 | return (
62 |
63 | {state.selected === "-" ? (
64 |
65 | ) : (
66 |
{
72 | setState({ tabsKey: key });
73 | }}
74 | >
75 | {field.map((m) => {
76 | return m.fields.map((item) => (
77 |
78 | {state.tabsKey === item.key && (
79 |
85 | )}
86 |
87 | ));
88 | })}
89 |
90 | )}
91 |
92 | );
93 | };
94 |
95 | export default FieldSetConf;
96 |
--------------------------------------------------------------------------------
/src/polyfills.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ie polyfill
3 | * yarn add react-app-polyfill core-js
4 | * create in 2020-08-12 by Aaron
5 | */
6 | import "react-app-polyfill/ie11";
7 | import "react-app-polyfill/stable";
8 |
--------------------------------------------------------------------------------
/src/renderer/common/context.js:
--------------------------------------------------------------------------------
1 | import { createContext } from "react";
2 | import materials from "~packages/materials";
3 |
4 | export const Ctx = createContext({});
5 | export const Compose = createContext();
6 |
7 | export function getField(type) {
8 | let fieldCanRedefine = false;
9 | let Field = materials[type];
10 |
11 | if (Field) {
12 | fieldCanRedefine = !!Field;
13 | }
14 |
15 | return {
16 | fieldCanRedefine,
17 | Field: Field || null
18 | };
19 | }
20 |
--------------------------------------------------------------------------------
/src/renderer/factory/atom.less:
--------------------------------------------------------------------------------
1 | .field-wrapper {
2 | position: relative;
3 | padding: 10px 0;
4 | width: 100%;
5 | height: 100%;
6 | font-variant: tabular-nums;
7 | }
8 |
9 | .field-flex {
10 | padding: 5px 10px;
11 | display: flex;
12 | align-items: center;
13 | width: 100%;
14 | &__label {
15 | display: block;
16 | flex-shrink: 0;
17 | }
18 | &__title {
19 | position: relative;
20 | display: inline-flex;
21 | align-items: center;
22 | min-height: 32px;
23 | color: var(--datav-font-color);
24 | font-size: 13px;
25 | }
26 | &__desc {
27 | margin-left: 3px;
28 | font-size: 12px;
29 | }
30 | &__control {
31 | position: relative;
32 | flex-grow: 1;
33 | }
34 | &__vertical {
35 | align-items: stretch;
36 | -webkit-box-orient: vertical;
37 | -webkit-box-direction: normal;
38 | -ms-flex-direction: column;
39 | flex-direction: column;
40 | .field-flex__label {
41 | padding: 0 0 4px;
42 | line-height: 1.5715;
43 | white-space: normal;
44 | text-align: left;
45 | }
46 | }
47 | &__slider {
48 | display: flex;
49 | align-items: center;
50 | width: 100%;
51 | }
52 | &__object {
53 | position: relative;
54 | margin-bottom: 10px;
55 | padding: 5px 0;
56 | line-height: 1.4;
57 | &::after {
58 | content: "";
59 | position: absolute;
60 | top: 0;
61 | left: 0;
62 | width: 200%;
63 | height: 200%;
64 | transform: scale(0.5);
65 | transform-origin: 0 0;
66 | pointer-events: none;
67 | border: 0 solid #303030;
68 | border-bottom-width: 2px; /* no */
69 | left: 0;
70 | right: 0;
71 | }
72 | .field-flex__title {
73 | font-size: 16px;
74 | }
75 | }
76 | .field-wrapper {
77 | padding: 0;
78 | .field-flex {
79 | padding: 7px 0 0;
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/renderer/factory/getField.js:
--------------------------------------------------------------------------------
1 | import { mapping } from "~packages";
2 |
3 | function getWidgetName(schema, _mapping = mapping) {
4 | const { type, format, enum: enums, readonly } = schema;
5 |
6 | // 如果已经注明了渲染widget,那最好
7 | if (schema.component) {
8 | return schema.component;
9 | }
10 |
11 | const list = [];
12 | if (readonly) {
13 | list.push(`${type}?readonly`);
14 | list.push("*?readonly");
15 | }
16 | if (enums) {
17 | list.push(`${type}?enum`);
18 | // array 默认使用list,array?enum 默认使用checkboxes,*?enum 默认使用select
19 | list.push("*?enum");
20 | }
21 | if (format) {
22 | list.push(`${type}:${format}`);
23 | }
24 | list.push(type); // 放在最后兜底,其他都不match时使用type默认的组件
25 | let found = "";
26 | list.some((item) => {
27 | found = _mapping[item];
28 | return !!found;
29 | });
30 | return found;
31 | }
32 |
33 | export default function getField(schema = {}, { customized, generated, mapping }) {
34 | const { component: widget, widget: field } = schema;
35 | // Field能否被重定义
36 | let fieldCanRedefine = false;
37 | let Field;
38 | // component 是字符串,从generated中查,不是的话,就是本身
39 | const _widget = typeof widget === "string" ? generated[widget] : widget;
40 | if (field && !Field) {
41 | Field = typeof field === "string" ? customized[field] : field;
42 | }
43 | if (!Field && _widget) {
44 | Field = _widget;
45 | }
46 | if (!Field && !_widget) {
47 | Field = generated[getWidgetName(schema, mapping)];
48 | fieldCanRedefine = !!Field;
49 | }
50 | return {
51 | fieldCanRedefine,
52 | Field: Field || null
53 | };
54 | }
55 |
--------------------------------------------------------------------------------
/src/renderer/factory/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react";
2 | import PropTypes from "prop-types";
3 | import Factory from "./factory";
4 | import { mapping, widgets } from "~packages";
5 | import "./atom.less";
6 |
7 | export default class Factorys extends PureComponent {
8 | static propTypes = {
9 | mapping: PropTypes.object,
10 | widgets: PropTypes.object
11 | };
12 |
13 | static defaultProps = {
14 | mapping: {},
15 | widgets: {}
16 | };
17 |
18 | render() {
19 | const { mapping: customizedMapping, widgets: customizedWidgets, ...props } = this.props;
20 | return (
21 |
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/renderer/factory/resolve.js:
--------------------------------------------------------------------------------
1 | import { cloneDeep } from "lodash";
2 | import { isFunction } from "../utils";
3 |
4 | // 获取当前字段默认值
5 | function getDefaultValue(schema) {
6 | const { default: def, enum: enums = [], type } = schema;
7 | const defaultValue = {
8 | array: [],
9 | boolean: false,
10 | integer: "",
11 | null: null,
12 | number: "",
13 | object: {},
14 | string: "",
15 | range: null
16 | };
17 |
18 | if (isFunction(def)) {
19 | return defaultValue[type];
20 | }
21 | if (isFunction(enums)) {
22 | if (type === "array") {
23 | return [];
24 | }
25 | if (type === "string" || type === "number") {
26 | return "";
27 | }
28 | }
29 |
30 | // 如果设置默认值,优先从默认值中获取
31 | if (typeof def !== "undefined") {
32 | return def;
33 | }
34 | // array且enum的情况,为多选框,默认值[]
35 | if (type === "array" && enums.length) {
36 | return [];
37 | }
38 | // 如果enum是表达式,不处理
39 | // 如果设置枚举值,其次从枚举值中获取
40 | if (Array.isArray(enums) && enums[0] && typeof enums[0] !== "undefined") {
41 | if ("default" in schema || schema.hasOwnProperty("default")) {
42 | return schema.default; // 就算default: undefined, 也用 undefined, 这样就可以清空了
43 | }
44 | return enums[0];
45 | }
46 | // 最后使用对应基础类型的默认值
47 | return defaultValue[type];
48 | }
49 |
50 | function resolve(schema, data, options = {}) {
51 | const {
52 | // 类型
53 | type,
54 | // 对象子集
55 | properties,
56 | // 数组子集
57 | items,
58 | // 必选值,对象的子集
59 | default: def,
60 | component: widget
61 | } = schema;
62 | const {
63 | // 按照required规则做数据补全
64 | checkRequired = false
65 | } = options;
66 |
67 | const value = typeof data === "undefined" ? getDefaultValue(schema) : cloneDeep(data);
68 |
69 | if (type === "object") {
70 | // 如果自定义组件
71 | if (widget) {
72 | if (def && typeof def === "object") {
73 | return def;
74 | }
75 | return value;
76 | }
77 | const subs = properties || {};
78 | const ret = {};
79 | Object.keys(subs).forEach((name) => {
80 | const checkAndPass = checkRequired && [].concat(required).indexOf(name) !== -1;
81 | if (!checkRequired || checkAndPass) {
82 | ret[name] = resolve(subs[name], value[name], options);
83 | }
84 | });
85 | return ret;
86 | }
87 | if (type === "array") {
88 | // 如果没有value且default有值,用default
89 | if (def && Array.isArray(def) && !value) {
90 | return def;
91 | }
92 | // 如果自定义组件
93 | if (widget) return value;
94 |
95 | const subs = [].concat(items || []);
96 | const ret = [];
97 | value.forEach &&
98 | value.forEach((item, idx) => {
99 | ret[idx] = resolve(subs[idx] || subs[0], item, options);
100 | });
101 | return ret;
102 | }
103 | return value;
104 | }
105 |
106 | export default resolve;
107 |
--------------------------------------------------------------------------------
/src/renderer/factory/subFieldGenerator.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const subFieldGenerator = ({
4 | fieldCanRedefine: can,
5 | Field: SourceField = null,
6 | props = {},
7 | }) => args => {
8 | const { name, Field: RedefineField = null, ...others } = args;
9 | const Field = (can && RedefineField) || SourceField;
10 | if (Field) {
11 | return ;
12 | }
13 | return null;
14 | };
15 |
16 | export default subFieldGenerator;
17 |
--------------------------------------------------------------------------------
/src/renderer/index.js:
--------------------------------------------------------------------------------
1 | export { default as AxureScreen } from "./src/core";
2 |
3 | export { default as AxureGrid } from "./src/core-grid";
4 |
5 | export { default as AxureScreenParser } from "./src/parser";
6 |
7 | export { default as AxureGridParser } from "./src/parser-grid";
8 |
9 | export { default as AxureParser } from "./src/core-resolve";
10 |
--------------------------------------------------------------------------------
/src/renderer/src/core-resolve.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 下钻模态框组件解析
3 | */
4 | import React, { useMemo } from "react";
5 | import cx from "classnames";
6 | import { connect } from "react-redux";
7 | import { getField } from "../common/context";
8 | import { converLayout } from "../utils";
9 |
10 | import generator from "./generator";
11 |
12 | import { guid } from "@/utils";
13 |
14 | const GeneratorField = ({ mode, value, onRowValueChange }) => {
15 | const { width, height, background, ...rest } = value.data;
16 |
17 | const classNames = cx("gc-field animate__animated", {
18 | [`animate__${rest.animateType}`]: rest.animateType,
19 | [`animate__${rest.animateSpeed}`]: rest.animateSpeed,
20 | [`animate__${rest.animateRepeat}`]: rest.animateRepeat,
21 | [`animate__delay-${rest.animateTime}s`]: rest.animateTime
22 | });
23 |
24 | const overwriteStyle = {
25 | width: mode === "development" ? "100%" : "calc((width || 1) / 12 * 90%)",
26 | height: converLayout(height),
27 | borderStyle: rest.borderStyle || "solid",
28 | borderColor: "transparent",
29 | background,
30 | borderRadius: rest.borderRadius,
31 | borderWidth: rest.borderWidth || 2,
32 | boxShadow: rest.shadowColor
33 | ? `${rest.shadowColor} ${rest.shadowWidth || 0} ${rest.shadowOffset || 0} ${rest.shadowOffset || 0}`
34 | : rest.shadowWidth
35 | };
36 |
37 | const getSubField = (m) => {
38 | const field = getField(value.type);
39 | return generator(field)(m);
40 | };
41 |
42 | const fieldProps = useMemo(
43 | () => ({
44 | value: value.data,
45 | uniqueId: guid(),
46 | options: value.data.config || {},
47 | onChange: (val, level) => onRowValueChange(val, level)
48 | }),
49 | [value.data, onRowValueChange]
50 | );
51 |
52 | return (
53 |
54 | {getSubField(fieldProps)}
55 |
56 | );
57 | };
58 |
59 | export default connect((state) => ({
60 | mode: state.component.mode
61 | }))(GeneratorField);
62 |
--------------------------------------------------------------------------------
/src/renderer/src/generator.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Empty } from "antd";
3 |
4 | import "./renderer.less";
5 | import "./datav.less";
6 |
7 | const subFieldGenerator = ({ fieldCanRedefine: can, Field: SourceField = null, props = {} }) => (args) => {
8 | const { Field: RedefineField = null, ...rest } = args;
9 | const Field = (can && RedefineField) || SourceField;
10 | if (Field) {
11 | return ;
12 | }
13 | return (
14 |
15 |
16 |
17 | );
18 | };
19 |
20 | export default subFieldGenerator;
21 |
--------------------------------------------------------------------------------
/src/renderer/src/parser-grid.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import cx from "classnames";
3 | import { WidthProvider, Responsive } from "react-grid-layout";
4 | import { getField } from "../common/context";
5 | import generator from "./generator";
6 |
7 | import "react-grid-layout/css/styles.css";
8 | import "react-resizable/css/styles.css";
9 |
10 | const ResponsiveGridLayout = WidthProvider(Responsive);
11 |
12 | const GeneratorField = ({ value }) => {
13 | const { title, hideTitle, titleAlign, titleColor, background, ...rest } = value.data;
14 | const className = cx("gc-field-grid animate__animated", {
15 | [`animate__${rest.animateType}`]: rest.animateType,
16 | [`animate__${rest.animateSpeed}`]: rest.animateSpeed,
17 | [`animate__${rest.animateRepeat}`]: rest.animateRepeat,
18 | [`animate__delay-${rest.animateTime}s`]: rest.animateTime
19 | });
20 |
21 | const overwriteStyle = {
22 | background,
23 | boxShadow: rest.shadowColor
24 | ? `${rest.shadowColor} ${rest.shadowWidth || 0} ${rest.shadowOffset || 0} ${rest.shadowOffset || 0}`
25 | : rest.shadowWidth
26 | };
27 |
28 | const getSubField = (m) => {
29 | const prop = getField(value.type);
30 | return generator(prop)(m);
31 | };
32 |
33 | return (
34 |
35 | {!hideTitle ? (
36 |
43 | {title}
44 |
45 | ) : null}
46 |
47 | {getSubField({
48 | value: value.data,
49 | uniqueId: value.uniqueId,
50 | options: value.data.config
51 | })}
52 |
53 |
54 | );
55 | };
56 |
57 | const GeneratorWidget = ({ widgets = [] }) => {
58 | if (widgets.length === 0) return null;
59 |
60 | return (
61 |
70 | {widgets.map((prop) => (
71 |
80 | {!prop.data.isHidden ? : null}
81 |
82 | ))}
83 |
84 | );
85 | };
86 |
87 | export default GeneratorWidget;
88 |
--------------------------------------------------------------------------------
/src/renderer/src/parser.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import cx from "classnames";
3 | import { getField } from "../common/context";
4 |
5 | import generator from "./generator";
6 |
7 | const GeneratorField = ({ value }) => {
8 | const { width, height, background, left, top, ...rest } = value.data;
9 | const className = cx("animate__animated", {
10 | [`animate__${rest.animateType}`]: rest.animateType,
11 | [`animate__${rest.animateSpeed}`]: rest.animateSpeed,
12 | [`animate__${rest.animateRepeat}`]: rest.animateRepeat,
13 | [`animate__delay-${rest.animateTime}s`]: rest.animateTime
14 | });
15 |
16 | const overwriteStyle = {
17 | position: "absolute",
18 | left: left,
19 | top: top,
20 | padding: "5px 12px",
21 | width: width,
22 | height: height,
23 | borderColor: "transparent",
24 | borderWidth: 2,
25 | borderStyle: "solid",
26 | background,
27 | boxShadow: rest.shadowColor
28 | ? `${rest.shadowColor} ${rest.shadowWidth || 0} ${rest.shadowOffset || 0} ${rest.shadowOffset || 0}`
29 | : rest.shadowWidth
30 | };
31 |
32 | const getSubField = (m) => {
33 | const prop = getField(value.type);
34 | return generator(prop)(m);
35 | };
36 |
37 | return (
38 |
39 | {getSubField({
40 | isDevelop: false,
41 | value: value.data,
42 | uniqueId: value.uniqueId,
43 | options: value.data.config
44 | })}
45 |
46 | );
47 | };
48 |
49 | const GeneratorWidget = ({ widgets = [] }) => {
50 | if (widgets.length === 0) return null;
51 | return widgets.map((prop) => );
52 | };
53 |
54 | export default GeneratorWidget;
55 |
--------------------------------------------------------------------------------
/src/router/history.js:
--------------------------------------------------------------------------------
1 | import { createHashHistory } from "history";
2 |
3 | const env = process.env.NODE_ENV; // 环境参数
4 | let options = {};
5 |
6 | if (env === "production") {
7 | options.basename = "/";
8 | }
9 |
10 | export default createHashHistory(options);
11 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Router, Route, Switch, Redirect } from "react-router-dom";
3 | import { connect } from "react-redux";
4 |
5 | import history from "./history";
6 | import AccountIn from "@/pages/account";
7 | import BaseLayout from "@/layouts";
8 | import AxureScreen from "@/pages/design-screen";
9 | import AxureGrid from "@/pages/design-report";
10 | import AxureScreenPanel from "@/pages/design-screen/preview";
11 | import AxureGridPanel from "@/pages/design-report/preview";
12 |
13 | class CreateRouter extends Component {
14 | render() {
15 | const { accessToken } = this.props;
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | {
27 | return !accessToken ? : ;
28 | }}
29 | />
30 |
31 |
32 | );
33 | }
34 | }
35 |
36 | export default connect((state) => ({ accessToken: state.app.accessToken }))(CreateRouter);
37 |
--------------------------------------------------------------------------------
/src/router/router-map.js:
--------------------------------------------------------------------------------
1 | import { lazy } from "react";
2 |
3 | const Dashboard = lazy(() => import("@/pages/dashboard"));
4 | const UtilBus = lazy(() => import("@/pages/dashboard/hooks"));
5 | const SketchRuler = lazy(() => import("@/pages/dashboard/sketchRuler"));
6 | const Factory = lazy(() => import("@/pages/dashboard/factory"));
7 | const IFrame = lazy(() => import("@/pages/dashboard/iframe"));
8 | const Error = lazy(() => import("@/pages/404"));
9 |
10 | export default [
11 | {
12 | path: "/dashboard",
13 | component: Dashboard
14 | },
15 | {
16 | path: "/utils",
17 | component: UtilBus
18 | },
19 | {
20 | path: "/sketch-ruler",
21 | component: SketchRuler
22 | },
23 | {
24 | path: "/factory",
25 | component: Factory
26 | },
27 | {
28 | path: "/iframe",
29 | component: IFrame
30 | },
31 | {
32 | path: "/error/404",
33 | component: Error
34 | }
35 | ];
36 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from "redux";
2 | import thunk from "redux-thunk";
3 | import reducer from "./modules";
4 |
5 | // 引入redux-persist插件进行持久化存储状态
6 | // defaults to localStorage for web. eg: session => import storage from "redux-persist/es/storage/session";
7 | import { persistStore, persistReducer } from "redux-persist";
8 | import storage from "redux-persist/es/storage";
9 |
10 | // 首先定义一个对
11 | const persistConfig = {
12 | key: "root",
13 | storage
14 | };
15 |
16 | // 使用redux-persist合并
17 | const rootReducer = persistReducer(persistConfig, reducer);
18 |
19 | const store = createStore(rootReducer, applyMiddleware(thunk));
20 | // 应用redux-persist完成数据持久化
21 | export const persistor = persistStore(store);
22 | export default store;
23 |
--------------------------------------------------------------------------------
/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | const initState = {
2 | accessToken: "",
3 | userInfo: {},
4 | routerPath: "",
5 | layouts: {},
6 | sidebarOpened: false
7 | };
8 |
9 | /**
10 | * 用户信息、路由信息
11 | * @param {*} state
12 | * @param {*} action
13 | */
14 | function app(state = initState, action) {
15 | switch (action.type) {
16 | case "ACCESS_TOKEN": {
17 | return {
18 | ...state,
19 | accessToken: action.data
20 | };
21 | }
22 | case "USER_INFO": {
23 | return {
24 | ...state,
25 | userInfo: action.data
26 | };
27 | }
28 | case "ROUTER_PATH": {
29 | return {
30 | ...state,
31 | routerPath: action.data
32 | };
33 | }
34 | case "PAGE_LAYOUTS": {
35 | return {
36 | ...state,
37 | layouts: action.data
38 | };
39 | }
40 | case "MENU_SIDEBAR": {
41 | return {
42 | ...state,
43 | sidebarOpened: !state.sidebarOpened
44 | };
45 | }
46 | default:
47 | return state;
48 | }
49 | }
50 |
51 | export default app;
52 |
--------------------------------------------------------------------------------
/src/store/modules/component.js:
--------------------------------------------------------------------------------
1 | const initState = {
2 | mode: "development",
3 | conditions: [],
4 | dependences: {},
5 | dirlldownQuery: [],
6 | datavApiEnum: [],
7 | datavSqlEnum: []
8 | };
9 |
10 | /**
11 | * 用户信息、路由信息
12 | * @param {*} state
13 | * @param {*} action
14 | */
15 | function components(state = initState, action) {
16 | switch (action.type) {
17 | case "SET_MODE": {
18 | return {
19 | ...state,
20 | mode: ["development", "preview"].includes(action.data) ? action.data : "preview"
21 | };
22 | }
23 | case "SET_DEPENDENCE": {
24 | return {
25 | ...state,
26 | dependences: action.data
27 | };
28 | }
29 | case "SET_DIRLLDOWN_QUERY": {
30 | return {
31 | ...state,
32 | dirlldownQuery: action.data
33 | };
34 | }
35 | case "DATAV_API_ENUM": {
36 | return {
37 | ...state,
38 | datavApiEnum: action.data
39 | };
40 | }
41 | case "DATAV_SQL_ENUM": {
42 | return {
43 | ...state,
44 | datavSqlEnum: action.data
45 | };
46 | }
47 | default:
48 | return state;
49 | }
50 | }
51 |
52 | export default components;
53 |
--------------------------------------------------------------------------------
/src/store/modules/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 |
3 | import app from "./app";
4 | import component from "./component";
5 |
6 | const rootReducer = combineReducers({
7 | app,
8 | component
9 | });
10 |
11 | export default rootReducer;
12 |
--------------------------------------------------------------------------------
/src/styles/account.less:
--------------------------------------------------------------------------------
1 | .gc-login {
2 | width: 100%;
3 | min-height: 100vh;
4 | background-repeat: no-repeat;
5 | background-color: var(--datav-panel-title-bg);
6 | background-position: center;
7 | overflow: hidden;
8 | &__bd {
9 | position: relative;
10 | width: 360px;
11 | max-width: 100%;
12 | padding: 180px 35px 0;
13 | margin: 0 auto;
14 | overflow: hidden;
15 | }
16 | &__title {
17 | padding: 15px 0;
18 | font-size: 20px;
19 | color: #fff;
20 | text-align: center;
21 | }
22 | &__footer {
23 | position: absolute;
24 | left: 0;
25 | right: 0;
26 | bottom: 0;
27 | padding: 1em 0;
28 | &--copyright {
29 | margin-bottom: 10px;
30 | font-size: 12px;
31 | line-height: 12px;
32 | text-align: center;
33 | color: #fff;
34 | }
35 | &--options {
36 | font-size: 12px;
37 | line-height: 12px;
38 | text-align: center;
39 | }
40 | }
41 | .ant-input-affix-wrapper {
42 | border-color: rgba(0, 199, 255, 0.4);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/styles/antd-design.less:
--------------------------------------------------------------------------------
1 | .ant-layout-footer {
2 | padding: 15px 50px 24px;
3 | }
4 |
5 | .ant-tree-list {
6 | font-size: 13px;
7 | }
8 |
9 | .silder-tree {
10 | .ant-tree .ant-tree-treenode {
11 | padding: 5px 0 7px 5px;
12 | }
13 | }
14 |
15 | .gc-design__hd--action,
16 | .gc-design__hd--setting {
17 | .ant-btn {
18 | width: 100%;
19 | height: 100%;
20 | .anticon {
21 | display: block;
22 | padding-top: 3px;
23 | font-size: 20px;
24 | & + span {
25 | margin-left: 0;
26 | }
27 | }
28 | a {
29 | display: block;
30 | padding: 0 10px;
31 | font-size: 12px;
32 | color: rgba(255, 255, 255, 0.65);
33 | .anticon {
34 | padding: 4px 0;
35 | }
36 | }
37 | }
38 | .ant-btn[disabled] {
39 | a {
40 | color: rgba(255, 255, 255, 0.3);
41 | }
42 | }
43 | }
44 |
45 | .gc-design__hd--setting {
46 | .ant-space {
47 | justify-content: flex-end;
48 | }
49 | }
50 |
51 | .gc-design__setting {
52 | .ant-tabs .ant-tabs-nav .ant-tabs-nav-wrap {
53 | width: 50px;
54 | white-space: initial;
55 | }
56 | }
57 |
58 | .gc-design {
59 | .ant-drawer-wrapper-body {
60 | height: auto;
61 | }
62 | }
63 |
64 | .field-flex__control {
65 | .ant-radio-group .ant-radio-wrapper {
66 | display: block;
67 | padding: 3px 0;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/styles/layout.less:
--------------------------------------------------------------------------------
1 | .gc-collapsed {
2 | font-size: 20px;
3 | line-height: 64px;
4 | padding: 0 20px;
5 | cursor: pointer;
6 | transition: color 0.3s;
7 |
8 | &:hover {
9 | color: var(--datav-main-hover-color);
10 | }
11 | }
12 |
13 | .gc-logo {
14 | height: 32px;
15 | background: var(--datav-border-color);
16 | margin: 16px;
17 | }
18 |
19 | .gc-layout {
20 | position: relative;
21 | width: 100%;
22 | height: 100vh;
23 | display: flex;
24 | flex-direction: column;
25 |
26 | &__hd {
27 | padding: 0;
28 | display: flex;
29 | align-items: center;
30 | .gc-breadcrumb {
31 | flex: 1;
32 | }
33 | }
34 |
35 | &__bd {
36 | position: relative;
37 | -webkit-box-flex: 1;
38 | -ms-flex-positive: 1;
39 | flex-grow: 1;
40 | overflow: auto;
41 | }
42 |
43 | .user-avatar {
44 | margin: 0 15px;
45 | }
46 |
47 | .user-signout {
48 | display: inline-block;
49 | margin-right: 15px;
50 | height: 75%;
51 | }
52 | }
53 |
54 | .back-top {
55 | right: 50px;
56 |
57 | .ant-back-top-content {
58 | background: var(--datav-main-click-color);
59 | opacity: 1;
60 | transition: all 0.3s;
61 | box-shadow: 0 0 15px 1px rgba(69, 65, 78, 0.1);
62 | }
63 | }
64 |
65 | .sidebar-container {
66 | user-select: none;
67 | position: relative;
68 | z-index: 2;
69 |
70 | .ant-menu-inline-collapsed {
71 | width: 60px;
72 |
73 | .ant-menu-submenu .ant-menu-submenu-title,
74 | .ant-menu-item {
75 | padding: 0 20px !important;
76 |
77 | .anticon {
78 | font-size: 16px;
79 | }
80 | }
81 | }
82 | }
83 |
84 | .gc-page {
85 | position: relative;
86 | padding: 12px 15px 0;
87 | width: 100%;
88 | height: 100%;
89 | .dashboard-banner {
90 | position: absolute;
91 | top: 15px;
92 | left: 15px;
93 | right: 0;
94 | bottom: 0;
95 | background-color: #23242d;
96 | -webkit-filter: brightness(0.8);
97 | filter: brightness(0.8);
98 | video {
99 | object-fit: cover;
100 | }
101 | }
102 | .dashboard-typography {
103 | position: relative;
104 | padding: 25px 30px;
105 | }
106 | }
107 |
108 | .gc-exp {
109 | background: var(--datav-panel-item-bg);
110 | }
111 |
112 | .canvas-size {
113 | position: absolute;
114 | right: 10px;
115 | bottom: -55px;
116 | font-size: 12px;
117 | }
118 |
--------------------------------------------------------------------------------
/src/utils/RAFrame.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | // requestAnimationFrame、cancelAnimationFrame 兼容方法
4 | let lastTime = 0;
5 | const vendors = ["webkit", "moz", "ms", "o"];
6 |
7 | let requestAnimationFrame = window.requestAnimationFrame;
8 | let cancelAnimationFrame = window.cancelAnimationFrame;
9 | for (let x = 0; x < vendors.length; x++) {
10 | if (requestAnimationFrame && cancelAnimationFrame) {
11 | break;
12 | } else {
13 | requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"];
14 | cancelAnimationFrame =
15 | window[vendors[x] + "CancelAnimationFrame"] || window[vendors[x] + "CancelRequestAnimationFrame"];
16 | }
17 | }
18 | if (!requestAnimationFrame) {
19 | requestAnimationFrame = (callback) => {
20 | const currTime = new Date().getTime();
21 | const timeToCall = Math.max(0, 16 - (currTime - lastTime));
22 | const id = window.setTimeout(() => {
23 | callback(currTime + timeToCall);
24 | }, timeToCall);
25 | lastTime = currTime + timeToCall;
26 | return id;
27 | };
28 | }
29 | if (!cancelAnimationFrame) {
30 | cancelAnimationFrame = (id) => {
31 | clearTimeout(id);
32 | };
33 | }
34 |
35 | export { requestAnimationFrame, cancelAnimationFrame };
36 |
--------------------------------------------------------------------------------
/src/utils/http.js:
--------------------------------------------------------------------------------
1 | /**
2 | * request 网络请求工具,创建axios实例对象
3 | * send cookies when cross-domain requests
4 | */
5 |
6 | import axios from "axios";
7 | import { notification } from "antd";
8 | import store from "@/store";
9 | import { isEmpty } from "@/utils/helper";
10 |
11 | const codeMessage = {
12 | 200: "服务器成功返回请求的数据。",
13 | 201: "新建或修改数据成功。",
14 | 202: "一个请求已经进入后台排队(异步任务)。",
15 | 204: "删除数据成功。",
16 | 400: "发出的请求有错误,服务器没有进行新建或修改数据的操作。",
17 | 401: "用户没有权限(令牌、用户名、密码错误)。",
18 | 403: "用户得到授权,但是访问是被禁止的。",
19 | 404: "发出的请求针对的是不存在的记录,服务器没有进行操作。",
20 | 406: "请求的格式不可得。",
21 | 410: "请求的资源被永久删除,且不会再得到的。",
22 | 422: "当创建一个对象时,发生一个验证错误。",
23 | 500: "服务器发生错误,请检查服务器。",
24 | 502: "网关错误。",
25 | 503: "服务不可用,服务器暂时过载或维护。",
26 | 504: "网关超时。"
27 | };
28 |
29 | /**
30 | * 异常处理程序
31 | */
32 | const errorHandler = (error) => {
33 | const { response } = error;
34 | if (response && response.status) {
35 | const errorText = codeMessage[response.status] || response.statusText;
36 | const { status, url } = response;
37 | notification.error({
38 | message: `请求错误 ${status}: ${url}`,
39 | description: errorText
40 | });
41 | } else if (!response) {
42 | notification.error({
43 | description: "您的网络发生异常,无法连接服务器",
44 | message: "网络异常"
45 | });
46 | }
47 | return response;
48 | };
49 |
50 | const fetch = axios.create({
51 | baseURL: process.env.REACT_APP_API,
52 | timeout: 15 * 1000, // 请求超时时间
53 | retry: 4,
54 | retryDelay: 500,
55 | withCredentials: true
56 | });
57 |
58 | // request拦截器 请求处理
59 | fetch.interceptors.request.use(
60 | (config) => {
61 | // Do something before request is sent
62 | config.headers = {
63 | Accept: "application/json, text/plain, */*",
64 | Authorization: "Basic bmVpemhlbjpuZWl6aGVu",
65 | "Content-type": "application/json;charset=UTF-8"
66 | };
67 |
68 | const { accessToken } = store.getState().app;
69 | if (isEmpty(accessToken)) {
70 | // 让每个请求携带token -- ['AUTH_TOKEN']为自定义key
71 | config.headers["Authorization"] = `Bearer ${accessToken}`;
72 | }
73 |
74 | return config;
75 | },
76 | (error) => {
77 | return Promise.reject(error);
78 | }
79 | );
80 |
81 | // respone拦截器 响应处理
82 | fetch.interceptors.response.use(
83 | (response) => {
84 | // 根据自身业务定制化提示语
85 | response.data.code === 1 &&
86 | notification.error({
87 | message: "系统提示",
88 | description: response.data.msg
89 | });
90 |
91 | if (
92 | response.config.responseType === "blob" ||
93 | response.config.responseType === "arraybuffer" ||
94 | response.data instanceof Blob ||
95 | response.data instanceof ArrayBuffer
96 | ) {
97 | return response;
98 | }
99 | return response.data;
100 | },
101 | (error) => {
102 | // Do something with request error
103 | errorHandler(error);
104 | return Promise.resolve(error);
105 | }
106 | );
107 |
108 | export default fetch;
109 |
--------------------------------------------------------------------------------
/src/utils/storage.js:
--------------------------------------------------------------------------------
1 | export default {
2 | setCookie(name, value, days) {
3 | var Days = days || 7;
4 | var exp = new Date();
5 | if (Days && !isNaN(parseInt(Days))) {
6 | exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
7 | document.cookie = name + "=" + escape(value) + ";path=/;expires=" + exp.toGMTString();
8 | }
9 | },
10 | getCookie(name) {
11 | var arrStr = document.cookie.split(";");
12 | for (var i = 0; i < arrStr.length; i++) {
13 | var temp = arrStr[i].split("=");
14 | if (temp[0].trim() === name) return unescape(temp[1]);
15 | }
16 | },
17 | delCookie(name) {
18 | document.cookie = name + "=;expires=" + new Date(0).toGMTString();
19 | },
20 | getSession(name) {
21 | if (!name) return;
22 | return window.sessionStorage.getItem(name);
23 | },
24 | setSession(name, content) {
25 | if (!name) return;
26 | if (typeof content !== "string") {
27 | content = JSON.stringify(content);
28 | }
29 | window.sessionStorage.setItem(name, content);
30 | },
31 | removeSession(name) {
32 | if (!name) return;
33 | window.sessionStorage.removeItem(name);
34 | },
35 | getLocal(name) {
36 | if (!name) return;
37 | return window.localStorage.getItem(name);
38 | },
39 | setLocal(name, content) {
40 | if (!name) return;
41 | if (typeof content !== "string") {
42 | content = JSON.stringify(content);
43 | }
44 | window.localStorage.setItem(name, content);
45 | },
46 | removeLocal(name) {
47 | if (!name) return;
48 | window.localStorage.removeItem(name);
49 | },
50 | /**
51 | * Clear all localStorage and sessionStorage
52 | */
53 | clearAll() {
54 | window.localStorage.clear();
55 | window.sessionStorage.clear();
56 | }
57 | };
58 |
--------------------------------------------------------------------------------