Compare commits

..

148 Commits

Author SHA1 Message Date
奔跑的面条
fa84ee778c !88 feat-unify-test 试验后端工厂和事件机制
Merge pull request !88 from Kenjjj201377/feat-unify-test
2022-10-25 02:23:17 +00:00
金建
0eb75fbba4 fix: 整理文档 2022-10-25 00:05:52 +08:00
金建
6858f5bd39 fix: 保存进程移除 2022-10-24 23:37:41 +08:00
jinjian
b9f6ed95ab feat: 代码整合 2022-10-24 17:21:58 +08:00
奔跑的面条
8039213380 build: 修改版本号为2.1.0 2022-10-15 17:28:23 +08:00
奔跑的面条
c5a704767d fix: 修改版本号到2.0.10 2022-10-15 17:22:06 +08:00
奔跑的面条
20be7f2e45 Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-10-15 17:20:05 +08:00
奔跑的面条
d43823794d fix: 修复导入组件数据会错乱的问题 2022-10-09 16:39:10 +08:00
奔跑的面条
5e09105a70 Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-10-09 09:29:40 +08:00
奔跑的面条
7d2a98ab85 build: 修改正确版本到 2.0.8 2022-10-08 21:27:21 +08:00
奔跑的面条
2d302bb468 build: 升级版本到 2.0.9 2022-10-08 21:09:47 +08:00
奔跑的面条
789328b02e Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-10-08 21:06:06 +08:00
奔跑的面条
b93a2a516a feat: 合并1.1.1 2022-10-08 21:04:41 +08:00
奔跑的面条
c8662537ca feat: 合并1.1.1,升级版本到2.0.8 2022-09-27 20:28:47 +08:00
奔跑的面条
b083bd9724 style: 去除无用代码,设置默认值 2022-09-27 19:53:55 +08:00
奔跑的面条
5fd6870cfe perf: 优化用户体验,完成接口配置后调用保存接口 2022-09-27 10:33:30 +08:00
奔跑的面条
9004a55b8c Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-09-22 14:15:18 +08:00
奔跑的面条
c349e6384e Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-09-21 19:56:56 +08:00
奔跑的面条
4e298efaa0 Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-09-19 20:46:53 +08:00
奔跑的面条
c7cbb9b72a Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-09-19 20:14:40 +08:00
奔跑的面条
bd5e06350d perf: 优化滤镜模糊问题,默认不开启 2022-09-19 17:51:12 +08:00
奔跑的面条
c311dd08e3 fix: 修改请求接口 token 名称 2022-09-18 17:45:59 +08:00
奔跑的面条
504a1f4703 build: 升级版本到2.0.7 2022-09-18 17:23:16 +08:00
奔跑的面条
a67cc9f876 !47 修复在编辑项目之前加载数据异常,自动保存导致项目数据清空问题。
Merge pull request !47 from 秋名山路霸/master-fetch
2022-09-18 07:53:47 +00:00
秋名山路霸
e0d2e8031d fix: 修复在编辑项目之前加载数据异常,自动保存导致项目数据清空问题。 2022-09-18 11:56:26 +08:00
奔跑的面条
bcec26374f build: 升级依赖,锁定TS 版本 2022-09-12 01:20:27 +08:00
奔跑的面条
a5981a4387 feat: 升级版本到 2.0.6 2022-09-12 00:49:22 +08:00
奔跑的面条
0d26dce512 fix: 合并1.0.9 2022-09-12 00:31:52 +08:00
奔跑的面条
6359ec15b3 feat: 新增移动撤回 2022-08-30 19:14:52 +08:00
奔跑的面条
32ca138e6a build: 升级版本到 2.0.5 2022-08-30 15:04:29 +08:00
奔跑的面条
171e0aa408 perf: 完善首页预览功能 2022-08-30 15:03:25 +08:00
奔跑的面条
52009a415b Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-08-30 12:19:57 +08:00
奔跑的面条
da809f13b3 fix: 尝试解决背景图片无法截图的问题 2022-08-29 21:35:51 +08:00
奔跑的面条
801aa1f82f update LICENSE.
Signed-off-by: 奔跑的面条 <1262327911@qq.com>
2022-08-29 10:13:37 +00:00
奔跑的面条
72c26b71f3 perf: 优化参考线的展示方式 2022-08-21 14:52:30 +08:00
奔跑的面条
3b8181ae10 Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-08-21 14:49:32 +08:00
奔跑的面条
c2a9642393 Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-08-19 14:46:49 +08:00
奔跑的面条
3c04f81147 fix: 补充代码合并丢失的枚举 2022-08-19 11:43:23 +08:00
奔跑的面条
ec28ccf09c perf: 优化500错误页重定向 2022-08-19 11:34:38 +08:00
奔跑的面条
fa34300401 feat: 合并多选功能,解决冲突,升级版本到2.0.4 2022-08-19 10:44:44 +08:00
奔跑的面条
f506ccd2e4 Merge branch 'dev' into master-fetch 2022-08-05 08:42:22 +08:00
奔跑的面条
f379f7652c Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-07-26 16:12:31 +08:00
奔跑的面条
e8760c81ac Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-07-21 11:46:54 +08:00
奔跑的面条
a9bb294f60 feat: 合并 1.0.6 版本代码 2022-07-21 11:03:04 +08:00
奔跑的面条
0b5f35752b Merge branch 'dev' into master-fetch 2022-07-08 18:12:33 +08:00
奔跑的面条
7322817cb5 fix: 合并编辑功能的修改 2022-07-07 19:54:53 +08:00
奔跑的面条
437ad1bd9f Merge branch 'dev' into master-fetch 2022-07-07 13:14:53 +08:00
奔跑的面条
cd20e68480 fix: 补充合并代码丢失的icon图标 2022-07-06 22:12:24 +08:00
奔跑的面条
35968475b9 build: 合并dev 1.0.5 版本, 修改fetch分支为2.0.2 2022-07-06 21:58:28 +08:00
奔跑的面条
78eb3ccc3f Merge branch 'dev' into master-fetch 2022-06-27 21:42:24 +08:00
奔跑的面条
7cccbabca4 Merge branch 'dev' into master-fetch 2022-06-27 21:38:27 +08:00
奔跑的面条
cdccfa3e12 fix: 修改合并冲突错误的代码 2022-06-27 20:42:17 +08:00
奔跑的面条
e8c5455748 perf: 合并dev分支,解决组件无法更新数据的问题 2022-06-27 20:37:26 +08:00
奔跑的面条
8e40416e72 style: 去除多余代码 2022-06-24 11:44:26 +08:00
奔跑的面条
d058361e01 docs: 修改文档 2022-06-23 09:53:01 +08:00
奔跑的面条
2491f7ff57 feat: 新增保存按钮 2022-06-22 18:50:15 +08:00
奔跑的面条
c5908f6d92 build: 升级依赖 2022-06-20 15:13:50 +08:00
奔跑的面条
7e64d30258 build: 合并dev分支 2022-06-20 15:11:13 +08:00
奔跑的面条
437818abce feat: 新增排名列表字体大小控制功能 2022-06-17 14:13:14 +08:00
奔跑的面条
5197eb9292 build: 修改版本到 2.0.1 2022-06-16 10:48:42 +08:00
奔跑的面条
3cf01f0c51 fix: 解决打包之后无法加载页面的问题 2022-06-15 17:54:49 +08:00
奔跑的面条
db815661e8 feat: 新增 preview 模式,修改打包后路径指向 2022-06-15 17:48:16 +08:00
奔跑的面条
6edbb7c8a7 feat: 新增commitlint 2022-06-15 17:18:39 +08:00
奔跑的面条
799c445c4b docs: 更新文档 2022-06-15 17:15:29 +08:00
奔跑的面条
70fbf5de3e doc: 文档更新 2022-06-15 17:14:27 +08:00
奔跑的面条
73c7697443 feat: 新增渐变文本组件 2022-06-15 16:20:25 +08:00
奔跑的面条
8092edab81 build:升级依赖 2022-06-15 16:11:58 +08:00
奔跑的面条
ef4f45ddb0 fix: 解决npm,yarn 安装依赖报错的问题 2022-06-15 16:08:59 +08:00
奔跑的面条
3e969e4ae5 fix: 补充丢失的图片 2022-06-14 12:46:07 +08:00
奔跑的面条
bcd1dfd7bd feat:新增数字滚动组件动态获取数据功能 2022-06-14 12:31:46 +08:00
奔跑的面条
c1dfd78d18 feat: 新增表格滚动组件 2022-06-14 12:31:20 +08:00
奔跑的面条
39aa1645c9 fix:解决边框04展示不全的bug 2022-06-14 12:30:29 +08:00
奔跑的面条
d3afea8c5e feat:新增水球图设置项 2022-06-13 17:29:39 +08:00
奔跑的面条
2499cbdd44 fix: 修改列表页展示问题 2022-06-13 14:28:58 +08:00
奔跑的面条
3cc3714e94 feat: 新增环形图,新增NaiveUI-进度组件 2022-06-13 13:18:30 +08:00
奔跑的面条
be0aa6d099 feat: 新增进度条组件 2022-06-12 18:47:47 +08:00
奔跑的面条
34d27c1004 fix: 解决缩放比例展示不全的问题 2022-06-12 18:45:31 +08:00
奔跑的面条
85f3b4e9e3 chore: 优化拖拽锚点 2022-06-11 15:15:38 +08:00
奔跑的面条
0fb9a79df9 fix:修改请求地址为null时引起的异常bug 2022-06-11 14:37:22 +08:00
奔跑的面条
6b805fbdfb chore: 优化发布弹窗 2022-06-11 14:23:16 +08:00
奔跑的面条
abe76aeb59 build: 依赖基本升级 2022-06-11 14:15:28 +08:00
奔跑的面条
5f49bc1aa8 fix: 修改双折线图X轴无法变更的问题 2022-06-11 14:13:32 +08:00
奔跑的面条
71807be01d fix: 解决项目列表信息栏会换行的问题 2022-06-09 08:49:07 +08:00
奔跑的面条
e74f796203 fix:解决截图有白边的问题 2022-06-09 08:48:28 +08:00
奔跑的面条
041d7da9d8 fix: 修改复制失败的提示类型错误的问题 2022-06-06 10:43:14 +08:00
奔跑的面条
a99d949b29 chore: 新增路由白名单 2022-06-06 10:42:55 +08:00
奔跑的面条
c2a9e2a469 chore:优化搜索结果列表UI 2022-06-05 11:41:57 +08:00
奔跑的面条
80b05db764 chore:优化 dialog 的全局封装代码 2022-06-04 16:25:37 +08:00
奔跑的面条
340cab11b7 build:修改 fetch 版本号 2022-06-04 15:44:26 +08:00
奔跑的面条
712233c215 chore: 优化页面 UI 2022-06-03 20:21:35 +08:00
奔跑的面条
b9b915e913 fix: 修改自动复制粘贴的问题 2022-06-03 18:53:37 +08:00
奔跑的面条
044143571f fix: 新增发布页面处理 2022-06-03 14:48:58 +08:00
奔跑的面条
06fe805736 chore: 优化了标题展示和大小样式 2022-06-03 11:19:29 +08:00
奔跑的面条
077881e499 type: 修改类型错误 2022-06-01 23:00:28 +08:00
奔跑的面条
f655a57e61 fix:处理列表页标题过长的展示问题 2022-06-01 22:58:22 +08:00
奔跑的面条
0b04bf4929 type: 定义全局返回值类型 2022-06-01 22:41:11 +08:00
奔跑的面条
2fcd3b1132 build: 修改版本号 0.0.9 2022-06-01 22:28:46 +08:00
奔跑的面条
568fd6c105 chore:优化了路由写法,修改了错误页面的展示,新增未发布提示页面 2022-06-01 22:20:05 +08:00
奔跑的面条
221351ec11 feat: 新增动态预览功能 2022-06-01 22:19:03 +08:00
奔跑的面条
6ab34a0996 fix: 修改组件注册会报错的问题 2022-06-01 22:14:33 +08:00
奔跑的面条
6b551ae68b build:升级依赖包 2022-06-01 22:13:49 +08:00
奔跑的面条
2917cde2e5 fix: 解决列表图片展示缓存问题 2022-06-01 20:16:38 +08:00
奔跑的面条
294a4a6cdf fix: 解决获取数据,但是配置模块不完整的问题 2022-06-01 19:01:05 +08:00
奔跑的面条
1e678b7492 build: 修改请求地址 2022-05-31 11:19:17 +08:00
奔跑的面条
2ceca7287f fix: 修改oss接口不会动态更改的问题 2022-05-31 11:18:34 +08:00
奔跑的面条
7f4dd5295b chore:修改请求地址 2022-05-29 16:04:07 +08:00
奔跑的面条
ffd628fa82 docs: 修改文档说明 2022-05-29 15:23:04 +08:00
奔跑的面条
d639e445eb feat: 新增背景图文件上传保存 2022-05-29 14:54:35 +08:00
奔跑的面条
b263681dda chore: 去除生成预览图时的标尺 2022-05-28 17:58:07 +08:00
奔跑的面条
e16413b570 feat: 新增首页预览图展示 2022-05-28 16:39:27 +08:00
奔跑的面条
63b5186c3e Merge branch 'dev' into master-fetch 2022-05-28 15:58:06 +08:00
奔跑的面条
ba20316761 fix: 修改自动保存预览图无法存储的问题 2022-05-28 15:46:07 +08:00
奔跑的面条
437dd1c411 branch: 合并锚点样式修改 2022-05-28 12:46:32 +08:00
奔跑的面条
d0d5f5b77d feat: 保存预览图 2022-05-28 11:50:17 +08:00
奔跑的面条
efd9228cc9 fix: 解决打包后无法发送请求的bug 2022-05-28 00:32:32 +08:00
奔跑的面条
ebd6132385 feat: 新增上传文件接口 2022-05-27 20:09:48 +08:00
奔跑的面条
9098443c83 chore: 修改项目信息结构 2022-05-27 11:49:25 +08:00
奔跑的面条
423890a4df feat: 新增项目信息修改功能 2022-05-26 01:01:59 +08:00
奔跑的面条
fb2edeb7d2 fix: 修改导入组件id会重复的问题 2022-05-25 23:00:36 +08:00
奔跑的面条
b861587f01 feat: 新增快捷键展示 2022-05-24 18:17:41 +08:00
奔跑的面条
c3738fab45 faet: 新增保存快捷键 2022-05-24 18:16:33 +08:00
奔跑的面条
24fba75f28 feat: 新增自动同步功能 2022-05-24 17:42:49 +08:00
奔跑的面条
00b6b63e1e feat: 新增数据保存接口 2022-05-24 15:05:51 +08:00
奔跑的面条
5dab8fa7d9 Merge branch 'dev' into master-fetch 2022-05-24 12:28:14 +08:00
奔跑的面条
9a5d71fb5c feat: 新增获取项目数据功能,新增同步数据功能 2022-05-23 23:50:35 +08:00
奔跑的面条
c930efba0c Merge branch 'dev' into master-fetch 2022-05-23 16:06:13 +08:00
奔跑的面条
ff7c820b1f Merge branch 'dev' into master-fetch 2022-05-22 23:27:39 +08:00
奔跑的面条
4252725d9d feat: 新增获取项目数据接口 2022-05-22 22:11:56 +08:00
奔跑的面条
deeb3a472c feat:新增发布和取消发布接口 2022-05-22 16:38:22 +08:00
奔跑的面条
763173de44 style: 调整代码格式,去除多余代码 2022-05-22 15:39:30 +08:00
奔跑的面条
f46e6ad8c8 feat: 新增删除接口 2022-05-22 15:25:07 +08:00
奔跑的面条
09b31547e1 feat: 新增首页列表接口 2022-05-22 15:06:45 +08:00
奔跑的面条
dee2ff8dee Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-05-22 14:09:38 +08:00
奔跑的面条
7f315b95ce feat: 新增项目列表接口 2022-05-22 14:05:57 +08:00
奔跑的面条
093e7d1edb Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-05-22 13:36:30 +08:00
奔跑的面条
a4e18f8893 fix: 修改新建项目id错误问题 2022-05-21 21:04:10 +08:00
奔跑的面条
c754a36ee5 fix: 修改i18n错误提示 2022-05-21 19:46:44 +08:00
奔跑的面条
a07d4daed5 chore: 修改提示内容 2022-05-21 18:04:52 +08:00
奔跑的面条
39023832e3 feat: 新增创建接口,修改i8n部分内容 2022-05-21 18:03:15 +08:00
奔跑的面条
5b8dda60bd feat: 新增退出登录接口,新增全局接口封装,修改登录接口内容 2022-05-21 17:31:01 +08:00
奔跑的面条
f7ade54e93 Merge branch 'master' of https://gitee.com/MTrun/go-view into master-fetch 2022-05-21 14:01:53 +08:00
奔跑的面条
4ae7e9dd2e Merge branch 'dev' into master-fetch 2022-05-21 13:44:25 +08:00
奔跑的面条
7e237b508a feat: 新增登录接口请求 2022-05-20 16:12:27 +08:00
奔跑的面条
28bb82e579 fix: 修改plop的问题 2022-05-20 16:12:09 +08:00
奔跑的面条
bcabcb0831 Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-05-20 10:25:46 +08:00
奔跑的面条
c24b03f3da update README.md. 2022-05-19 01:56:30 +00:00
奔跑的面条
63dd03e755 update README.md. 2022-05-16 10:38:39 +00:00
奔跑的面条
f3f1f57b01 update README.md. 2022-05-16 04:23:10 +00:00
516 changed files with 13266 additions and 22920 deletions

12
.env
View File

@@ -1,14 +1,8 @@
# port
VITE_DEV_PORT = '8001'
VITE_DEV_PORT = '8080'
# development path
VITE_DEV_PATH = '/'
VITE_DEV_PATH = 'http://1.117.240.165:8080'
# production path
VITE_PRO_PATH = '/'
# spa-title
VITE_GLOB_APP_TITLE = GoView
# spa shortname
VITE_GLOB_APP_SHORT_NAME = GoView
VITE_PRO_PATH = 'http://1.117.240.165:8080'

2
.gitignore vendored
View File

@@ -4,5 +4,3 @@ dist
dist-ssr
*.local
.vscode
.idea
pnpm-lock

View File

@@ -1,51 +0,0 @@
version: '1.0'
name: branch-pipeline
displayName: BranchPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@nodejs
name: build_nodejs
displayName: Nodejs 构建
# 支持8.16.2、10.17.0、12.16.1、14.16.0、15.12.0五个版本
nodeVersion: 14.16.0
# 构建命令:安装依赖 -> 清除上次打包产物残留 -> 执行构建 【请根据项目实际产出进行填写】
commands:
- npm install && rm -rf ./dist && npm run build
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
path:
- ./dist
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_nodejs
- stage:
name: release
displayName: 发布
steps:
- step: publish@release_artifacts
name: publish_release_artifacts
displayName: '发布'
# 上游上传制品任务的产出
dependArtifact: output
# 发布制品版本号
version: '1.0.0.0'
# 是否开启版本号自增,默认开启
autoIncrement: true
triggers:
push:
branches:
exclude:
- master
include:
- .*

View File

@@ -1,49 +0,0 @@
version: '1.0'
name: master-pipeline
displayName: MasterPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@nodejs
name: build_nodejs
displayName: Nodejs 构建
# 支持8.16.2、10.17.0、12.16.1、14.16.0、15.12.0五个版本
nodeVersion: 14.16.0
# 构建命令:安装依赖 -> 清除上次打包产物残留 -> 执行构建 【请根据项目实际产出进行填写】
commands:
- npm install && rm -rf ./dist && npm run build
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
path:
- ./dist
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_nodejs
- stage:
name: release
displayName: 发布
steps:
- step: publish@release_artifacts
name: publish_release_artifacts
displayName: '发布'
# 上游上传制品任务的产出
dependArtifact: output
# 发布制品版本号
version: '1.0.0.0'
# 是否开启版本号自增,默认开启
autoIncrement: true
triggers:
push:
branches:
include:
- master

View File

@@ -1,36 +0,0 @@
version: '1.0'
name: pr-pipeline
displayName: PRPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@nodejs
name: build_nodejs
displayName: Nodejs 构建
# 支持8.16.2、10.17.0、12.16.1、14.16.0、15.12.0五个版本
nodeVersion: 14.16.0
# 构建命令:安装依赖 -> 清除上次打包产物残留 -> 执行构建 【请根据项目实际产出进行填写】
commands:
- npm install && rm -rf ./dist && npm run build
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
path:
- ./dist
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_nodejs
triggers:
pr:
branches:
include:
- master

201
README.md
View File

@@ -2,150 +2,73 @@
![logo](readme/logo-t-y.png)
GoView 是一个高效的拖拽式低代码数据可视化开发平台,将图表或页面元素封装为基础组件,无需编写代码即可制作数据大屏,减少心智负担。
**`master-fetch` 分支是带有后端接口请求的分支**
### 😶 纯 **前端** 分支: **`master`**
### 👻 携带 **后端** 请求分支: **`master-fetch`**
### feat-unify-test 分支目标
+ 实现 backend 后端工厂
将后端业务逻辑集中到 backend 了,控制 BackEndFactory 就可以适配不同的后端。
伪代码如下:
export const BackEndFactory = ():IBackend=>{
switch(项目后端配置){
case "无数据库":
return new MockBackend() // 等同: -master ,没有存储
case "indexdb":
return new IndexDbBackend() // 这次开发的,用 indexdb 做测试
case "java":
return new JavaBackend() // 等同: -fetch 没 java 环境,还没做
case "python":
return new PythonBackend() // 自定义开发的后端
。。。 其他 oss 、云平台的后端 。。。
}}
意义:
1 unify 统一 -fetch 和 master 分支,消除分支之间的差异。
2 方便接入不同的自定义后端平台。
3 前端存储功能让测试工作更加方便
### 📚 GoView **文档** 地址:[http://www.mtruning.club:81/](http://www.mtruning.club:81/)
+ 完善事件处理机制
在事件中修改图表配置
在事件中修改图表数据
在事件中调用图表 exposed 函数
数据驱动界面
项目纯前端-Demo 地址:[https://www.mtruning.club](https://www.mtruning.club)
### 试验功能1Backend 后端工厂
+ 对比 -fetch 分支,梳理后端逻辑到 backend 目录的 ibackend 接口
+ 登录 - login
+ 登出 - logout
+ 预览token 注入或单点登陆 - checkToken
+ 显示项目列表和分页 - projectList
+ 保存、发布、修改名称 - updateProject
+ 复制项目 - copyProject
+ 图表内的图片上传 - uploadFile
+ 上传图片显示处理 - getFileUrl
+ IndexDbBackend 用indexdb浏览器数据库实现了 project 相关所有功能。
+ Todo: 统一后端错误处理
+ Todo开发 javabackend适配现有的后端
项目带后端-Demo 地址:[后端 Demo 地址](http://1.117.240.165:8080/goview/#/login)
### 试验功能2事件处理机制
+ 实现最常用的互动:找到图表元素、显示或隐藏、修改数据
+ 核心代码useLifeHandler.hook.ts
+ 在事件代码中通过 runtime 实现运行时刻的图表管理,提供基础函数:
+ selectComponents 选择多个图表
+ selectOneComponent 选择一个图表
+ getChartConfig 读取图表
+ setChartConfig 设置图表
+ callExposed 调用图表 exposed 的函数
+ 以下例子可以在点击事件中加入代码并预览,测试效果。
文档-在线地址:[http://www.mtruning.club:81/](http://www.mtruning.club:81/)
+ 例子1 切换显示名称为 饼图 和 柱状图 的图表:
const range = runtime.fn.selectComponents("饼图 柱状图")
const h = runtime.fn.getChartConfig(range, "hide")
runtime.fn.setChartConfig(range, "hide", !h)
文档-源码地址:[https://gitee.com/MTrun/go-view-doc](https://gitee.com/MTrun/go-view-doc)
+ 例子2 修改一个名称 柱状图001 组件id 2wolqibrx3c000 的图表数据,以下两句等效
runtime.fn.setChartConfig("柱状图001", "dataset", {"dimensions":["product","data1","data2"],"source":[{"product":"Mon","data1":120,"data2":130}]})
runtime.fn.setChartConfig("#2wolqibrx3c000", "dataset", {"dimensions":["product","data1","data2"],"source":[{"product":"Mon","data1":120,"data2":230}]})
### 🤯 后端项目
后端项目 gitee 地址:[https://gitee.com/MTrun/go-view-serve](https://gitee.com/MTrun/go-view-serve)
接口说明地址:[https://docs.apipost.cn/preview/5aa85d10a59d66ce/ddb813732007ad2b?target_id=84dbc5b0-158f-4bcb-8f74-793ac604ada3#3e053622-1e76-43f9-a039-756aee822dbb](https://docs.apipost.cn/preview/5aa85d10a59d66ce/ddb813732007ad2b?target_id=84dbc5b0-158f-4bcb-8f74-793ac604ada3#3e053622-1e76-43f9-a039-756aee822dbb)
技术点:
- 框架:基于 `Vue3` 框架编写,使用 `hooks` 写法抽离部分逻辑,使代码结构更加清晰;
- 类型:使用 `TypeScript` 进行类型约束,减少未知错误发生概率,可以大胆修改逻辑内容;
- 性能:多处性能优化,使用页面懒加载、组件动态注册、数据滚动加载等方式,提升页面渲染速度;
- 存储:拥有本地记忆,部分配置项采用 `storage` 存储本地,提升使用体验;
- 封装:项目进行了详细的工具类封装如:路由、存储、加/解密、文件处理、主题、NaiveUI 全局方法、组件等
工作台:
![项目截图](readme/go-view-canvas.png)
请求配置:
![项目截图](readme/go-view-fetch.png)
数据过滤:
![项目截图](readme/go-view-filter.png)
主题色:
![项目截图](readme/go-view-color.png)
主要技术栈为:
| 名称 | 版本 | 名称 | 版本 |
| ------------------- | ----- | ----------- | ------ |
| Vue | 3.2.x | TypeScript4 | 4.6.x |
| Vite | 4.2.x | NaiveUI | 2.34.x |
| ECharts | 5.3.x | Pinia | 2.0.x |
| 详见 `package.json` | 😁 | 🥰 | 🤗 |
开发环境:
| 名称 | 版本 | 名称 | 版本 |
| ---- | ------- | ------- | ----- |
| node | 16.16.x | npm | 8.5.x |
| pnpm | 7.1.x | windows | 11 |
已完成图表:
| 分类 | 名称 | 名称 | 名称 |
| ------ | ---------------- | ---------------- | -------- |
| 图表 | 柱状图 | 横向柱状图 | 折线图 |
| \* | 单/多 折线面积图 | 饼图 | 水球图 |
| \* | 环形图 | NaiveUI 多种进度 | 🤠 |
| 信息 | 文字 | 图片 | 😶 |
| 列表 | 滚动排名列表 | 滚动表格 | 🤓 |
| 小组件 | 边框-01~13 | 装饰-01~05 | 数字翻牌 |
## 浏览器支持
开发和测试平台均在 `Google` 和最新版 `EDGE` 上完成,暂未测试 `IE11` 等其它浏览器,如有需求请自行测试与兼容。
## 安装
本项目采用` pnpm` 进行包管理
```shell
#建议使用 nrm 切换到淘宝源 https://registry.npmmirror.com/
#pnpm
pnpm install
#yarn
yarn install
#npm
npm install
```
## 启动
```shell
#pnpm
pnpm dev
# npm
npm run dev
#yarn
yarn dev
#Makefile
make dev
```
## 编译
```shell
#pnpm
pnpm run build
# npm
npm run build
#yarn
yarn run build
#Makefile
make dist
```
## 代码提交
- feat: 新功能
- fix: 修复 Bug
- docs: 文档修改
- perf: 性能优化
- revert: 版本回退
- ci: CICD 集成相关
- test: 添加测试代码
- refactor: 代码重构
- build: 影响项目构建或依赖修改
- style: 不影响程序逻辑的代码修改
- chore: 不属于以上类型的其他类型(日常事务)
## 交流
QQ 群1030129384
![QQ群](readme/go-view-qq.png)
![渲染海报](readme/logo-poster.png)
+ 例子3 找到一个组并隐藏
const c = runtime.fn.selectOneComponent("分组")
if(c){
console.log(runtime.fn.getChartConfig(c, "isGroup" ))
runtime.fn.setChartConfig(c, "hide", true)
}

View File

@@ -1,4 +1,3 @@
import path from 'path'
export const OUTPUT_DIR = 'dist'
// monaco-editor 路径
@@ -7,7 +6,7 @@ export const prefix = `monaco-editor/esm/vs`
// chunk 警告大小
export const chunkSizeWarningLimit = 2000
// 禁用 brotliSize 压缩大小报告
// 禁用 brotli 压缩大小报告
export const brotliSize = false
// 分包
@@ -15,12 +14,7 @@ export const rollupOptions = {
output: {
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: (chunkInfo) => {
if(['.png', '.jpg', '.jpeg'].includes(path.extname(chunkInfo.name))) {
return `static/[ext]/[name].[ext]`
}
return `static/[ext]/[name]-[hash].[ext]`
},
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
manualChunks: {
jsonWorker: [`${prefix}/language/json/json.worker`],
cssWorker: [`${prefix}/language/css/css.worker`],

View File

@@ -1,9 +0,0 @@
/**
* Get the configuration file variable name
* @param env
*/
export const getConfigFileName = (env: Record<string, any>) => {
return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
.toUpperCase()
.replace(/\s/g, '');
};

View File

@@ -7,8 +7,10 @@
<meta name="description" content="GoView 是高效、高性能的拖拽式低代码数据可视化开发平台,将页面元素封装为基础组件,无需编写代码即可完成业务需求。">
<meta name="keywords" content="GoViewgoview低代码可视化">
<meta name="author" content="奔跑的面条,面条">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1,maximum-scale=1,user-scalable=0">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=yes" media="(min-width: 769px)">
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
/>
<link rel="icon" href="./favicon.ico" />
<title>GoView</title>
<link rel="stylesheet" href="./index.css" />

View File

@@ -1,9 +1,6 @@
{
"name": "go-view",
"version": "1.1.11",
"engines": {
"node": ">=16.14"
},
"version": "2.1.0",
"scripts": {
"dev": "vite --host",
"build": "vue-tsc --noEmit && vite build",
@@ -14,38 +11,29 @@
"lint:fix": "eslint --ext .js,.jsx,.ts,.tsx,.vue src --fix"
},
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@amap/amap-jsapi-types": "^0.0.8",
"@iconify/json": "^2.2.158",
"@types/color": "^3.0.3",
"@types/crypto-js": "^4.1.1",
"@types/keymaster": "^1.6.30",
"@types/lodash": "^4.14.184",
"@visactor/vchart": "^1.12.12",
"@visactor/vchart-theme": "^1.12.2",
"animate.css": "^4.1.1",
"axios": "^1.4.0",
"axios": "^0.27.2",
"color": "^4.2.3",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.7",
"dom-helpers": "^5.2.1",
"echarts-liquidfill": "^3.1.0",
"echarts-stat": "^1.2.0",
"echarts-wordcloud": "^2.0.0",
"gsap": "^3.11.3",
"highlight.js": "^11.5.0",
"html2canvas": "^1.4.1",
"iconify-icon": "^1.0.8",
"keymaster": "^1.6.2",
"mitt": "^3.0.0",
"monaco-editor": "^0.33.0",
"naive-ui": "2.40.3",
"naive-ui": "2.33.4",
"pinia": "^2.0.13",
"screenfull": "^6.0.1",
"three": "^0.145.0",
"vue": "^3.5.13",
"vue": "^3.2.31",
"vue-demi": "^0.13.1",
"vue-i18n": "9.2.2",
"vue-i18n": "9.1.10",
"vue-router": "4.0.12",
"vue3-lazyload": "^0.2.5-beta",
"vue3-sketch-ruler": "^1.3.3",
@@ -60,8 +48,8 @@
"@typescript-eslint/parser": "^5.18.0",
"@vicons/carbon": "^0.12.0",
"@vicons/ionicons5": "~0.11.0",
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"@vitejs/plugin-vue": "^1.10.2",
"@vitejs/plugin-vue-jsx": "^1.3.9",
"@vue/compiler-sfc": "^3.2.31",
"@vueuse/core": "^7.7.1",
"commitlint": "^17.0.2",
@@ -80,7 +68,7 @@
"sass": "^1.49.11",
"sass-loader": "^12.6.0",
"typescript": "4.6.3",
"vite": "4.3.6",
"vite": "2.9.9",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-importer": "^0.2.5",
"vite-plugin-mock": "^2.9.6",
@@ -88,4 +76,4 @@
"vue-echarts": "^6.0.2",
"vue-tsc": "^0.28.10"
}
}
}

6323
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
<template>
<n-config-provider
:locale="zhCN"
:theme="darkTheme"
:hljs="hljsTheme"
:locale="locale"
:date-locale="dateLocale"
:date-locale="dateZhCN"
:theme-overrides="overridesTheme"
>
<go-app-provider>
@@ -14,10 +14,11 @@
</template>
<script lang="ts" setup>
import { NConfigProvider } from 'naive-ui'
import { zhCN, dateZhCN, NConfigProvider } from 'naive-ui'
import { GoAppProvider } from '@/components/GoAppProvider'
import { I18n } from '@/components/I18n'
import { useDarkThemeHook, useThemeOverridesHook, useCode, useLang } from '@/hooks'
import { useSystemInit, useDarkThemeHook, useThemeOverridesHook, useCode } from '@/hooks'
// 暗黑主题
const darkTheme = useDarkThemeHook()
@@ -28,7 +29,6 @@ const overridesTheme = useThemeOverridesHook()
// 代码主题
const hljsTheme = useCode()
// 全局语言
const { locale, dateLocale } = useLang()
//测试提交
// 系统全局数据初始化
useSystemInit()
</script>

View File

@@ -1,4 +1,4 @@
import axios, { AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios'
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'
import { ResultEnum } from "@/enums/httpEnum"
import { ErrorPageNameMap } from "@/enums/pageEnum"
import { redirectErrorPage } from '@/utils'
@@ -9,10 +9,10 @@ const axiosInstance = axios.create({
})
axiosInstance.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
(config: AxiosRequestConfig) => {
return config
},
(error: AxiosError) => {
(error: AxiosRequestConfig) => {
Promise.reject(error)
}
)
@@ -21,8 +21,7 @@ axiosInstance.interceptors.request.use(
axiosInstance.interceptors.response.use(
(res: AxiosResponse) => {
const { code } = res.data as { code: number }
if (code === undefined || code === null) return Promise.resolve(res.data)
if (code === ResultEnum.DATA_SUCCESS) return Promise.resolve(res.data)
if (code === ResultEnum.SUCCESS) return Promise.resolve(res.data)
// 重定向
if (ErrorPageNameMap.get(code)) redirectErrorPage(code)
return Promise.resolve(res.data)

View File

@@ -116,6 +116,7 @@ export const customizeHttp = (targetParams: RequestConfigType, globalParams: Req
if (!targetParams || !globalParams) {
return
}
// 全局
const {
// 全局请求源地址
@@ -163,6 +164,7 @@ export const customizeHttp = (targetParams: RequestConfigType, globalParams: Req
params = translateStr(params)
// form 类型处理
let formData: FormData = new FormData()
formData.set('default', 'defaultData')
// 类型处理
switch (requestParamsBodyType) {
@@ -171,9 +173,7 @@ export const customizeHttp = (targetParams: RequestConfigType, globalParams: Req
case RequestBodyEnum.JSON:
headers['Content-Type'] = ContentTypeEnum.JSON
//json对象也能使用'javasctipt:'来动态拼接参数
data = translateStr(targetRequestParams.Body['json'])
if(typeof data === 'string') data = JSON.parse(data)
data = translateStr(JSON.parse(targetRequestParams.Body['json']))
// json 赋值给 data
break

View File

@@ -1,100 +0,0 @@
{
"nodes": [
{
"id": "0",
"name": "Myriel",
"symbolSize": "@integer(0, 50)",
"x": -266.82776,
"y": 299.6904,
"value": "@integer(0, 50)",
"category": 3
},
{
"id": "1",
"name": "Napoleon",
"symbolSize": "@integer(0, 50)",
"x": -418.08344,
"y": 446.8853,
"value": "@integer(0, 50)",
"category": 5
},
{
"id": "2",
"name": "MlleBaptistine",
"symbolSize": "@integer(0, 50)",
"x": -212.76357,
"y": 245.29176,
"value": "@integer(0, 50)",
"category": 1
},
{
"id": "3",
"name": "MmeMagloire",
"symbolSize": "@integer(0, 50)",
"x": -242.82404,
"y": 235.26283,
"value": "@integer(0, 50)",
"category": 1
},
{
"id": "4",
"name": "CountessDeLo",
"symbolSize": "@integer(0, 50)",
"x": -379.30386,
"y": 429.06424,
"value": "@integer(0, 50)",
"category": 0
}
],
"links": [
{
"source": "1",
"target": "@integer(2, 4)"
},
{
"source": "2",
"target": "@integer(3, 4)"
},
{
"source": "3",
"target": "@integer(0, 2)"
},
{
"source": "3",
"target": "@integer(0, 1)"
},
{
"source": "4",
"target": "@integer(0, 3)"
}
],
"categories": [
{
"name": "A"
},
{
"name": "B"
},
{
"name": "C"
},
{
"name": "D"
},
{
"name": "E"
},
{
"name": "F"
},
{
"name": "G"
},
{
"name": "H"
},
{
"name": "I"
}
]
}

View File

@@ -1,5 +1,4 @@
import test from './test.mock'
import vchart from './vchart.mock'
import { MockMethod } from 'vite-plugin-mock'
import { RequestHttpEnum } from '@/enums/httpEnum'
@@ -16,14 +15,9 @@ export const radarUrl = '/mock/radarData'
export const heatMapUrl = '/mock/heatMapData'
export const scatterBasicUrl = '/mock/scatterBasic'
export const mapUrl = '/mock/map'
export const capsuleUrl = '/mock/capsule'
export const wordCloudUrl = '/mock/wordCloud'
export const treemapUrl = '/mock/treemap'
export const threeEarth01Url = '/mock/threeEarth01Data'
export const sankeyUrl = '/mock/sankey'
export const graphUrl = '/mock/graphData'
export const vchartBarDataUrl = '/mock/vchart/barDataUrl'
const mockObject: MockMethod[] = [
{
@@ -88,11 +82,6 @@ const mockObject: MockMethod[] = [
method: RequestHttpEnum.GET,
response: () => test.fetchMap
},
{
url: capsuleUrl,
method: RequestHttpEnum.GET,
response: () => test.fetchCapsule
},
{
url: wordCloudUrl,
method: RequestHttpEnum.GET,
@@ -108,21 +97,6 @@ const mockObject: MockMethod[] = [
method: RequestHttpEnum.GET,
response: () => test.threeEarth01Data
},
{
url: sankeyUrl,
method: RequestHttpEnum.GET,
response: () => test.fetchSankey
},
{
url: graphUrl,
method: RequestHttpEnum.GET,
response: () => test.graphData
},
{
url: vchartBarDataUrl,
method: RequestHttpEnum.GET,
response: () => vchart.bar
}
]
export default mockObject

View File

@@ -1,9 +1,79 @@
{
"markers|50": [
"point": [
{
"name": "某某地市",
"value": "@integer(2, 20)",
"position": ["@float(115, 117, 1, 6)", "@float(38, 40, 1, 6)"]
"name": "北京",
"value": [116.405285, 39.904989, 200]
},
{
"name": "郑州",
"value": [113.665412, 34.757975, 888]
},
{
"name": "青海",
"value": [101.778916, 36.623178, 666]
},
{
"name": "宁夏回族自治区",
"value": [106.278179, 38.46637, 66]
},
{
"name": "哈尔滨市",
"value": [126.642464, 45.756967, 101]
}
],
"map": [
{
"name": "北京市",
"value": "@integer(0, 1000)"
},
{
"name": "河北省",
"value": "@integer(0, 1000)"
},
{
"name": "江苏省",
"value": "@integer(0, 1000)"
},
{
"name": "福建省",
"value": "@integer(0, 1000)"
},
{
"name": "山东省",
"value": "@integer(0, 1000)"
},
{
"name": "河南省",
"value": "@integer(0, 1000)"
},
{
"name": "湖北省",
"value": "@integer(0, 1000)"
},
{
"name": "广西壮族自治区",
"value": "@integer(0, 1000)"
},
{
"name": "海南省",
"value": "@integer(0, 1000)"
},
{
"name": "青海省",
"value": "@integer(0, 1000)"
},
{
"name": "新疆维吾尔自治区",
"value": "@integer(0, 1000)"
}
],
"pieces": [
{ "gte": 1000, "label": ">1000" },
{ "gte": 600, "lte": 999, "label": "600-999" },
{ "gte": 200, "lte": 599, "label": "200-599" },
{ "gte": 50, "lte": 199, "label": "49-199" },
{ "gte": 10, "lte": 49, "label": "10-49" },
{ "lte": 9, "label": "<9" }
]
}

View File

@@ -1,86 +0,0 @@
{
"label": [
{
"name": "a"
},
{
"name": "b"
},
{
"name": "a1"
},
{
"name": "a2"
},
{
"name": "b1"
},
{
"name": "b2"
}
],
"links": [
{
"source": "a",
"target": "a1",
"value": "@integer(0, 10)"
},
{
"source": "a",
"target": "a2",
"value": "@integer(0, 10)"
},
{
"source": "b",
"target": "b1",
"value": "@integer(0, 10)"
},
{
"source": "a",
"target": "b1",
"value": "@integer(0, 10)"
},
{
"source": "b1",
"target": "a1",
"value": "@integer(0, 10)"
},
{
"source": "b1",
"target": "b2",
"value": "@integer(0, 10)"
}
],
"levels": [
{
"depth": 0,
"itemStyle": {
"color": "#decbe4"
},
"lineStyle": {
"color": "source",
"opacity": 0.9
}
},
{
"depth": 1,
"itemStyle": {
"color": "#b3cde3"
},
"lineStyle": {
"color": "source",
"opacity": 0.6
}
},
{
"depth": 2,
"itemStyle": {
"color": "#ccebc5"
},
"lineStyle": {
"color": "source",
"opacity": 0.6
}
}
]
}

View File

@@ -2,8 +2,6 @@ import heatmapJson from './heatMapData.json'
import scatterJson from './scatter.json'
import mapJson from './map.json'
import tTreemapJson from './treemap.json'
import sankeyJson from './sankey.json'
import graphDataJson from './graph.json'
export default {
// 单图表
@@ -12,8 +10,28 @@ export default {
status: 200,
msg: '请求成功',
data: {
dimensions: ['product', 'dataOne'],
'source|20': [
dimensions: ['product', 'dataOne'],
source: [
{
product: '@name',
'dataOne|0-900': 3
},
{
product: '@name',
'dataOne|0-900': 3
},
{
product: '@name',
'dataOne|0-900': 3
},
{
product: '@name',
'dataOne|0-900': 3
},
{
product: '@name',
'dataOne|0-900': 3
},
{
product: '@name',
'dataOne|0-900': 3
@@ -21,31 +39,43 @@ export default {
]
}
},
// 胶囊图
fetchCapsule: {
code: 0,
status: 200,
msg: '请求成功',
data: {
dimensions: ['name', 'value'],
"source|2-5": [
{ 'name|+1': ["厦门","福州","北京","上海","新疆","郑州","湖南","内蒙古"], 'value|0-40': 20 },
]
}
},
// 图表
fetchMockData: {
code: 0,
status: 200,
msg: '请求成功',
data: {
dimensions: ['product', 'dataOne', 'dataTwo', 'dataThree'],
'source|20': [
dimensions: ['product', 'dataOne', 'dataTwo'],
source: [
{
product: '@name',
'dataOne|100-900': 3,
'dataTwo|100-900': 3,
'dataThree|100-900': 3
'dataTwo|100-900': 3
},
{
product: '@name',
'dataOne|100-900': 3,
'dataTwo|100-900': 3
},
{
product: '@name',
'dataOne|100-900': 3,
'dataTwo|100-900': 3
},
{
product: '@name',
'dataOne|100-900': 3,
'dataTwo|100-900': 3
},
{
product: '@name',
'dataOne|100-900': 3,
'dataTwo|100-900': 3
},
{
product: '@name',
'dataOne|100-900': 3,
'dataTwo|100-900': 3
}
]
}
@@ -55,7 +85,21 @@ export default {
code: 0,
status: 200,
msg: '请求成功',
'data|50': [{ name: '@name', 'value|100-900': 5 }]
data: [
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 },
{ name: '@name', 'value|100-900': 5 }
]
},
// 轮播表格
fetchScrollBoard: {
@@ -218,22 +262,13 @@ export default {
data: [
{
startArray: { name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' },
'endArray|10': [{ name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' }]
endArray: [
{ name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' },
{ name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' },
{ name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' },
{ name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' }
]
}
]
},
// 桑基图
fetchSankey: {
code: 0,
status: 200,
msg: '请求成功',
data: sankeyJson
},
// 关系图
graphData: {
code: 0,
status: 200,
msg: '请求成功',
data: graphDataJson
},
}
}

View File

@@ -1,10 +0,0 @@
import bar from './vchart/bar.json'
export default {
bar: {
code: 0,
status: 200,
msg: '请求成功',
data: bar
}
}

View File

@@ -1,16 +0,0 @@
{
"values": [
{ "type": "Nail polish", "year": "Africa", "value|100-900": 3 },
{ "type": "Nail polish", "year": "EU", "value|100-900": 3 },
{ "type": "Nail polish", "year": "China", "value|100-900": 3 },
{ "type": "Nail polish", "year": "USA", "value|100-900": 3 },
{ "type": "Eyebrow pencil", "year": "Africa", "value|100-900": 3 },
{ "type": "Eyebrow pencil", "year": "EU", "value|100-900": 3 },
{ "type": "Eyebrow pencil", "year": "China", "value|100-900": 3 },
{ "type": "Eyebrow pencil", "year": "USA", "value|100-900": 3 },
{ "type": "Rouge", "year": "Africa", "value|100-900": 3 },
{ "type": "Rouge", "year": "EU", "value|100-900": 3 },
{ "type": "Rouge", "year": "China", "value|100-900": 3 },
{ "type": "Rouge", "year": "USA", "value|100-900": 3 }
]
}

2
src/api/path/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from '@/api/path/project.api'
export * from '@/api/path/system.api'

View File

@@ -0,0 +1,84 @@
import { http } from '@/api/http'
import { httpErrorHandle } from '@/utils'
import { ContentTypeEnum, RequestHttpEnum, ModuleTypeEnum } from '@/enums/httpEnum'
// * 项目列表
export const projectListApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.GET)(`${ModuleTypeEnum.PROJECT}/list`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 新增项目
export const createProjectApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.PROJECT}/create`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 获取项目
export const fetchProjectApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.GET)(`${ModuleTypeEnum.PROJECT}/getData`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 保存项目
export const saveProjectApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.PROJECT}/save/data`, data, ContentTypeEnum.FORM_URLENCODED);
return res;
} catch {
httpErrorHandle();
}
}
// * 修改项目基础信息
export const updateProjectApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.PROJECT}/edit`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 删除项目
export const deleteProjectApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.DELETE)(`${ModuleTypeEnum.PROJECT}/delete`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 修改发布状态 [-1未发布,1发布]
export const changeProjectReleaseApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.PUT)(`${ModuleTypeEnum.PROJECT}/publish`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 上传文件
export const uploadFile = async (url:string, data: object) => {
try {
const res = await http(RequestHttpEnum.POST)(url, data, ContentTypeEnum.FORM_DATA);
return res;
} catch {
httpErrorHandle();
}
}

View File

@@ -0,0 +1,33 @@
import { http } from '@/api/http'
import { httpErrorHandle } from '@/utils'
import { RequestHttpEnum, ModuleTypeEnum } from '@/enums/httpEnum'
// * 登录
export const loginApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.SYSTEM}/login`, data);
return res;
} catch(err) {
httpErrorHandle();
}
}
// * 登出
export const logoutApi = async () => {
try {
const res = await http(RequestHttpEnum.GET)(`${ModuleTypeEnum.SYSTEM}/logout`);
return res;
} catch(err) {
httpErrorHandle();
}
}
// * 获取 oss 上传接口
export const ossUrlApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.GET)(`${ModuleTypeEnum.SYSTEM}/getOssInfo`, data);
return res;
} catch(err) {
httpErrorHandle();
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

146
src/backend/ibackend.ts Normal file
View File

@@ -0,0 +1,146 @@
/**
* 后端接口,相关功能对应表:
* 登录 - login
* 登出 - logout
* 预览token 注入或单点登陆 - checkToken
* 显示项目列表和分页 - projectList
* 保存、发布、修改名称 - updateProject
* 复制项目 - copyProject
* 图表内的图片上传 - uploadFile
* 上传图片显示处理 - getFileUrl
* 所有接口返回格式MyResponseType
*/
import { IndexDbBackend } from "./indexdb/indexdbbackend";
// import { PythonBackend } from "./python/pythonbackend";
export interface MyResponseType {
code: number; // 状态200 表示接口调用成功参考HttpEnum
msg: string; // 提示信息,配合 data 和 code
data: any; // data = null 表示接口结果错误,错误原因放在 msg
}
export class MyResponse implements MyResponseType {
code: number = 200;
msg: string = "";
data: any = {};
}
/**
* 实现 IBackend 后端接口
* 错误处理:
*/
export interface IBackend {
/**
* 初始化后端系统测试后端连接oss地址等
* @param data 可选,备用
*/
init(data:any):any
/**
* 登陆
* @param data {} .username .password
* @return MyResponseType
* .data 须包含:
* token:{tokenValue:"", tokenName:""},
* userinfo:{nickname:"", username: "", id: 用户ID}
* 错误处理:
* 1 接口错误 .code 不为 200 .msg 可选,后端反馈错误信息
* 2 登陆错误 .code=200 .data = null, msg 可选,反馈不能登陆的原因
* 登陆信息.data 记录:
* setLocalStorage(GO_LOGIN_INFO_STORE, res.data)
*/
login(data:any):any
/**
* 通知后端登出
*/
logout():any
/**
* 检查Token是否有效配合预览页面和单点登陆备用
* @param data {tokenValue, tokenName}
* @return 同 login()
*/
checkToken(data:any):any
/**
* 项目列表
* @param data {} .page, .limit
* @return [项目],字段名称需要进行 map
* id: projectId
* title:projectName
* release,
* label:remarks
* image:indexImage 如果需要挂刷新,在这里处理。如果需要拼接 urlgetFileUrl也在这里处理好。
*/
projectList(data:any):any
/**
* 新增项目
* @param data
* .projectName
* @return id 新项目 ID
*/
createProject(data: any):any
/**
* 获取项目
* @param data .projectId
* @return
id:projectId
projectName,
state: release,
remarks,
content
*/
fetchProject(data: any):any
/**
* 修改项目
* @param data
* .projectId 必须
* .projectName 可选
* .release 可选
* .content 可选
* .object File 可选 对象
* .remarks 可选
* @return
*/
updateProject(data: any):any
/**
* 复制项目
* @param data
* .copyId 需要复制的项目ID
* .projectName
* @return id 新项目ID
*/
copyProject(data: any):any
/**
* 删除项目
* @param data
* .projectId
* @return
*/
deleteProject(data: any):any
/**
* 文件上传
* @param file File 图片对象
* @param params 备用 Todo: 上传文件可带上项目ID和其他附加信息以便后端文件管理
* @return .uri 文件对象 uri。建议在图表中保存相对地址通过 getFileUrl 得到完整地址
*/
uploadFile(file: File, params: any):any
/**
* 文件地址转换,处理 uploadFile 的返回地址。如果是绝对地址,可以不处理
* @param uploadUri 上传返回的 uri
* @return 供 image.src 使用的地址信息
*/
getFileUrl(uploadUri:string):string
}
export const BackEndFactory = new IndexDbBackend();
// export const BackEndFactory = new MockBackend();
// export const BackEndFactory = new PythonBackend();

View File

@@ -0,0 +1,147 @@
/**
* IndexDb 帮助类
*/
const win: { [k: string]: any } = window || globalThis;
const indexedDB =
win.indexedDB || win.mozIndexedDB || win.webkitIndexedDB || win.msIndexedDB;
const dbs: { [k: string]: IDBDatabase } = {};
let databaseName: string;
let request: IDBOpenDBRequest;
interface AnyEvent {
[k: string]: any;
}
export interface TableOption {
storeName: string;
option: { [K: string]: any };
index: { [K: string]: any }[];
}
export const createDB = (
name: string,
version?: string,
options?: TableOption[],
) =>
new Promise<IDBDatabase>((resolve, reject) => {
if (!indexedDB) reject('浏览器不支持indexedDB');
databaseName = name;
if (dbs?.[name]) {
resolve(dbs[name]);
return;
}
request = indexedDB.open(name, version);
createTable(options)?.then((db: IDBDatabase) => resolve(db));
request.onsuccess = (event: AnyEvent) => {
// IDBDatabase
const db = event.target.result;
// 缓存起来
dbs[name] = db;
resolve(db);
};
request.onerror = (event: AnyEvent) => reject(event);
});
export const createTable = (options?: TableOption[]) => {
if (!options) return;
return new Promise<IDBDatabase>((resolve) => {
request.onupgradeneeded = (event: AnyEvent) => {
const db = event.target.result;
dbs[databaseName] = db;
for (const i in options) {
// 判断是否存在表
if (!db.objectStoreNames.contains(options[i].storeName)) {
const objectStore = db.createObjectStore(
options[i].storeName,
options[i].option,
);
for (const j of options[i].index) {
objectStore.createIndex(j.name, j.keyPath, {
unique: j.unique,
});
}
}
}
resolve(db);
};
});
};
const getTransaction = async (name: string, version?: string) => {
let db: IDBDatabase;
// 先从缓存获取
if (dbs[databaseName]) {
db = dbs[databaseName];
} else {
db = await createDB(databaseName, version);
}
return db.transaction(name, 'readwrite');
};
const getObjectStore = async (
name: string,
version?: string,
): Promise<IDBObjectStore> => {
const transaction = await getTransaction(name, version);
return transaction.objectStore(name);
};
const getStore = (name: string, type: string, data: any) =>
new Promise<IDBDatabase>((resolve) => {
getObjectStore(name).then((objectStore: IDBObjectStore | any) => {
const request = objectStore[type](data);
request.onsuccess = (event: AnyEvent) =>
resolve(event.target.result);
});
});
const findStore = (
name: string,
start: any,
end: any,
startInclude: any,
endInclude: any,
) =>
new Promise<IDBDatabase>((resolve, reject) => {
getObjectStore(name).then((objectStore: IDBObjectStore) => {
const request = objectStore.openCursor(
IDBKeyRange.bound(start, end, startInclude, endInclude),
);
request.onsuccess = (event: AnyEvent) =>
resolve(event.target.result);
request.onerror = (event: AnyEvent) => reject(event);
});
});
export interface DBSelect {
add: (data: any) => Promise<IDBDatabase>;
get: (data: any) => Promise<IDBDatabase>;
getAll: () => Promise<IDBDatabase>;
del: (data: any) => Promise<IDBDatabase>;
clear: (data: any) => Promise<IDBDatabase>;
put: (data: any) => Promise<IDBDatabase>;
find: (
start: any,
end: any,
startInclude: any,
endInclude: any,
) => Promise<IDBDatabase>;
}
// 获取一个store
export const onDBSelect = async (
name: string,
version: string
): Promise<DBSelect> => {
const add = (data: any) => getStore(name, 'add', data);
const get = (data: any) => getStore(name, 'get', data);
const getAll = () => getStore(name, 'getAll', null);
const del = (data: any) => getStore(name, 'delete', data);
const clear = (data: any) => getStore(name, 'clear', data);
const put = (data: any) => getStore(name, 'put', data);
const find = (start: any, end: any, startInclude: any, endInclude: any) =>
findStore(name, start, end, startInclude, endInclude);
const options: DBSelect = { add, get, getAll, clear, del, put, find };
getObjectStore(name, version);
return options;
};

View File

@@ -0,0 +1,155 @@
import { MyResponse, IBackend } from '../ibackend'
import { createDB, DBSelect, onDBSelect } from '../indexdb/indexdb'
import { fileToUrl, fileToBlob } from "@/utils"
const PROJECT_TABLE = "project"
const IMAGE_TABLE = "image" // 保存图片未实现Todo
const DB_NAME = "goview"
const DB_VER = "1"
export class IndexDbBackend implements IBackend {
public async init(data: any) {
let rtn:MyResponse = new MyResponse;
const db:IDBDatabase = await createDB(DB_NAME, DB_VER, [
{
storeName: PROJECT_TABLE,
option: {
keyPath: "projectId", autoIncrement:true
},
index: [
{name: 'projectId', keyPath: "projectId", unique: true},
{name: 'projectName', keyPath: "projectName", unique: false},
{name: 'release', keyPath: "release", unique: false},
{name: 'remarks', keyPath: "remarks", unique: false},
{name: 'content', keyPath: "content", unique: false},
{name: 'indexImage', keyPath: "indexImage", unique: false}
]
}
])
return rtn;
}
public async login(data:any) {
let rtn:MyResponse = new MyResponse;
if(data.password == "123456" && data.username == "admin"){
rtn.data = {
token:{tokenValue:"mockToken", tokenName:"name"},
userinfo:{nickname:"nickname", username:data.username, id:1}
}
}else{
rtn.data = null
rtn.msg = "admin 和 123456"
}
return rtn;
}
public async logout() {
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async checkToken(data: any) {
let rtn:MyResponse = new MyResponse;
console.log("CheckToken: " + data.token)
rtn.data = {
token:{tokenValue:"mockToken", tokenName:"name"},
userinfo:{nickname:"nickname", username:data.username, id:1}
}
return rtn;
}
public async projectList(data:any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
const r:any = await db.getAll()
rtn.data = []
r.map(function (item: any) {
let url = ""
if(item.indexImage){
const Url = URL || window.URL || window.webkitURL
url = Url.createObjectURL(item.indexImage)
}
rtn.data.push({
id: item.projectId,
title: item.projectName,
release: item.release == 1,
label:item.remarks,
image:url
})
})
return rtn;
}
public async createProject(data: any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
rtn.data.id = await db.add({ projectName:data.projectName })
return rtn;
}
public async fetchProject(data: any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
const r:any = await db.get(parseInt(data.projectId))
rtn.data = {
id:r.projectId,
projectName: r.projectName,
state: r.release,
remarks: r.remarks,
content: r.content
}
return rtn;
}
public async updateProject(data: any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
const row:any = await db.get(parseInt(data.projectId))
if("content" in data) row.content = data.content
if("projectName" in data) row.projectName = data.projectName
if("release" in data) row.release = data.release
if("remarks" in data) row.remarks = data.remarks
if("object" in data) {
row.indexImage = await fileToBlob(data.object)
}
await db.put(row)
return rtn;
}
public async copyProject(data: any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
const row:any = await db.get(parseInt(data.copyId))
rtn.data.id =await db.add({
projectName:data.projectName,
content:row.content,
indexImage:row.indexImage,
remarks:row.remarks
})
return rtn;
}
public async deleteProject(data: any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
await db.del(parseInt(data.projectId))
return rtn;
}
public async changeProjectRelease(data: any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async uploadFile(data: File, params:any){
// Todo: 图片可以保存在表中
let rtn:MyResponse = new MyResponse;
rtn.data.uri = fileToUrl(data)
return rtn;
}
public getFileUrl(uploadUri:string){
return uploadUri;
}
}

View File

@@ -0,0 +1,17 @@
export enum ModuleTypeEnum {
SYSTEM = 'sys',
PROJECT = 'project',
}
// 接口白名单(免登录)
export const fetchAllowList = [
// 登录
`${ModuleTypeEnum.SYSTEM}/login`,
// 获取 OSS 接口
`${ModuleTypeEnum.SYSTEM}/getOssInfo`,
// 预览获取数据
`${ModuleTypeEnum.PROJECT}/getData`,
]
// 接口黑名单
export const fetchBlockList = []

71
src/backend/java/axios.ts Normal file
View File

@@ -0,0 +1,71 @@
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'
import { ResultEnum } from "@/enums/httpEnum"
import { PageEnum, ErrorPageNameMap } from "@/enums/pageEnum"
import { StorageEnum } from '@/enums/storageEnum'
import { axiosPre } from '@/settings/httpSetting'
import { SystemStoreEnum, SystemStoreUserInfoEnum } from '@/store/modules/systemStore/systemStore.d'
import { redirectErrorPage, getLocalStorage, routerTurnByName, httpErrorHandle } from '@/utils'
import { fetchAllowList } from './axios.config'
import includes from 'lodash/includes'
const axiosInstance = axios.create({
baseURL: `${import.meta.env.PROD ? import.meta.env.VITE_PRO_PATH : ''}${axiosPre}`,
timeout: ResultEnum.TIMEOUT,
})
axiosInstance.interceptors.request.use(
(config: AxiosRequestConfig) => {
// 白名单校验
if (includes(fetchAllowList, config.url)) return config
// 获取 token
const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
// 重新登录
if (!info) {
routerTurnByName(PageEnum.BASE_LOGIN_NAME)
return config
}
const userInfo = info[SystemStoreEnum.USER_INFO]
config.headers = {
...config.headers,
[userInfo[SystemStoreUserInfoEnum.TOKEN_NAME] || 'token']: userInfo[SystemStoreUserInfoEnum.USER_TOKEN] || ''
}
return config
},
(err: AxiosRequestConfig) => {
Promise.reject(err)
}
)
// 响应拦截器
axiosInstance.interceptors.response.use(
(res: AxiosResponse) => {
const { code } = res.data as { code: number }
// 成功
if (code === ResultEnum.SUCCESS) {
return Promise.resolve(res.data)
}
// 登录过期
if (code === ResultEnum.TOKEN_OVERDUE) {
window['$message'].error(window['$t']('http.token_overdue_message'))
routerTurnByName(PageEnum.BASE_LOGIN_NAME)
return Promise.resolve(res.data)
}
// 固定错误码重定向
if (ErrorPageNameMap.get(code)) {
redirectErrorPage(code)
return Promise.resolve(res.data)
}
// 提示错误
window['$message'].error(window['$t']((res.data as any).msg))
return Promise.resolve(res.data)
},
(err: AxiosResponse) => {
Promise.reject(err)
}
)
export default axiosInstance

226
src/backend/java/http.ts Normal file
View File

@@ -0,0 +1,226 @@
import axiosInstance from './axios'
import {
RequestHttpEnum,
ContentTypeEnum,
RequestBodyEnum,
RequestDataTypeEnum,
RequestContentTypeEnum,
RequestParamsObjType
} from '@/enums/httpEnum'
import type { RequestGlobalConfigType, RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
export const get = (url: string, params?: object) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.GET,
params: params,
})
}
export const post = (url: string, data?: object, headersType?: string) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.POST,
data: data,
headers: {
'Content-Type': headersType || ContentTypeEnum.JSON
}
})
}
export const patch = (url: string, data?: object, headersType?: string) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.PATCH,
data: data,
headers: {
'Content-Type': headersType || ContentTypeEnum.JSON
}
})
}
export const put = (url: string, data?: object, headersType?: ContentTypeEnum) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.PUT,
data: data,
headers: {
'Content-Type': headersType || ContentTypeEnum.JSON
}
})
}
export const del = (url: string, params?: object) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.DELETE,
params
})
}
// 获取请求函数默认get
export const http = (type?: RequestHttpEnum) => {
switch (type) {
case RequestHttpEnum.GET:
return get
case RequestHttpEnum.POST:
return post
case RequestHttpEnum.PATCH:
return patch
case RequestHttpEnum.PUT:
return put
case RequestHttpEnum.DELETE:
return del
default:
return get
}
}
const prefix = 'javascript:'
// 对输入字符进行转义处理
export const translateStr = (target: string | object) => {
if (typeof target === 'string') {
if (target.startsWith(prefix)) {
const funcStr = target.split(prefix)[1]
let result;
try {
result = new Function(`${funcStr}`)()
} catch (error) {
console.log(error)
window['$message'].error('js内容解析有误')
}
return result
} else {
return target
}
}
for (const key in target) {
if (Object.prototype.hasOwnProperty.call(target, key)) {
const subTarget = (target as any)[key];
(target as any)[key] = translateStr(subTarget)
}
}
return target
}
/**
* * 自定义请求
* @param targetParams 当前组件参数
* @param globalParams 全局参数
*/
export const customizeHttp = (targetParams: RequestConfigType, globalParams: RequestGlobalConfigType) => {
if (!targetParams || !globalParams) {
return
}
// 全局
const {
// 全局请求源地址
requestOriginUrl,
// 全局请求内容
requestParams: globalRequestParams
} = globalParams
// 目标组件(优先级 > 全局组件)
const {
// 请求地址
requestUrl,
// 普通 / sql
requestContentType,
// 获取数据的方式
requestDataType,
// 请求方式 get/post/del/put/patch
requestHttpType,
// 请求体类型 none / form-data / x-www-form-urlencoded / json /xml
requestParamsBodyType,
// SQL 请求对象
requestSQLContent,
// 请求内容 params / cookie / header / body: 同 requestParamsBodyType
requestParams: targetRequestParams
} = targetParams
// 静态排除
if (requestDataType === RequestDataTypeEnum.STATIC) return
if (!requestUrl) {
return
}
// 处理头部
let headers: RequestParamsObjType = {
...globalRequestParams.Header,
...targetRequestParams.Header
}
headers = translateStr(headers)
// data 参数
let data: RequestParamsObjType | FormData | string = {}
// params 参数
let params: RequestParamsObjType = { ...targetRequestParams.Params }
params = translateStr(params)
// form 类型处理
let formData: FormData = new FormData()
formData.set('default', 'defaultData')
// 类型处理
switch (requestParamsBodyType) {
case RequestBodyEnum.NONE:
break
case RequestBodyEnum.JSON:
headers['Content-Type'] = ContentTypeEnum.JSON
data = translateStr(JSON.parse(targetRequestParams.Body['json']))
// json 赋值给 data
break
case RequestBodyEnum.XML:
headers['Content-Type'] = ContentTypeEnum.XML
// xml 字符串赋值给 data
data = translateStr(targetRequestParams.Body['xml'])
break
case RequestBodyEnum.X_WWW_FORM_URLENCODED: {
headers['Content-Type'] = ContentTypeEnum.FORM_URLENCODED
const bodyFormData = targetRequestParams.Body['x-www-form-urlencoded']
for (const i in bodyFormData) formData.set(i, translateStr(bodyFormData[i]))
// FormData 赋值给 data
data = formData
break
}
case RequestBodyEnum.FORM_DATA: {
headers['Content-Type'] = ContentTypeEnum.FORM_DATA
const bodyFormUrlencoded = targetRequestParams.Body['form-data']
for (const i in bodyFormUrlencoded) {
formData.set(i, translateStr(bodyFormUrlencoded[i]))
}
// FormData 赋值给 data
data = formData
break
}
}
// sql 处理
if (requestContentType === RequestContentTypeEnum.SQL) {
headers['Content-Type'] = ContentTypeEnum.JSON
data = requestSQLContent
}
try {
const url = (new Function("return `" + `${requestOriginUrl}${requestUrl}`.trim() + "`"))();
return axiosInstance({
url,
method: requestHttpType,
data,
params,
headers
})
} catch (error) {
console.log(error)
window['$message'].error('URL地址格式有误')
}
}

View File

@@ -0,0 +1,130 @@
import { MyResponse, IBackend } from './ibackend'
import { fileToUrl } from '@/utils'
/**
* MockBackend
* 模拟纯前端,不会保存,也不报错。
*/
export class MockBackend implements IBackend {
public async init(data: any) {
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async login(data:any) {
let rtn:MyResponse = new MyResponse;
if(data.password == "123456" && data.username == "admin"){
rtn.data = {
token:{tokenValue:"mockToken", tokenName:"name"},
userinfo:{nickname:"nickname", username:data.username, id:1}
}
}else{
rtn.data = null
rtn.msg = "用户名或密码错误!"
}
return rtn;
}
public async logout() {
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async checkToken(data:any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async projectList(data:any){
let rtn:MyResponse = new MyResponse;
rtn.data =[
{
id: 1,
title: '假数据不可用',
release: true,
label: '官方案例'
},
{
id: 2,
title: '物料2-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 3,
title: '物料3-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 4,
title: '物料4-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 5,
title: '物料5-假数据不可用',
release: false,
label: '官方案例'
}
];
return rtn;
}
public async createProject(data: any){
let rtn:MyResponse = new MyResponse;
rtn.data.id = "newId"
return rtn;
}
public async fetchProject(data: any){
let rtn:MyResponse = new MyResponse;
rtn.data = {
id:data.projectId,
projectName: '假数据不可用',
indexImage:'',
state: 0,
remarks: '官方案例',
content: null
}
return rtn;
}
public async saveProject(data: object){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async updateProject(data: any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async copyProject(data: any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async deleteProject(data: any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async changeProjectRelease(data: any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async uploadFile(data: File, params:any){
let rtn:MyResponse = new MyResponse;
rtn.data.uri = fileToUrl(data)
return rtn;
}
public getFileUrl(uploadUri:string){
return uploadUri;
}
}

View File

@@ -1,5 +1,4 @@
import Flipper from './index.vue'
type FlipType = 'up' | 'down'
export { Flipper, FlipType }

View File

@@ -1,13 +1,19 @@
<template>
<div class="go-flipper" :class="[flipType, { go: isFlipping }]">
<div class="M-Flipper" :class="[flipType, { go: isFlipping }]">
<div class="digital front" :data-front="frontTextFromData"></div>
<div class="digital back" :data-back="backTextFromData"></div>
</div>
</template>
<script lang="ts">
export default {
name: 'Flipper'
}
</script>
<script lang="ts" setup>
import { ref, PropType, watch, nextTick } from 'vue'
import { FlipType } from './index'
import { ref, PropType, watch } from 'vue'
import { FlipType } from '.'
const props = defineProps({
flipType: {
@@ -43,10 +49,6 @@ const props = defineProps({
backColor: {
type: String,
default: '#000000'
},
borderWidth: {
type: Number,
default: 2
}
})
@@ -54,27 +56,19 @@ const isFlipping = ref(false)
const frontTextFromData = ref(props.count || 0)
const backTextFromData = ref(props.count || 0)
let timeoutID: any = 0
//
const flip = async (front: string | number, back: string | number) => {
const flip = (front: string | number, back: string | number) => {
//
if (isFlipping.value) {
isFlipping.value = false //
clearTimeout(timeoutID) //
await nextTick()
await flip(front, back) //
return
}
if (isFlipping.value) return
//
backTextFromData.value = back
frontTextFromData.value = front
// true
isFlipping.value = true
//
timeoutID = setTimeout(() => {
setTimeout(() => {
isFlipping.value = false // false
frontTextFromData.value = back
}, props.duration)
@@ -98,7 +92,6 @@ $radius: v-bind('`${props.radius}px`');
$width: v-bind('`${props.width}px`');
$height: v-bind('`${props.height}px`');
$perspective: v-bind('`${props.height * 2}px`');
$borderWidth: v-bind('`${props.borderWidth * 2}px`');
$speed: v-bind('`${props.duration / 1000}s`');
$shadowColor: #000000;
$lineColor: #4a9ef8;
@@ -138,12 +131,13 @@ $lineColor: #4a9ef8;
}
// #endregion
.go-flipper {
.M-Flipper {
display: inline-block;
position: relative;
width: $width;
height: $height;
line-height: $height;
border: solid 1px $backColor;
border-radius: $radius;
background: $frontColor;
font-size: $width;
@@ -151,17 +145,6 @@ $lineColor: #4a9ef8;
box-shadow: 0 0 6px rgba($color: $shadowColor, $alpha: 0.5); //
text-align: center;
// font-family: 'Helvetica Neue';
&::after {
content: '';
position: absolute;
z-index: 10;
left: 0;
top: 0;
right: 0;
bottom: 0;
box-shadow: inset 0 0 $borderWidth 0 $frontColor; //
border-radius: $radius;
}
.digital:before,
.digital:after {
@@ -209,13 +192,11 @@ $lineColor: #4a9ef8;
&.down.go .front:before {
transform-origin: 50% 100%;
animation: frontFlipDown $speed ease-in-out both;
box-shadow: 0 -2px $borderWidth 0 $frontColor;
box-shadow: 0 -2px 6px rgba($color: $lineColor, $alpha: 0.3);
backface-visibility: hidden;
}
&.down.go .back:after {
animation: backFlipDown $speed ease-in-out both;
box-shadow: 0 2px $borderWidth 0 $frontColor;
backface-visibility: hidden;
}
/*向上翻*/
&.up .front:after {
@@ -233,13 +214,11 @@ $lineColor: #4a9ef8;
&.up.go .front:after {
transform-origin: 50% 0;
animation: frontFlipUp $speed ease-in-out both;
box-shadow: 0 2px $borderWidth 0 $frontColor;
box-shadow: 0 2px 6px rgba($color: $lineColor, $alpha: 0.3);
backface-visibility: hidden;
}
&.up.go .back:before {
animation: backFlipUp $speed ease-in-out both;
box-shadow: 0 -2px $borderWidth 0 $frontColor;
backface-visibility: hidden;
}
}
</style>

View File

@@ -1,3 +0,0 @@
import GoIconify from './index.vue';
export { GoIconify };

View File

@@ -1,34 +0,0 @@
<template>
<iconify-icon
:icon="icon"
:rotate="`${rotate}deg`"
:width="width"
:style="{
color: color
}"
></iconify-icon>
</template>
<script setup lang="ts">
defineProps({
icon: {
type: String,
required: true
},
color: {
type: String,
default: '#999999',
required: false
},
width: {
type: [String, Number],
default: '20',
required: false
},
rotate: {
type: [String, Number],
default: '0',
required: false
}
})
</script>

View File

@@ -14,19 +14,16 @@
</template>
<script lang="ts" setup>
import { useI18n } from "vue-i18n";
import { useLangStore } from '@/store/modules/langStore/langStore'
import { langList } from '@/i18n/index'
import { LangEnum } from '@/enums/styleEnum'
import { icon } from '@/plugins'
const { LanguageIcon } = icon.ionicons5
const { locale } = useI18n();
const langStore = useLangStore()
const options = langList
const handleSelect = (key: LangEnum) => {
locale.value = key;
langStore.changeLang(key)
}
</script>

View File

@@ -1,5 +1,5 @@
<template>
<n-modal v-model:show="modelShowRef" @afterLeave="closeHandle">
<n-modal v-model:show="modelShow" @afterLeave="closeHandle">
<n-list bordered class="go-system-info">
<template #header>
<n-space justify="space-between">
@@ -11,7 +11,7 @@
</template>
<n-list-item>
<n-space class="go-my-2" :size="20">
<n-space :size="20">
<n-text class="item-left">版权声明</n-text>
<n-text>
GoView 版权属于
@@ -21,7 +21,8 @@
</n-list-item>
<n-list-item>
<n-space class="go-my-2" :size="20">
<n-divider style="margin-top: 0" />
<n-space :size="20">
<n-text class="item-left">协议备注</n-text>
<n-text>
请遵守开源 MIT 协议以上声明 <n-text type="error">不可删除</n-text>否则视作侵权行为后果自负
@@ -30,7 +31,8 @@
</n-list-item>
<n-list-item>
<n-space class="go-mt-2" :size="20">
<n-divider style="margin-top: 0" />
<n-space :size="20">
<n-text class="item-left">商业授权</n-text>
<n-text>
若不想保留版权声明请通过仓库/交流群 联系项目作者进行授权
@@ -42,19 +44,13 @@
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
import { icon } from '@/plugins'
const props = defineProps({
modelShow: Boolean
})
const emit = defineEmits(['update:modelShow'])
const { HelpOutlineIcon, CloseIcon } = icon.ionicons5
const modelShowRef = ref(false)
const emit = defineEmits(['update:modelShow'])
watch(() => props.modelShow, (newValue) => {
modelShowRef.value = newValue
defineProps({
modelShow: Boolean
})
const closeHandle = () => {

View File

@@ -1,5 +1,5 @@
<template>
<n-modal v-model:show="modelShowRef" @afterLeave="closeHandle">
<n-modal v-model:show="modelShow" @afterLeave="closeHandle">
<n-list bordered class="go-system-setting">
<template #header>
<n-space justify="space-between">
@@ -69,20 +69,21 @@
</template>
<script script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import { reactive } from 'vue'
import { ListType } from './index.d'
import { useSettingStore } from '@/store/modules/settingStore/settingStore'
import { SettingStoreEnums, ToolsStatusEnum } from '@/store/modules/settingStore/settingStore.d'
import { icon } from '@/plugins'
const props = defineProps({
const { HelpOutlineIcon, CloseIcon } = icon.ionicons5
const emit = defineEmits(['update:modelShow'])
defineProps({
modelShow: Boolean
})
const emit = defineEmits(['update:modelShow'])
const { HelpOutlineIcon, CloseIcon } = icon.ionicons5
const settingStore = useSettingStore()
const modelShowRef = ref(false)
const list = reactive<ListType[]>([
{
@@ -167,10 +168,6 @@ const list = reactive<ListType[]>([
}
])
watch(() => props.modelShow, (newValue) => {
modelShowRef.value = newValue
})
const closeHandle = () => {
emit('update:modelShow', false)
}

View File

@@ -1,3 +0,0 @@
import GoVChart from './index.vue'
export { GoVChart }

View File

@@ -1,242 +0,0 @@
<template>
<div
ref="vChartRef"
v-on="{
...Object.fromEntries(event.map((eventName: string) => [eventName, (eventData: MouseEvent) => eventHandlers(eventData, eventName)]))
}"
></div>
</template>
<script setup lang="ts">
import { ref, PropType, watch, onBeforeUnmount, nextTick, toRaw, toRefs } from 'vue'
import { VChart, type IVChart, type IInitOption, type ISpec } from '@visactor/vchart'
import { transformHandler } from './transformProps'
import { IOption } from '@/packages/components/VChart/index.d'
// 事件说明 v1.13.0 https://www.visactor.io/vchart/api/API/event
const event = [
'mousedown',
'mouseup',
'mouseupoutside',
'rightdown',
'rightup',
'rightupoutside',
'click',
'dblclick',
'mousemove',
'mouseover',
'mouseout',
'mouseenter',
'mouseleave',
'wheel',
'touchstart',
'touchend',
'touchendoutside',
'touchmove',
'touchcancel',
'tap',
'dragstart',
'dragend',
'drag',
'dragenter',
'dragleave',
'dragover',
'drop',
'pan',
'panstart',
'panend',
'press',
'pressup',
'pressend',
'pinch',
'pinchstart',
'pinchend',
'swipe',
'dimensionHover',
'dimensionClick',
'dataZoomChange',
'scrollBarChange',
'brushStart',
'brushChange',
'brushEnd',
'brushClear',
'drill',
'legendItemClick',
'legendItemHover',
'legendItemUnHover',
'legendFilter',
'initialized',
'rendered',
'renderFinished',
'animationFinished',
'layoutStart',
'layoutEnd',
'afterResizef'
]
const emit = defineEmits([
'mousedown',
'mouseup',
'mouseupoutside',
'rightdown',
'rightup',
'rightupoutside',
'click',
'dblclick',
'mousemove',
'mouseover',
'mouseout',
'mouseenter',
'mouseleave',
'wheel',
'touchstart',
'touchend',
'touchendoutside',
'touchmove',
'touchcancel',
'tap',
'dragstart',
'dragend',
'drag',
'dragenter',
'dragleave',
'dragover',
'drop',
'pan',
'panstart',
'panend',
'press',
'pressup',
'pressend',
'pinch',
'pinchstart',
'pinchend',
'swipe',
'dimensionHover',
'dimensionClick',
'dataZoomChange',
'scrollBarChange',
'brushStart',
'brushChange',
'brushEnd',
'brushClear',
'drill',
'legendItemClick',
'legendItemHover',
'legendItemUnHover',
'legendFilter',
'initialized',
'rendered',
'renderFinished',
'animationFinished',
'layoutStart',
'layoutEnd',
'afterResizef'
])
const props = defineProps({
option: {
type: Object as PropType<
IOption & {
dataset: any
}
>,
required: true
},
initOptions: {
type: Object as PropType<
IInitOption & {
deepWatch?: boolean | number
}
>,
required: false,
default: () => ({})
}
})
const vChartRef = ref()
let chart: IVChart
// 解构 props.option排除 dataset
const { dataset, ...restOfOption } = toRefs(props.option)
// 排除 data 监听
watch(
() => ({
...restOfOption
}),
() => {
nextTick(() => {
createOrUpdateChart(props.option)
})
},
{
deep: props.initOptions?.deepWatch || true,
immediate: true
}
)
watch(
() => dataset.value,
() => {
nextTick(() => {
createOrUpdateChart(props.option)
})
},
{
deep: false
}
)
// 更新
const createOrUpdateChart = (
chartProps: IOption & {
dataset: any
}
) => {
if (vChartRef.value && !chart) {
const spec = transformHandler[chartProps.category](chartProps)
chart = new VChart(
{ ...spec, data: chartProps.dataset },
{
dom: vChartRef.value,
...props.initOptions
}
)
chart.renderSync()
return true
} else if (chart) {
const spec = transformHandler[chartProps.category](chartProps)
chart.updateSpec({ ...spec, data: toRaw(chartProps.dataset), dataset: undefined })
return true
}
return false
}
// 刷新
const refresh = () => {
if (chart) {
chart.renderSync()
}
}
// 抛出事件
const eventHandlers = (eventData: MouseEvent, eventName: string) => {
if (event.includes(eventName)) emit(eventName as any, eventData)
}
// 卸载
onBeforeUnmount(() => {
if (chart) {
chart.release()
}
})
defineExpose({
// 重刷新
refresh,
release: () => {
if (chart) {
chart.release()
}
}
})
</script>

View File

@@ -1,32 +0,0 @@
import { cloneDeep } from "lodash"
export default (chartProps: any) => {
const spec = cloneDeep(chartProps)
delete spec.category
// tooltip
const keyFill = spec.tooltip.style.keyLabel.fill
const valueFill = spec.tooltip.style.valueLabel.fill
const titleFill = spec.tooltip.style.titleLabel.keyFill
delete spec.tooltip.style.keyLabel.fill
delete spec.tooltip.style.valueLabel.fill
delete spec.tooltip.style.titleLabel.keyFill
spec.tooltip.style.keyLabel.fontColor = keyFill
spec.tooltip.style.valueLabel.fontColor = valueFill
spec.tooltip.style.titleLabel.fontColor = titleFill
// axis
const { name: xAxisName, ...restXAxisProps } = chartProps.xAxis
const { name: yAxisName, ...restYAxisProps } = chartProps.yAxis
spec.axes = [{
orient: 'bottom',
...restXAxisProps
}, {
orient: 'left',
...restYAxisProps
}]
delete spec.xAxis
delete spec.yAxis
console.log('spec-transform', spec)
return spec
}

View File

@@ -1,8 +0,0 @@
import { ChatCategoryEnum, IOption } from "@/packages/components/VChart/index.d";
import bars from './bars'
export const transformHandler: {
[key: string]: (args: IOption) => any
} = {
[ChatCategoryEnum.BAR]: bars,
// todo: more charts handler
}

View File

@@ -1,3 +0,0 @@
import ChartGlobImage from './index.vue'
export { ChartGlobImage }

View File

@@ -1,31 +0,0 @@
<template>
<img class="list-img" v-lazy="imageInfo" alt="图表图片" />
</template>
<script setup lang="ts">
import { ref, PropType, watch } from 'vue'
import { fetchImages } from '@/packages'
import { ConfigType } from '@/packages/index.d'
const props = defineProps({
chartConfig: {
type: Object as PropType<ConfigType>,
required: true
}
})
const imageInfo = ref('')
// 获取图片
const fetchImageUrl = async () => {
imageInfo.value = await fetchImages(props.chartConfig)
}
watch(
() => props.chartConfig.key,
() => fetchImageUrl(),
{
immediate: true
}
)
</script>

View File

@@ -1,45 +0,0 @@
<template>
<n-radio-group :value="props.modelValue || INHERIT_VALUE" @update:value="handleChange">
<n-space>
<n-tooltip :show-arrow="false" trigger="hover" v-for="item in rendererList" :key="item.value">
<template #trigger>
<n-radio :value="item.value">
{{ item.value }}
</n-radio>
</template>
{{ item.desc }}
</n-tooltip>
</n-space>
</n-radio-group>
</template>
<script setup lang="ts">
import { type EchartsRenderer } from '@/settings/chartThemes'
const props = defineProps<{ modelValue?: EchartsRenderer; includeInherit?: boolean }>()
const emits = defineEmits(['update:modelValue'])
const INHERIT_VALUE = 'inherit'
const handleChange = (val: EchartsRenderer & typeof INHERIT_VALUE) => {
emits('update:modelValue', val === INHERIT_VALUE ? undefined : val)
}
const rendererList = [
{
value: 'svg',
desc: '在缩放场景下具有更好的表现'
},
{
value: 'canvas',
desc: '数据量较大(经验判断 > 1k、较多交互时建议选择'
},
...(props.includeInherit
? [
{
value: INHERIT_VALUE,
desc: '默认继承全局配置'
}
]
: [])
]
</script>

View File

@@ -1,34 +1,4 @@
<template>
<collapse-item name="渲染器">
<setting-item-box :alone="true">
<template #name>
<n-text>全局</n-text>
<n-tooltip trigger="hover">
<template #trigger>
<n-icon size="21" :depth="3">
<help-outline-icon></help-outline-icon>
</n-icon>
</template>
<n-text>所有echarts图表组件默认都将采用所选的渲染器进行渲染</n-text>
</n-tooltip>
</template>
<EchartsRendererSetting v-model="themeSetting.renderer" />
</setting-item-box>
<setting-item-box :alone="true">
<template #name>
<n-text>当前</n-text>
<n-tooltip trigger="hover">
<template #trigger>
<n-icon size="21" :depth="3">
<help-outline-icon></help-outline-icon>
</n-icon>
</template>
<n-text>仅当前组件采用指定渲染器渲染</n-text>
</n-tooltip>
</template>
<EchartsRendererSetting v-model="optionData.renderer" includeInherit />
</setting-item-box>
</collapse-item>
<collapse-item v-if="title" name="标题">
<template #header>
<n-switch v-model:value="title.show" size="small"></n-switch>
@@ -257,36 +227,9 @@
<n-switch v-model:value="legend.show" size="small"></n-switch>
</template>
<setting-item-box name="图例文字">
<setting-item name="颜色">
<setting-item>
<n-color-picker size="small" v-model:value="legend.textStyle.color"></n-color-picker>
</setting-item>
<setting-item name="大小">
<n-input-number v-model:value="legend.textStyle.fontSize" :min="1" size="small"></n-input-number>
</setting-item>
</setting-item-box>
<setting-item-box name="图例位置">
<setting-item name="x轴">
<n-select v-model:value="legend.x" size="small" :options="legendConfig.lengendX" />
</setting-item>
<setting-item name="y轴">
<n-select v-model:value="legend.y" size="small" :options="legendConfig.lengendY" />
</setting-item>
</setting-item-box>
<setting-item-box name="图例信息">
<setting-item name="方向">
<n-select v-model:value="legend.orient" size="small" :options="legendConfig.orient" />
</setting-item>
<setting-item name="形状">
<n-select v-model:value="legend.icon" size="small" :options="legendConfig.shape" />
</setting-item>
</setting-item-box>
<setting-item-box name="图例大小">
<setting-item name="宽">
<n-input-number v-model:value="legend.itemWidth" :min="1" size="small"></n-input-number>
</setting-item>
<setting-item name="高">
<n-input-number v-model:value="legend.itemHeight" :min="1" size="small"></n-input-number>
</setting-item>
</setting-item-box>
</collapse-item>
@@ -336,15 +279,10 @@
</template>
<script setup lang="ts">
import { PropType, computed, watch } from 'vue'
import { PropType, computed } from 'vue'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import { axisConfig, legendConfig } from '@/packages/chartConfiguration/echarts/index'
import { axisConfig } from '@/packages/chartConfiguration/echarts/index'
import { CollapseItem, SettingItemBox, SettingItem, GlobalSettingPosition } from '@/components/Pages/ChartItemSetting'
import { icon } from '@/plugins'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import EchartsRendererSetting from './EchartsRendererSetting.vue'
const { HelpOutlineIcon } = icon.ionicons5
const props = defineProps({
optionData: {
@@ -358,12 +296,6 @@ const props = defineProps({
}
})
const chartEditStore = useChartEditStore()
const themeSetting = computed(() => {
const chartThemeSetting = chartEditStore.getEditCanvasConfig.chartThemeSetting
return chartThemeSetting
})
const title = computed(() => {
return props.optionData.title
})
@@ -387,18 +319,4 @@ const grid = computed(() => {
const visualMap = computed(() => {
return props.optionData.visualMap
})
// 监听legend color颜色改变type = scroll的颜色
watch(() => legend.value && legend.value.textStyle.color, (newVal) => {
if (legend.value && newVal) {
if (!legend.value.pageTextStyle) {
legend.value.pageTextStyle = { color: newVal }
} else {
legend.value.pageTextStyle.color = newVal
}
}
}, {
immediate: true,
deep: true,
})
</script>

View File

@@ -69,22 +69,6 @@
</setting-item>
</setting-item-box>
<!-- 预设滤镜 -->
<div v-if="presetImageList.length" class="preset-filter">
<n-image
class="preset-img"
width="46"
preview-disabled
object-fit="scale-down"
v-for="(item, index) in presetImageList"
:key="index"
:class="{ 'active-preset': item.hueRotate === chartStyles.hueRotate }"
:style="{ filter: `hue-rotate(${item.hueRotate}deg)` }"
:src="item.src"
@click="() => (chartStyles.hueRotate = item.hueRotate)"
></n-image>
</div>
<!-- 混合模式 -->
<setting-item-box v-if="!isCanvas" :alone="true">
<template #name>
@@ -165,12 +149,10 @@
</template>
<script setup lang="ts">
import { ref, PropType } from 'vue'
import { PropType } from 'vue'
import { PickCreateComponentType, BlendModeEnumList } from '@/packages/index.d'
import { SettingItemBox, SettingItem, CollapseItem } from '@/components/Pages/ChartItemSetting'
import { icon } from '@/plugins'
import logoImg from '@/assets/logo.png'
import { useDesignStore } from '@/store/modules/designStore/designStore'
const props = defineProps({
isGroup: {
@@ -193,48 +175,10 @@ const { HelpOutlineIcon } = icon.ionicons5
const sliderFormatTooltip = (v: string) => {
return `${(parseFloat(v) * 100).toFixed(0)}%`
}
// 角度格式化
const degFormatTooltip = (v: string) => {
return `${v}deg`
}
// 预设滤镜
interface presetImageData {
index: number
src: string
hueRotate: number
}
const presetImageList = ref([] as presetImageData[])
for (let i = 1; i <= 12; i++) {
presetImageList.value.push({
index: i,
src: logoImg,
hueRotate: i * 30
})
}
</script>
<style lang="scss" scoped>
// 预设滤镜
.preset-filter {
margin: 20px 0 10px 0;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.preset-img {
margin-bottom: 10px;
padding: 2px;
border-radius: 6px;
transition: 0.2s all;
cursor: pointer;
&:hover {
box-shadow: 0 0 0 2px #66a9c9;
}
}
.active-preset {
box-shadow: 0 0 0 2px #66a9c9;
}
}
</style>
<style lang="scss" scoped></style>

View File

@@ -99,7 +99,6 @@ const colorSelectHandle = (color: AppThemeColorType) => {
overflow: hidden;
border-radius: 5px;
@extend .go-background-filter-shallow;
backdrop-filter: none;
}
&-color {
width: 8px;

View File

@@ -36,7 +36,7 @@
</div>
<div class="model-footer">
中国色列表来自于
<n-a href="http://zhongguose.com" target="_blank">http://zhongguose.com</n-a>
<n-a href="http://zhongguose.com">http://zhongguose.com</n-a>
</div>
</div>
</n-modal>
@@ -157,7 +157,6 @@ $height: 85vh;
}
}
.model-footer {
z-index: 1;
text-align: end;
}
}

View File

@@ -1,71 +0,0 @@
<template>
<collapse-item v-model:name="axis.name">
<template #header>
<n-switch v-model:value="axis.visible" size="small"></n-switch>
</template>
<setting-item-box name="轴标签">
<setting-item name="可见性">
<n-space>
<n-switch v-model:value="axis.label.visible" size="small"></n-switch>
</n-space>
</setting-item>
<setting-item name="角度">
<n-input-number v-model:value="axis.label.style.angle" :min="0" :max="360" size="small" />
</setting-item>
<FontStyle :style="axis.label.style"></FontStyle>
</setting-item-box>
<setting-item-box name="轴标题">
<setting-item name="可见性">
<n-space>
<n-switch v-model:value="axis.title.visible" size="small"></n-switch>
</n-space>
</setting-item>
<setting-item name="标题内容">
<n-input v-model:value="axis.title.style.text" size="small"></n-input>
</setting-item>
<FontStyle :style="axis.title.style"></FontStyle>
</setting-item-box>
<setting-item-box name="轴线">
<setting-item name="可见性">
<n-space>
<n-switch v-model:value="axis.domainLine.visible" size="small"></n-switch>
</n-space>
</setting-item>
<setting-item name=""> </setting-item>
<setting-item name="粗细">
<n-input-number v-model:value="axis.domainLine.style.lineWidth" :min="0" size="small" />
</setting-item>
<setting-item name="颜色">
<n-color-picker v-model:value="axis.domainLine.style.stroke" size="small" />
</setting-item>
</setting-item-box>
<setting-item-box name="网格线">
<setting-item name="可见性">
<n-space>
<n-switch v-model:value="axis.grid.visible" size="small"></n-switch>
</n-space>
</setting-item>
<setting-item name=""> </setting-item>
<setting-item name="粗细">
<n-input-number v-model:value="axis.grid.style.lineWidth" :min="0" size="small" />
</setting-item>
<setting-item name="颜色">
<n-color-picker v-model:value="axis.grid.style.stroke" size="small" />
</setting-item>
</setting-item-box>
</collapse-item>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import FontStyle from './common/FontStyle.vue'
import { vChartGlobalThemeJsonType } from '@/settings/vchartThemes/index'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
defineProps({
axis: {
type: Object as PropType<vChartGlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -1,38 +0,0 @@
<template>
<!-- todo 补充常用配置项 -->
<div v-if="optionData.legends">
<div v-for="(legendItem, index) in optionData.legends" :key="index">
<collapse-item name="图例">
<template #header>
<n-switch v-model:value="legendItem.visible" size="small"></n-switch>
</template>
<setting-item-box name="布局">
<setting-item name="位置">
<n-select v-model:value="legendItem.orient" size="small" :options="legendsConfig.orient" />
</setting-item>
<setting-item name="对齐方式">
<n-select v-model:value="legendItem.position" size="small" :options="legendsConfig.position" />
</setting-item>
</setting-item-box>
<setting-item-box name="项配置">
<FontStyle :style="legendItem.item.label.style"></FontStyle>
</setting-item-box>
</collapse-item>
</div>
</div>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { legendsConfig } from '@/packages/chartConfiguration/vcharts/index'
import FontStyle from './common/FontStyle.vue'
import { vChartGlobalThemeJsonType } from '@/settings/vchartThemes/index'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
defineProps({
optionData: {
type: Object as PropType<vChartGlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -1,47 +0,0 @@
<template>
<!-- todo 补充常用配置项 -->
<div v-if="optionData.tooltip">
<collapse-item name="提示框">
<template #header>
<n-switch v-model:value="optionData.tooltip.visible" size="small"></n-switch>
</template>
<setting-item-box name="框">
<setting-item name="填充">
<n-color-picker v-model:value="optionData.tooltip.style.panel.backgroundColor" size="small" />
</setting-item>
<setting-item name="瞄边">
<n-color-picker v-model:value="optionData.tooltip.style.panel.border.color" size="small" />
</setting-item>
<setting-item name="粗细">
<n-input-number v-model:value="optionData.tooltip.style.panel.border.width" :min="0" size="small" />
</setting-item>
<setting-item name="圆角">
<n-input-number v-model:value="optionData.tooltip.style.panel.border.radius" :min="0" size="small" />
</setting-item>
</setting-item-box>
<setting-item-box name="标题">
<FontStyle :style="optionData.tooltip.style.titleLabel"></FontStyle>
</setting-item-box>
<setting-item-box name="名称">
<FontStyle :style="optionData.tooltip.style.keyLabel"></FontStyle>
</setting-item-box>
<setting-item-box name="值">
<FontStyle :style="optionData.tooltip.style.valueLabel"></FontStyle>
</setting-item-box>
</collapse-item>
</div>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import FontStyle from './common/FontStyle.vue'
import { vChartGlobalThemeJsonType } from '@/settings/vchartThemes/index'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
defineProps({
optionData: {
type: Object as PropType<vChartGlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -1,18 +0,0 @@
<template>
<!-- 图例 -->
<Legends :optionData="optionData"></Legends>
<Tooltip :optionData="optionData"></Tooltip>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { vChartGlobalThemeJsonType } from '@/settings/vchartThemes/index'
import Legends from './Legends.vue'
import Tooltip from './Tooltip.vue'
const props = defineProps({
optionData: {
type: Object as PropType<vChartGlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -1,33 +0,0 @@
<template>
<!-- todo 补充常用配置项 -->
<!-- <div v-if="style"> -->
<!-- <setting-item-box v-if="style" name=""> -->
<setting-item name="颜色">
<n-color-picker v-model:value="style.fill" size="small" />
</setting-item>
<setting-item name="大小">
<n-input-number v-model:value="style.fontSize" :min="1" size="small" />
</setting-item>
<setting-item name="字体">
<n-select v-model:value="style.fontFamily" :options="fontStyleConfig.fontFamily" size="small" />
</setting-item>
<setting-item name="字重">
<n-select v-model:value="style.fontSize" :options="fontStyleConfig.fontWeight" size="small" />
</setting-item>
<!-- </setting-item-box> -->
<!-- </div> -->
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { fontStyleConfig } from '@/packages/chartConfiguration/vcharts/index'
import { FontType } from '@/settings/vchartThemes/index'
import { SettingItem } from '@/components/Pages/ChartItemSetting'
defineProps({
style: {
type: Object as PropType<FontType>,
required: true
}
})
</script>

View File

@@ -1,4 +0,0 @@
import VChartGlobalSetting from './VChartGlobalSetting.vue'
import Axis from './Axis.vue'
export { VChartGlobalSetting, Axis }

View File

@@ -9,14 +9,6 @@ export enum DragKeyEnum {
DRAG_KEY = 'ChartData'
}
// 不同页面保存操作
export enum SavePageEnum {
CHART = 'SaveChart',
CHART_TO_PREVIEW = 'ChartToPreview',
JSON = 'SaveJSON',
CLOSE = 'close'
}
// 操作枚举
export enum MenuEnum {
// 移动
@@ -48,7 +40,6 @@ export enum MenuEnum {
UN_GROUP = 'unGroup',
// 后退
BACK = 'back',
// 前进
FORWORD = 'forward',
// 保存
SAVE = 'save',
@@ -69,8 +60,7 @@ export enum WinKeyboard {
ALT = ' alt',
CTRL_SOURCE_KEY = 'control',
SHIFT_SOURCE_KEY = 'shift',
ALT_SOURCE_KEY = 'alt',
SPACE = 'Space'
ALT_SOURCE_KEY = 'alt'
}
// Mac 键盘枚举
@@ -81,6 +71,17 @@ export enum MacKeyboard {
ALT = '⌥',
CTRL_SOURCE_KEY = '⌘',
SHIFT_SOURCE_KEY = '⇧',
ALT_SOURCE_KEY = '⌥',
SPACE = 'Space'
ALT_SOURCE_KEY = '⌥'
}
// 同步状态枚举
export enum SyncEnum {
// 等待
PENDING,
// 开始
START,
// 成功
SUCCESS,
// 失败
FAILURE
}

View File

@@ -1,59 +0,0 @@
// 基础事件类型(vue不加 on)
export enum BaseEvent {
// 点击
ON_CLICK = 'click',
// 双击
ON_DBL_CLICK = 'dblclick',
// 移入
ON_MOUSE_ENTER = 'mouseenter',
// 移出
ON_MOUSE_LEAVE = 'mouseleave'
}
// 组件交互回调事件
export enum InteractEvents {
INTERACT_ON = 'interactOn',
INTERACT_COMPONENT_ID = 'interactComponentId',
INTERACT_FN = 'interactFn'
}
// 全局组件交互回调事件触发的类型(当然可以自定义名称)
export enum InteractEventOn {
CLICK = 'click',
CHANGE = 'change'
}
// 确定交互组件触发类型 key名称
export const COMPONENT_INTERACT_EVENT_KET = 'componentInteractEventKey'
// 交互式组件的触发配置
export type InteractActionsType = {
interactType: InteractEventOn
interactName: string
componentEmitEvents: { [T: string]: { value: any; label: string }[] }
}
// vue3 生命周期事件
export enum EventLife {
// 渲染之后
VNODE_MOUNTED = 'vnodeMounted',
// 渲染之前
VNODE_BEFORE_MOUNT = 'vnodeBeforeMount'
}
// 内置字符串函数对象列表
export const excludeParseEventKeyList = [
EventLife.VNODE_BEFORE_MOUNT,
EventLife.VNODE_MOUNTED,
BaseEvent.ON_CLICK,
BaseEvent.ON_DBL_CLICK,
BaseEvent.ON_MOUSE_ENTER,
BaseEvent.ON_MOUSE_LEAVE,
//过滤器
'filter'
]
// 内置字符串函数键值列表
export const excludeParseEventValueList = [
// 请求里的函数语句
'javascript:'
]

View File

@@ -7,6 +7,7 @@ export enum ResultEnum {
SERVER_ERROR = 500,
SERVER_FORBIDDEN = 403,
NOT_FOUND = 404,
TOKEN_OVERDUE = 886,
TIMEOUT = 60000
}
@@ -15,9 +16,7 @@ export enum RequestDataTypeEnum {
// 静态数据
STATIC = 0,
// 请求数据
AJAX = 1,
// 数据池
Pond = 2
AJAX = 1
}
// 请求主体类型
@@ -28,6 +27,12 @@ export enum RequestContentTypeEnum {
SQL = 1
}
// 头部
export enum RequestHttpHeaderEnum {
TOKEN = 'Token',
COOKIE = 'Cookie'
}
/**
* @description: 请求方法
*/

View File

@@ -12,12 +12,6 @@ export enum PreviewEnum {
CHART_PREVIEW_NAME = 'ChartPreview',
}
export enum EditEnum {
// 图表JSON编辑
CHART_EDIT = '/chart/edit/:id(.*)*',
CHART_EDIT_NAME = 'ChartEdit',
}
export enum PageEnum {
// 登录
BASE_LOGIN = '/login',
@@ -26,10 +20,15 @@ export enum PageEnum {
//重定向
REDIRECT = '/redirect',
REDIRECT_NAME = 'Redirect',
// 未发布
REDIRECT_UN_PUBLISH = '/redirect/unPublish',
REDIRECT_UN_PUBLISH_NAME = 'redirect-un-publish',
// 重载
RELOAD = '/reload',
RELOAD_NAME = 'Reload',
// 首页
BASE_HOME = '/project',
BASE_HOME_NAME = 'Project',

View File

@@ -12,7 +12,5 @@ export enum StorageEnum {
// 工作台布局配置
GO_CHART_LAYOUT_STORE = 'GO_CHART_LAYOUT',
// 工作台需要保存的数据
GO_CHART_STORAGE_LIST = 'GO_CHART_STORAGE_LIST',
// 用户存储的图片媒体
GO_USER_MEDIA_PHOTOS = 'GO_USER_MEDIA_PHOTOS'
GO_CHART_STORAGE_LIST = 'GO_CHART_STORAGE_LIST'
}

View File

@@ -2,8 +2,4 @@ export * from '@/hooks/useTheme.hook'
export * from '@/hooks/usePreviewScale.hook'
export * from '@/hooks/useCode.hook'
export * from '@/hooks/useChartDataFetch.hook'
export * from '@/hooks/useChartDataPondFetch.hook'
export * from '@/hooks/useLifeHandler.hook'
export * from '@/hooks/useLang.hook'
export * from '@/hooks/useChartInteract.hook'
export * from '@/hooks/useVCharts.hook'
export * from '@/hooks/useLifeHandler.hook'

View File

@@ -1,26 +0,0 @@
import { inject, type Ref } from 'vue'
import { EchartsRenderer } from '@/settings/chartThemes'
import { SCALE_KEY } from '@/views/preview/hooks/useScale.hook'
import { use } from 'echarts/core'
import { CanvasRenderer, SVGRenderer } from 'echarts/renderers'
use([CanvasRenderer, SVGRenderer])
type InitOptions = {
renderer: EchartsRenderer
devicePixelRatio?: number
}
// 获取需要给当前echarts组件设置什么初始值
export function useCanvasInitOptions(option: any, themeSetting: any) {
const renderer = option.renderer || themeSetting.renderer
const initOptions: InitOptions = { renderer }
const scaleRef = inject<Ref<{ width: number; height: number }>>(SCALE_KEY) || { value: { width: 1, height: 1 } }
if (renderer === 'canvas') {
initOptions.devicePixelRatio = Math.ceil(
Math.max(window.devicePixelRatio, scaleRef.value.width, scaleRef.value.height)
)
}
return initOptions
}

View File

@@ -1,130 +1,99 @@
import { ref, toRefs, toRaw, watch } from 'vue'
import type VChart from 'vue-echarts'
import { customizeHttp } from '@/api/http'
import { useChartDataPondFetch } from '@/hooks/'
import { CreateComponentType, ChartFrameEnum } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { RequestDataTypeEnum } from '@/enums/httpEnum'
import { isPreview, newFunctionHandle, intervalUnitHandle } from '@/utils'
import { setOption } from '@/packages/public/chart'
import { isNil } from 'lodash'
// 获取类型
type ChartEditStoreType = typeof useChartEditStore
/**
* setdata 数据监听与更改
* @param targetComponent
* @param useChartEditStore 若直接引会报错,只能动态传递
* @param updateCallback 自定义更新函数
*/
export const useChartDataFetch = (
targetComponent: CreateComponentType,
useChartEditStore: ChartEditStoreType,
updateCallback?: (...args: any) => any
) => {
const vChartRef = ref<typeof VChart | null>(null)
let fetchInterval: any = 0
// 数据
const { addGlobalDataInterface } = useChartDataPondFetch()
// 组件类型
const { chartFrame } = targetComponent.chartConfig
// eCharts 组件配合 vChart 库更新方式
const echartsUpdateHandle = (dataset: any) => {
if (chartFrame === ChartFrameEnum.ECHARTS) {
if (vChartRef.value) {
setOption(vChartRef.value, { dataset: dataset }, false)
}
}
}
const requestIntervalFn = () => {
const chartEditStore = useChartEditStore()
// 全局数据
const {
requestOriginUrl,
requestIntervalUnit: globalUnit,
requestInterval: globalRequestInterval
} = toRefs(chartEditStore.getRequestGlobalConfig)
// 目标组件
const {
requestDataType,
requestUrl,
requestIntervalUnit: targetUnit,
requestInterval: targetInterval
} = toRefs(targetComponent.request)
// 非请求类型
if (requestDataType.value !== RequestDataTypeEnum.AJAX) return
try {
// 处理地址
// @ts-ignore
if (requestUrl?.value) {
// requestOriginUrl 允许为空
const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl.value
if (!completePath) return
clearInterval(fetchInterval)
const fetchFn = async () => {
const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.getRequestGlobalConfig))
if (res) {
try {
const filter = targetComponent.filter
const { data } = res
echartsUpdateHandle(newFunctionHandle(data, res, filter))
// 更新回调函数
if (updateCallback) {
updateCallback(newFunctionHandle(data, res, filter))
}
} catch (error) {
console.error(error)
}
}
}
// 普通初始化与组件交互处理监听
watch(
() => targetComponent.request.requestParams,
() => {
fetchFn()
},
{
immediate: true,
deep: true
}
)
// 定时时间
const time = targetInterval && !isNil(targetInterval.value) ? targetInterval.value : globalRequestInterval.value
// 单位
const unit = targetInterval && !isNil(targetInterval.value) ? targetUnit.value : globalUnit.value
// 开启轮询
if (time) {
fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit))
}
}
// eslint-disable-next-line no-empty
} catch (error) {
console.log(error)
}
}
if (isPreview()) {
targetComponent.request.requestDataType === RequestDataTypeEnum.Pond
? addGlobalDataInterface(targetComponent, useChartEditStore, (newData: any) => {
echartsUpdateHandle(newData)
if (updateCallback) updateCallback(newData)
})
: requestIntervalFn()
} else {
requestIntervalFn()
}
return { vChartRef }
}
import { ref, toRefs, toRaw } from 'vue'
import type VChart from 'vue-echarts'
import { customizeHttp } from '@/api/http'
import { CreateComponentType, ChartFrameEnum } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { RequestDataTypeEnum } from '@/enums/httpEnum'
import { isPreview, newFunctionHandle, intervalUnitHandle } from '@/utils'
// 获取类型
type ChartEditStoreType = typeof useChartEditStore
/**
* setdata 数据监听与更改
* @param targetComponent
* @param useChartEditStore 若直接引会报错,只能动态传递
* @param updateCallback 自定义更新函数
*/
export const useChartDataFetch = (
targetComponent: CreateComponentType,
useChartEditStore: ChartEditStoreType,
updateCallback?: (...args: any) => any
) => {
const vChartRef = ref<typeof VChart | null>(null)
let fetchInterval: any = 0
const requestIntervalFn = () => {
const chartEditStore = useChartEditStore()
// 全局数据
const {
requestOriginUrl,
requestIntervalUnit: globalUnit,
requestInterval: globalRequestInterval
} = toRefs(chartEditStore.getRequestGlobalConfig)
// 目标组件
const {
requestDataType,
requestUrl,
requestIntervalUnit: targetUnit,
requestInterval: targetInterval
} = toRefs(targetComponent.request)
// 组件类型
const { chartFrame } = targetComponent.chartConfig
// 非请求类型
if (requestDataType.value !== RequestDataTypeEnum.AJAX) return
try {
// 处理地址
// @ts-ignore
if (requestUrl?.value) {
// requestOriginUrl 允许为空
const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl.value
if (!completePath) return
clearInterval(fetchInterval)
const fetchFn = async () => {
const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.requestGlobalConfig))
if (res) {
try {
const filter = targetComponent.filter
// eCharts 组件配合 vChart 库更新方式
if (chartFrame === ChartFrameEnum.ECHARTS) {
if (vChartRef.value) {
vChartRef.value.setOption({ dataset: newFunctionHandle(res?.data, res, filter) })
}
}
// 更新回调函数
if (updateCallback) {
updateCallback(newFunctionHandle(res?.data, res, filter))
}
} catch (error) {
console.error(error)
}
}
}
// 立即调用
fetchFn()
// 定时时间
const time = targetInterval && targetInterval.value ? targetInterval.value : globalRequestInterval.value
// 单位
const unit = targetInterval && targetInterval.value ? targetUnit.value : globalUnit.value
// 开启轮询
if (time) fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit))
}
// eslint-disable-next-line no-empty
} catch (error) {
console.log(error)
}
}
isPreview() && requestIntervalFn()
return { vChartRef }
}

View File

@@ -1,130 +0,0 @@
import { toRaw, watch, computed, ComputedRef } from 'vue'
import { customizeHttp } from '@/api/http'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { RequestGlobalConfigType, RequestDataPondItemType } from '@/store/modules/chartEditStore/chartEditStore.d'
import { newFunctionHandle, intervalUnitHandle } from '@/utils'
// 获取类型
type ChartEditStoreType = typeof useChartEditStore
// 数据池存储的数据类型
type DataPondMapType = {
updateCallback: (...args: any) => any
filter?: string | undefined
}
// 数据池 Map 中请求对应 callback
const mittDataPondMap = new Map<string, DataPondMapType[]>()
// 创建单个数据项轮询接口
const newPondItemInterval = (
requestGlobalConfig: RequestGlobalConfigType,
requestDataPondItem: ComputedRef<RequestDataPondItemType>,
dataPondMapItem?: DataPondMapType[]
) => {
if (!dataPondMapItem) return
let fetchInterval: any = 0
clearInterval(fetchInterval)
// 请求
const fetchFn = async () => {
try {
const res = await customizeHttp(toRaw(requestDataPondItem.value.dataPondRequestConfig), toRaw(requestGlobalConfig))
if (res) {
try {
// 遍历更新回调函数
dataPondMapItem.forEach(item => {
item.updateCallback(newFunctionHandle(res?.data, res, item.filter))
})
} catch (error) {
console.error(error)
return error
}
}
} catch (error) {
return error
}
}
watch(
() => requestDataPondItem.value.dataPondRequestConfig.requestParams.Params,
() => {
fetchFn()
},
{
immediate: false,
deep: true
}
)
// 立即调用
fetchFn()
const targetInterval = requestDataPondItem.value.dataPondRequestConfig.requestInterval
const targetUnit = requestDataPondItem.value.dataPondRequestConfig.requestIntervalUnit
const globalRequestInterval = requestGlobalConfig.requestInterval
const globalUnit = requestGlobalConfig.requestIntervalUnit
// 定时时间
const time = targetInterval ? targetInterval : globalRequestInterval
// 单位
const unit = targetInterval ? targetUnit : globalUnit
// 开启轮询
if (time) fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit))
}
/**
* 数据池接口处理
*/
export const useChartDataPondFetch = () => {
// 新增全局接口
const addGlobalDataInterface = (
targetComponent: CreateComponentType,
useChartEditStore: ChartEditStoreType,
updateCallback: (...args: any) => any
) => {
const chartEditStore = useChartEditStore()
const { requestDataPond } = chartEditStore.getRequestGlobalConfig
// 组件对应的数据池 Id
const requestDataPondId = targetComponent.request.requestDataPondId as string
// 新增数据项
const mittPondIdArr = mittDataPondMap.get(requestDataPondId) || []
mittPondIdArr.push({
updateCallback: updateCallback,
filter: targetComponent.filter
})
mittDataPondMap.set(requestDataPondId, mittPondIdArr)
}
// 清除旧数据
const clearMittDataPondMap = () => {
mittDataPondMap.clear()
}
// 初始化数据池
const initDataPond = (useChartEditStore: ChartEditStoreType) => {
const { requestGlobalConfig } = useChartEditStore()
const chartEditStore = useChartEditStore()
// 根据 mapId 查找对应的数据池配置
for (let pondKey of mittDataPondMap.keys()) {
const requestDataPondItem = computed(() => {
return requestGlobalConfig.requestDataPond.find(item => item.dataPondId === pondKey)
}) as ComputedRef<RequestDataPondItemType>
if (requestDataPondItem) {
newPondItemInterval(chartEditStore.requestGlobalConfig, requestDataPondItem, mittDataPondMap.get(pondKey))
}
}
}
return {
addGlobalDataInterface,
clearMittDataPondMap,
initDataPond
}
}

View File

@@ -1,59 +0,0 @@
import { toRefs } from 'vue'
import { isPreview } from '@/utils'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
// 获取类型
type ChartEditStoreType = typeof useChartEditStore
// Params 参数修改触发 api 更新图表请求
export const useChartInteract = (
chartConfig: CreateComponentType,
useChartEditStore: ChartEditStoreType,
param: { [T: string]: any },
interactEventOn: string
) => {
if (!isPreview()) return
const chartEditStore = useChartEditStore()
const { interactEvents } = chartConfig.events
const fnOnEvent = interactEvents.filter(item => {
return item.interactOn === interactEventOn
})
if (fnOnEvent.length === 0) return
fnOnEvent.forEach(item => {
const globalConfigPindAprndex = chartEditStore.requestGlobalConfig.requestDataPond.findIndex(cItem =>
cItem.dataPondId === item.interactComponentId
)
if (globalConfigPindAprndex !== -1) {
const { Params, Header } = toRefs(chartEditStore.requestGlobalConfig.requestDataPond[globalConfigPindAprndex].dataPondRequestConfig.requestParams)
Object.keys(item.interactFn).forEach(key => {
if (key in Params.value) {
Params.value[key] = param[item.interactFn[key]]
}
if (key in Header.value) {
Header.value[key] = param[item.interactFn[key]]
}
})
} else {
const index = chartEditStore.fetchTargetIndex(item.interactComponentId)
if (index === -1) return
const { Params, Header } = toRefs(chartEditStore.componentList[index].request.requestParams)
Object.keys(item.interactFn).forEach(key => {
if (key in Params.value) {
Params.value[key] = param[item.interactFn[key]]
}
if (key in Header.value) {
Header.value[key] = param[item.interactFn[key]]
}
})
}
})
}
// 联动事件触发的 type 变更时,清除当前绑定内容
export const clearInteractEvent = (chartConfig: CreateComponentType) => {
}

View File

@@ -1,24 +0,0 @@
import { computed } from 'vue'
import { LangEnum } from '@/enums/styleEnum'
import { useLangStore } from '@/store/modules/langStore/langStore'
import { zhCN, enUS, dateEnUS, dateZhCN } from 'naive-ui'
type LangStoreType = typeof useLangStore
// 语言切换
export const useLang = () => {
const lang = useLangStore()
const locale = computed(() => {
return lang.getLang === LangEnum.ZH ? zhCN : enUS
})
const dateLocale = computed(() => {
return lang.getLang === LangEnum.ZH ? dateZhCN : dateEnUS
})
return {
locale,
dateLocale
}
}

View File

@@ -1,68 +1,269 @@
import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
import { EventLife } from '@/enums/eventEnum'
import { CreateComponentType, EventLife } from '@/packages/index.d'
import * as echarts from 'echarts'
import { BackEndFactory } from '@/backend/ibackend'
import { reactive, toRef , watch, computed} from 'vue';
/**
* 事件测试:
*
切换显示名称为 饼图 和 柱状图 的图标
const range = runtime.fn.selectComponents("饼图 柱状图")
const h = runtime.fn.getChartConfig(range, "hide")
runtime.fn.setChartConfig(range, "hide", !h)
修改一个名称 柱状图001 组件id 2wolqibrx3c000 的图表数据,以下两句等效
runtime.fn.setChartConfig("柱状图001", "dataset", {"dimensions":["product","data1","data2"],"source":[{"product":"Mon","data1":120,"data2":130}]})
runtime.fn.setChartConfig("#2wolqibrx3c000", "dataset", {"dimensions":["product","data1","data2"],"source":[{"product":"Mon","data1":120,"data2":230}]})
找到一个组并隐藏
const c = runtime.fn.selectOneComponent("分组")
if(c){
console.log(runtime.fn.getChartConfig(c, "isGroup" ))
runtime.fn.setChartConfig(c, "hide", true)
}
调用组件 exposed 函数的例子
组件中增加: defineExpose({ actionTest:actionTest })
以下调用名称为 柱状图 组件的 actionTest
runtime.fn.callExposed("柱状图", "actionTest")
数据驱动界面:
图表A 的 MOUNTED 加入对 status1 的 Watch = "0" 隐藏
watch(()=>runtime.variables.status1, newValue => runtime.fn.setChartConfig(this, "hide", newValue == "0"))
图表B 的 MOUNTED 也加入对 status1 的 Watch = "1" 隐藏
watch(()=>runtime.variables.status1, newValue => runtime.fn.setChartConfig(this, "hide", newValue == "1"))
点击事件代码实现图表A 和 图表B 的切换显示:
if(runtime.variables.status1 == "0"){
runtime.variables.status1 = "1"
} else{
runtime.variables.status1 = "0"
}
图表A 的 MOUNTED 加入对 data1 的 Watch
watch(()=>runtime.datasets.data1,
newValue => runtime.fn.setChartConfig(this, "dataset", newValue))
图表B 的 MOUNTED 加入对 data1 的 Watch
watch(()=>runtime.datasets.data1,
newValue => runtime.fn.setChartConfig(this, "dataset", newValue))
点击事件代码修改datasets.data1同时更新图表A 和 图表B 的数据
runtime.datasets.data1 = {"dimensions":["product","data1","data2"],"source":[{"product":"Mon","data1":120,"data2":230}]}
*
*/
// * 初始化
export const useSystemInit = async () => {
const res = await BackEndFactory.init({}) as any;
}
const getOneChartConfig = (component:any, configName:string, params?:any)=>{
let root = null
if(component.proxy.chartConfig) root = component.proxy.chartConfig
else if (component.proxy.groupData) root = component.proxy.groupData
// if(!root) return null
switch(configName){
case "hide":
return root.status.hide
break;
case "dataset":
return root.option.dataset
break;
case "isGroup":
return root.isGroup
break;
case "key":
return root.key
break;
case "attr":
return root.attr
break;
case "name":
return root.chartConfig.title
}
}
const setOneChartConfig = (component:any, configName:string, newValue:any, params?:any)=>{
let root = null
if(component.proxy.chartConfig) root = component.proxy.chartConfig
else if (component.proxy.groupData) root = component.proxy.groupData
switch(configName){
case "hide":
root.status.hide = newValue
break;
case "dataset":
root.option.dataset = newValue
break;
}
}
/**
* 选择器语法:参考 css selectors
* 名称 组件名称,不能有空格和特殊字符(. # 引号等)
* [name=名称] Todo
* #id 组件编号
* .key 组件类型 Todo
* @param selectors
* @returns []
*/
const getComponentsBySelectors = (selectors:string):any[]=>{
// 返回:数组,可能多个
let rtn:any[] = []
const ar = selectors.split(" ")
for(let a of ar){
rtn = rtn.concat(getComponentsBySelector(a))
}
return rtn
}
const getComponentsBySelector = (selector:string):any[]=>{
// 返回:数组,可能多个
const rtn:any[] = []
if(selector.substring(0,1) == "#")
{
const key = selector.substring(1)
if(key in components){
return [components[key]]
}
return rtn
}
for (let key in components) {
if(getOneChartConfig(components[key], "name") == selector){
rtn.push(components[key])
}
}
return rtn
}
// 所有图表组件集合对象
const components: { [K in string]?: any } = {}
// 项目提供的npm 包变量
export const npmPkgs = { echarts }
// 组件事件处理 hook
export const useLifeHandler = (chartConfig: CreateComponentType | CreateComponentGroupType) => {
if (!chartConfig.events) return {}
// 处理基础事件
const baseEvent: { [key: string]: any } = {}
for (const key in chartConfig.events.baseEvent) {
const fnStr: string | undefined = (chartConfig.events.baseEvent as any)[key]
// 动态绑定基础事件
if (fnStr) {
baseEvent[key] = generateBaseFunc(fnStr)
const runtime = {
// 变量,管理各种状态
variables:reactive({}),
// 数据集
datasets:reactive({}),
// 组件列表 {}
components:components,
// 帮助类
fn:{
/**
* 选择一个组件
* @param selectors string 选择器语法 | component | [component]
* @return 第一个符合要求的 component 或 null
*/
selectOneComponent:(selectors:any)=>{
const cList = runtime.fn.selectComponents(selectors)
if(cList.length > 0){
return cList[0]
}
return null
},
/**
* 选择组件
* @param selectors string 选择器语法 | component | [component]
* @return 要求的 [component] 或 []
*/
selectComponents:(selectors:any):any[]=>{
if(!selectors) return []
if(typeof selectors == "string") return getComponentsBySelectors(selectors)
if(Array.isArray(selectors)) return selectors
return [selectors]
},
/**
* 获取组件的值,如果多个,使用第一个
* @param selectors string 选择器语法 | component | [component]
* @param configName 配置名称
* @param params 备用参数,可选
* @returns 配置的值
*/
getChartConfig:(selectors:any, configName:string, params?:any)=>{
const component:any = runtime.fn.selectOneComponent(selectors)
if(!component && !component.proxy) return null
return getOneChartConfig(component, configName, params)
},
/**
* 设置组件的值,支持多个
* @param selectors string 选择器语法 | component | [component]
* @param configName 配置名称
* @param newValue 新值
* @param params 备用参数,可选
* @returns 配置的值
*/
setChartConfig:(selectors:any, configName:string, newValue:any, params?:any)=>{
const cList:any[] = runtime.fn.selectComponents(selectors)
for(let c of cList){
if(!c && !c.proxy) return null
setOneChartConfig(c, configName, newValue, params)
}
},
/**
* 调用组件暴露的函数,组件中使用 defineExpose 进行定义
* @param selectors string 选择器语法 | component | [component]
* @param action 组件中 defineExpose 的函数名
* @param params 调用的参数只支持一个参数或没有参数
* @returns 无
*/
callExposed:(selectors:any, action:string, params?:any)=>{
const cList:any[] = runtime.fn.selectComponents(selectors)
for(let c of cList){
if(!c && !c.exposed) return null
if(typeof c.exposed[action] == "function") c.exposed[action](params)
}
}
}
}
// 项目提供的npm 包变量
export const npmPkgs = { echarts, toRef , watch, computed, runtime }
export const useLifeHandler = (chartConfig: CreateComponentType) => {
const events = chartConfig.events || {}
console.log("chartConfig.events")
console.log(chartConfig.events)
// 生成生命周期事件
const events = chartConfig.events.advancedEvents || {}
const lifeEvents = {
[EventLife.VNODE_BEFORE_MOUNT](e: any) {
let lifeEvents = {
[EventLife.BEFORE_MOUNT](e: any) {
// 存储组件
components[chartConfig.id] = e.component
const fnStr = (events[EventLife.VNODE_BEFORE_MOUNT] || '').trim()
generateFunc(fnStr, e)
const fnStr = (events[EventLife.BEFORE_MOUNT] || '').trim()
generateFunc(fnStr, e, e.component)
},
[EventLife.VNODE_MOUNTED](e: any) {
const fnStr = (events[EventLife.VNODE_MOUNTED] || '').trim()
generateFunc(fnStr, e)
[EventLife.MOUNTED](e: any) {
const fnStr = (events[EventLife.MOUNTED] || '').trim()
generateFunc(fnStr, e, e.component)
}
}
return { ...baseEvent, ...lifeEvents }
}
/**
* 生成基础函数
* @param fnStr 用户方法体代码
* @param event 鼠标事件
*/
export function generateBaseFunc(fnStr: string) {
try {
return new Function(`
return (
async function(components,mouseEvent){
${fnStr}
// 遍历,按需侦听
for(let key in EventLife)
{
if(key != "BEFORE_MOUNT" && key != "MOUNTED"){
const k = EventLife[key as keyof typeof EventLife]
const fnStr = (events[<EventLife>k] || '').trim()
if(fnStr){
lifeEvents[k as keyof typeof lifeEvents] = (e:any) => {
const fnStr = (events[<EventLife>k] || '').trim()
generateFunc(fnStr, e, components[chartConfig.id])
}
)`)().bind(undefined,components)
} catch (error) {
console.error(error)
}
}
}
return lifeEvents
}
/**
* 生成高级函数
*
* @param fnStr 用户方法体代码
* @param e 执行生命周期的动态组件实例
*/
function generateFunc(fnStr: string, e: any) {
function generateFunc(fnStr: string, e: any, component:any) {
if(fnStr == "") return
try {
// npmPkgs 便于拷贝 echarts 示例时设置option 的formatter等相关内容
Function(`
@@ -72,7 +273,7 @@ function generateFunc(fnStr: string, e: any) {
const {${Object.keys(npmPkgs).join()}} = node_modules;
${fnStr}
}
)`)().bind(e?.component)(e, components, npmPkgs)
)`)().bind(component)(e, components, npmPkgs)
} catch (error) {
console.error(error)
}

View File

@@ -1,218 +1,218 @@
import throttle from 'lodash/throttle'
// 拆出来是为了更好的分离单独复用
// * 屏幕缩放适配(两边留白)
export const usePreviewFitScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 画布尺寸px
const baseWidth = width
const baseHeight = height
// * 默认缩放值
const scale = {
width: 1,
height: 1,
}
// * 需保持的比例
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
const calcRate = () => {
// 当前屏幕宽高比
const currentRate = parseFloat(
(window.innerWidth / window.innerHeight).toFixed(5)
)
if (scaleDom) {
if (currentRate > baseProportion) {
// 表示更宽
scale.width = parseFloat(((window.innerHeight * baseProportion) / baseWidth).toFixed(5))
scale.height = parseFloat((window.innerHeight / baseHeight).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
} else {
// 表示更高
scale.height = parseFloat(((window.innerWidth / baseProportion) / baseHeight).toFixed(5))
scale.width = parseFloat((window.innerWidth / baseWidth).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
}
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 卸载监听
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}
// * X轴撑满Y轴滚动条
export const usePreviewScrollYScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 画布尺寸px
const baseWidth = width
const baseHeight = height
// * 默认缩放值
const scale = {
width: 1,
height: 1,
}
// * 需保持的比例
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
const calcRate = () => {
if (scaleDom) {
scale.height = parseFloat(((window.innerWidth / baseProportion) / baseHeight).toFixed(5))
scale.width = parseFloat((window.innerWidth / baseWidth).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 卸载监听
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}
// * Y轴撑满X轴滚动条
export const usePreviewScrollXScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 画布尺寸px
const baseWidth = width
const baseHeight = height
// * 默认缩放值
const scale = {
height: 1,
width: 1,
}
// * 需保持的比例
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
const calcRate = () => {
if (scaleDom) {
scale.width = parseFloat(((window.innerHeight * baseProportion) / baseWidth).toFixed(5))
scale.height = parseFloat((window.innerHeight / baseHeight).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 卸载监听
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}
// * 变形内容,宽高铺满
export const usePreviewFullScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 默认缩放值
const scale = {
width: 1,
height: 1,
}
const calcRate = () => {
if (scaleDom) {
scale.width = parseFloat((window.innerWidth / width).toFixed(5))
scale.height = parseFloat((window.innerHeight / height).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 卸载监听
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
import throttle from 'lodash/throttle'
// 拆出来是为了更好的分离单独复用
// * 屏幕缩放适配(两边留白)
export const usePreviewFitScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 画布尺寸px
const baseWidth = width
const baseHeight = height
// * 默认缩放值
const scale = {
width: 1,
height: 1,
}
// * 需保持的比例
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
const calcRate = () => {
// 当前屏幕宽高比
const currentRate = parseFloat(
(window.innerWidth / window.innerHeight).toFixed(5)
)
if (scaleDom) {
if (currentRate > baseProportion) {
// 表示更宽
scale.width = parseFloat(((window.innerHeight * baseProportion) / baseWidth).toFixed(5))
scale.height = parseFloat((window.innerHeight / baseHeight).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
} else {
// 表示更高
scale.height = parseFloat(((window.innerWidth / baseProportion) / baseHeight).toFixed(5))
scale.width = parseFloat((window.innerWidth / baseWidth).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
}
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 改变窗口大小重新绘制
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}
// * X轴撑满Y轴滚动条
export const usePreviewScrollYScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 画布尺寸px
const baseWidth = width
const baseHeight = height
// * 默认缩放值
const scale = {
width: 1,
height: 1,
}
// * 需保持的比例
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
const calcRate = () => {
if (scaleDom) {
scale.height = parseFloat(((window.innerWidth / baseProportion) / baseHeight).toFixed(5))
scale.width = parseFloat((window.innerWidth / baseWidth).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 改变窗口大小重新绘制
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}
// * Y轴撑满X轴滚动条
export const usePreviewScrollXScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 画布尺寸px
const baseWidth = width
const baseHeight = height
// * 默认缩放值
const scale = {
height: 1,
width: 1,
}
// * 需保持的比例
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
const calcRate = () => {
if (scaleDom) {
scale.width = parseFloat(((window.innerHeight * baseProportion) / baseWidth).toFixed(5))
scale.height = parseFloat((window.innerHeight / baseHeight).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 改变窗口大小重新绘制
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}
// * 变形内容,宽高铺满
export const usePreviewFullScale = (
width: number,
height: number,
scaleDom: HTMLElement | null,
callback?: (scale: {
width: number;
height: number;
}) => void
) => {
// * 默认缩放值
const scale = {
width: 1,
height: 1,
}
const calcRate = () => {
if (scaleDom) {
scale.width = parseFloat((window.innerWidth / width).toFixed(5))
scale.height = parseFloat((window.innerHeight / height).toFixed(5))
scaleDom.style.transform = `scale(${scale.width}, ${scale.height})`
if (callback) callback(scale)
}
}
const resize = throttle(() => {
calcRate()
}, 200)
// * 改变窗口大小重新绘制
const windowResize = () => {
window.addEventListener('resize', resize)
}
// * 改变窗口大小重新绘制
const unWindowResize = () => {
window.removeEventListener('resize', resize)
}
return {
calcRate,
windowResize,
unWindowResize,
}
}

View File

@@ -1,164 +0,0 @@
import { watch } from 'vue'
import { VChart, type ITheme } from '@visactor/vchart'
import light from '@visactor/vchart-theme/public/light.json'
import dark from '@visactor/vchart-theme/public/dark.json'
import vScreenVolcanoBlue from '@visactor/vchart-theme/public/vScreenVolcanoBlue.json'
import vScreenClean from '@visactor/vchart-theme/public/vScreenClean.json'
import vScreenOutskirts from '@visactor/vchart-theme/public/vScreenOutskirts.json'
import vScreenBlueOrange from '@visactor/vchart-theme/public/vScreenBlueOrange.json'
import vScreenFinanceYellow from '@visactor/vchart-theme/public/vScreenFinanceYellow.json'
import vScreenWenLvCyan from '@visactor/vchart-theme/public/vScreenWenLvCyan.json'
import vScreenElectricGreen from '@visactor/vchart-theme/public/vScreenElectricGreen.json'
import vScreenECommercePurple from '@visactor/vchart-theme/public/vScreenECommercePurple.json'
import vScreenRedBlue from '@visactor/vchart-theme/public/vScreenRedBlue.json'
import vScreenPartyRed from '@visactor/vchart-theme/public/vScreenPartyRed.json'
// 行业色板
import veODesignLightFinance from '@visactor/vchart-theme/public/veODesignLightFinance.json'
import veODesignDarkFinance from '@visactor/vchart-theme/public/veODesignDarkFinance.json'
import veODesignLightGovernment from '@visactor/vchart-theme/public/veODesignLightGovernment.json'
import veODesignDarkGovernment from '@visactor/vchart-theme/public/veODesignDarkGovernment.json'
import veODesignLightConsumer from '@visactor/vchart-theme/public/veODesignLightConsumer.json'
import veODesignDarkConsumer from '@visactor/vchart-theme/public/veODesignDarkConsumer.json'
import veODesignLightAutomobile from '@visactor/vchart-theme/public/veODesignLightAutomobile.json'
import veODesignDarkAutomobile from '@visactor/vchart-theme/public/veODesignDarkAutomobile.json'
import veODesignLightCulturalTourism from '@visactor/vchart-theme/public/veODesignLightCulturalTourism.json'
import veODesignDarkCulturalTourism from '@visactor/vchart-theme/public/veODesignDarkCulturalTourism.json'
import veODesignLightMedical from '@visactor/vchart-theme/public/veODesignLightMedical.json'
import veODesignDarkMedical from '@visactor/vchart-theme/public/veODesignDarkMedical.json'
import veODesignLightNewEnergy from '@visactor/vchart-theme/public/veODesignLightNewEnergy.json'
import veODesignDarkNewEnergy from '@visactor/vchart-theme/public/veODesignDarkNewEnergy.json'
const themeMap = {
// 明亮
light: light,
// 暗黑
dark: dark,
// 火山蓝
vScreenVolcanoBlue: vScreenVolcanoBlue,
// 党建红
vScreenPartyRed: vScreenPartyRed,
// 清新蜡笔
vScreenClean: vScreenClean,
// 郊外
vScreenOutskirts: vScreenOutskirts,
// 汽车蓝橙
vScreenBlueOrange: vScreenBlueOrange,
// 金融黄
vScreenFinanceYellow: vScreenFinanceYellow,
// 文旅青
vScreenWenLvCyan: vScreenWenLvCyan,
// 电力绿
vScreenElectricGreen: vScreenElectricGreen,
// 电商紫
vScreenECommercePurple: vScreenECommercePurple,
// 红蓝
vScreenRedBlue: vScreenRedBlue,
// 金融行业色板
veODesignLightFinance: veODesignLightFinance,
veODesignDarkFinance: veODesignDarkFinance,
// 政府行业色板
veODesignLightGovernment: veODesignLightGovernment,
veODesignDarkGovernment: veODesignDarkGovernment,
// 消费行业色板
veODesignLightConsumer: veODesignLightConsumer,
veODesignDarkConsumer: veODesignDarkConsumer,
// 汽车行业色板
veODesignLightAutomobile: veODesignLightAutomobile,
veODesignDarkAutomobile: veODesignDarkAutomobile,
// 文旅行业色板
veODesignLightCulturalTourism: veODesignLightCulturalTourism,
veODesignDarkCulturalTourism: veODesignDarkCulturalTourism,
// 医疗行业色板
veODesignLightMedical: veODesignLightMedical,
veODesignDarkMedical: veODesignDarkMedical,
// 新能源行业色板
veODesignLightNewEnergy: veODesignLightNewEnergy,
veODesignDarkNewEnergy: veODesignDarkNewEnergy
}
export const useVCharts = () => {
const getThemeMap = () => {
return themeMap
}
// 注册主题(支持自定义主题)
const registerTheme = (themeName: keyof typeof themeMap, theme: any) => {
VChart.ThemeManager.registerTheme(themeName, (themeMap[themeName] as any) || theme)
}
// 设置当前主题
const setCurrentTheme = (themeName = 'vScreenVolcanoBlue') => {
VChart.ThemeManager.setCurrentTheme(themeName)
}
// 判断主题是否存在
const themeExist = (name: string): boolean => {
return VChart.ThemeManager.themeExist(name)
}
// 获取主题
const getTheme = (name: string): ITheme => {
return VChart.ThemeManager.getTheme(name)
}
// 获取当前主题
const getCurrentTheme = (): ITheme => {
return VChart.ThemeManager.getCurrentTheme()
}
// 设置主题
const setTheme = (name: keyof typeof themeMap): boolean => {
if (themeExist(name)) {
setCurrentTheme(name)
return true
} else {
// 先注册
const theme = themeMap[name]
if (theme) {
registerTheme(name, theme)
setCurrentTheme(name)
return true
} else {
// 注册默认主题
registerTheme('vScreenVolcanoBlue', vScreenVolcanoBlue)
}
}
return false
}
return {
getThemeMap,
registerTheme,
setCurrentTheme,
themeExist,
getTheme,
setTheme,
getCurrentTheme
}
}
// 主题初始化
export const useInitVChartsTheme = (chartEditStore: any) => {
const vCharts = useVCharts()
const initVChartsThemeIns = watch(
() => chartEditStore.getEditCanvasConfig.vChartThemeName,
(newTheme: string) => {
vCharts.setTheme(newTheme as any)
},
{
immediate: true
}
)
return {
initVChartsThemeIns
}
}

View File

@@ -11,6 +11,8 @@ const global = {
help: 'Help',
contact: 'About Software',
logout: 'Logout',
logout_success: 'Logout success',
logout_failure: 'Logout Failed',
// system setting
sys_set: 'System Setting',
lang_set: 'Language Setting',
@@ -26,8 +28,14 @@ const global = {
r_more: 'More',
}
const http = {
error_message: 'The interface is abnormal, please check the interface!',
token_overdue_message: 'Login expired, please log in again!'
}
export default {
global,
http,
login,
project
}

View File

@@ -2,6 +2,6 @@ export default {
desc: "Login",
form_auto: "Sign in automatically",
form_button: "Login",
login_success: "Login success",
login_message: "Please complete the letter",
login_success: "Login success!",
login_message: "Please complete the letter!",
}

View File

@@ -1,6 +1,8 @@
export default {
create_btn: 'Create',
create_tip: 'Please select a content for development',
create_btn: 'Creat',
create_success: 'Creat Success!',
create_failure: 'Failed to create, please try again later',
create_tip: 'Please select a content for development!',
project: 'Project',
my: 'My',
new_project: 'New Project',

View File

@@ -23,8 +23,6 @@ export const langList = [
]
const i18n = createI18n({
legacy: false,
globalInjection:true,
locale: langStorage?.lang || lang,
fallbackLocale: langStorage?.lang || lang,
messages: {

View File

@@ -11,6 +11,8 @@ const global = {
help: '帮助中心',
contact: '关于软件',
logout: '退出登录',
logout_success: '退出成功!',
logout_failure: '退出失败!',
// 系统设置
sys_set: '系统设置',
lang_set: '语言设置',
@@ -18,16 +20,27 @@ const global = {
r_edit: '编辑',
r_preview: '预览',
r_copy: '克隆',
r_copy_success: '克隆成功!',
r_rename: '重命名',
r_rename_success: '重命名成功!',
r_publish: '发布',
r_publish_success: '成功发布!',
r_unpublish: '取消发布',
r_unpublish_success: '取消成功!',
r_download: '下载',
r_delete: '删除',
r_delete_success: '删除成功!',
r_more: '更多',
}
const http = {
error_message: '获取数据失败,请稍后重试!',
token_overdue_message: '登录过期,请重新登录!'
}
export default {
global,
http,
login,
project
}

View File

@@ -2,6 +2,6 @@ export default {
desc: "登录",
form_auto: "自动登录",
form_button: "登录",
login_success: "登录成功",
login_message: "请填写完整信息",
login_success: "登录成功!",
}

Some files were not shown because too many files have changed in this diff Show More