Compare commits

..

108 Commits

Author SHA1 Message Date
奔跑的面条
672f7a0a7c Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-11-05 21:46:26 +08:00
奔跑的面条
32762aa8a7 fix: 修改预览页面的报错问题 2022-11-05 21:31:42 +08:00
奔跑的面条
c35dd6fad9 fix: 解决锁定时,修改模块隐藏展示,标尺不会自适应的问题 2022-11-05 21:16:34 +08:00
奔跑的面条
de78fb2149 docs: 新增文档截图 2022-11-05 18:19:33 +08:00
奔跑的面条
8d05953b8b chore: 修改路径地址 2022-11-05 15:45:29 +08:00
奔跑的面条
afed1cb6b3 fix: 处理切换语言,UI框架不会变的问题 2022-11-05 15:23:01 +08:00
奔跑的面条
f6a4e87e05 style: 优化提示文案 2022-11-03 09:27:09 +08:00
奔跑的面条
e36265a2f4 style: 修改 iframe默认地址 2022-11-02 20:03:32 +08:00
奔跑的面条
4a59e69bbe docs: 修改首页地址 2022-11-02 19:46:19 +08:00
奔跑的面条
a442e13a7b feat: 升级版本到1.1.4 2022-11-02 19:05:16 +08:00
奔跑的面条
4d093a4a80 Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-11-02 19:02:38 +08:00
奔跑的面条
3dd94fb408 feat: 新增点击事件案例 2022-11-02 16:35:08 +08:00
奔跑的面条
7e59d8d36c docs: 修改链接地址 2022-11-02 09:57:28 +08:00
奔跑的面条
bb46050bb3 style: 优化代码结构 2022-10-31 09:52:02 +08:00
奔跑的面条
b50620ec2c style: 优化代码结构 2022-10-31 09:30:35 +08:00
奔跑的面条
7bef69af92 style: 修改翻牌器全局引用的目录 2022-10-31 09:29:22 +08:00
奔跑的面条
8241077495 update README.md.
Signed-off-by: 奔跑的面条 <1262327911@qq.com>
2022-10-29 12:09:36 +00:00
奔跑的面条
82b29a2f8b docs: 优化说明文档 2022-10-29 19:57:02 +08:00
奔跑的面条
b789d80f98 fix: 修改主页说明 2022-10-29 19:51:33 +08:00
奔跑的面条
a8c80e2366 docs: 修改主页说明 2022-10-29 19:50:30 +08:00
奔跑的面条
ae8fcd640b perf: 优化 iframe 组件 2022-10-29 19:10:47 +08:00
奔跑的面条
455f3ff09a perf: 优化iframe 2022-10-29 17:55:14 +08:00
奔跑的面条
e4afaded36 !92 新增Iframe组件
Merge pull request !92 from 自由/dev-wu-20221027
2022-10-29 09:27:15 +00:00
奔跑的面条
a10409f829 perf: 优化 mock 列表的快捷写法 2022-10-29 17:15:14 +08:00
奔跑的面条
112138fdc5 perf: 优化高德地图 2022-10-29 17:14:47 +08:00
wuyuting1
73e5ca59df feat: 增加iframe组件 2022-10-28 11:38:39 +08:00
奔跑的面条
38f92febb7 feat: 优化胶囊图,新增配置 2022-10-28 11:13:52 +08:00
奔跑的面条
cd6f1e0438 perf: 优化版权声明的UI 2022-10-28 09:49:35 +08:00
奔跑的面条
1d73f71aaf fix: 优化在线编辑JSON的代码和交互 2022-10-27 21:10:58 +08:00
奔跑的面条
e4e77cd141 !91 增加胶囊柱图
Merge pull request !91 from daidai/dev
2022-10-27 12:30:58 +00:00
奔跑的面条
e0e7270623 !87 feat:新增高德地图
Merge pull request !87 from jiangcheng/dev
2022-10-27 12:27:22 +00:00
奔跑的面条
885c376e2a !85 feat: 新增JSON编辑页面 以及 保存跨窗口同步视图更新
Merge pull request !85 from 潘潘/dev
2022-10-27 12:27:01 +00:00
奔跑的面条
2845e9829d fix: 修改工作台不去除监听的问题 2022-10-27 19:38:58 +08:00
刘嘉威
36b82de301 style: remove console.log 2022-10-27 11:41:53 +08:00
刘嘉威
a2ac99e7cb feat: 胶囊柱图增加mock数据源 2022-10-27 11:40:53 +08:00
刘嘉威
d037e1ff36 feat: 增加胶囊柱图 2022-10-27 11:01:41 +08:00
潘潘
9d4424356d feat: 定时、失焦、保存同步 2022-10-26 12:10:19 +08:00
奔跑的面条
f087a0766b !89 解决编译混淆的警告和i18n的控制台警告
Merge pull request !89 from unidmr/dev
2022-10-25 04:50:31 +00:00
xushihao
0bb7400755 chore:解决控制台关于vue-i18n的告警 2022-10-25 09:55:18 +08:00
xushihao
6460c475b6 build:用terser混淆时会出现警告,需要配置minify为terser,或者用esbuild 2022-10-25 09:52:12 +08:00
蒋承
f8b645f867 feat: 添加点标记 2022-10-24 10:20:06 +08:00
panjianhua
834b330bc8 style: 修改UI 2022-10-22 21:16:37 +08:00
潘潘
a4b4ce57bd feat: 预览页\JSON编辑页\图表页保存同步更新 2022-10-20 15:38:15 +08:00
蒋承
ff7a0830d0 feat: 高德地图新增属性 2022-10-19 10:21:48 +08:00
蒋承
ff43d0c2d8 feat: 新增高德地图 2022-10-19 09:29:50 +08:00
奔跑的面条
bad4b5d7b3 fix: 去除单独颜色设置 2022-10-18 21:23:18 +08:00
奔跑的面条
adaf30dd3b perf: 优化弹幕图片 2022-10-18 21:00:41 +08:00
奔跑的面条
c0fc84061e !81 feat:新增弹幕文字组件
Merge pull request !81 from jiangcheng/dev
2022-10-18 12:58:35 +00:00
蒋承
b84ad0f598 feat: 弹幕文字 2022-10-18 11:24:57 +08:00
奔跑的面条
aa0569ef4b fix: 修改列表图偏移的问题 2022-10-17 09:42:56 +08:00
奔跑的面条
b88d90cb80 perf: 优化文案描述 2022-10-17 09:26:19 +08:00
奔跑的面条
c055129309 perf: 优化标题 2022-10-16 20:49:48 +08:00
奔跑的面条
30351cecb5 feat: 新增案例说明 2022-10-16 20:46:01 +08:00
奔跑的面条
4f6757fff2 perf: 优化事件编辑页面 2022-10-16 16:27:15 +08:00
奔跑的面条
284fdb8e05 perf: 修改事件文件位置,修改图标 2022-10-16 10:41:12 +08:00
奔跑的面条
738bcae563 fix: 去除 setPosition 2022-10-16 10:29:01 +08:00
奔跑的面条
987a68a6d1 !76 新增组件生命周期事件
Merge pull request !76 from 潘潘/dev
2022-10-16 02:28:09 +00:00
奔跑的面条
bdbdd37789 build: 升级版本到1.1.3 2022-10-15 17:17:34 +08:00
奔跑的面条
c8fb5b9152 Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-10-15 17:12:13 +08:00
奔跑的面条
59cb5d609d perf: 去除grid多余配置,添加散点图、热力图的grid 2022-10-15 16:51:16 +08:00
奔跑的面条
4add9ce4c6 !80 feat: 折线图和柱状图添加网格配置,xy轴标签偏移量
Merge pull request !80 from jiangcheng/dev
2022-10-15 02:09:47 +00:00
蒋承
c75d9f7a59 feat: x,y轴标签偏移量 2022-10-14 17:09:26 +08:00
蒋承
2d94085c54 feat: 网格容器配置 2022-10-14 16:50:06 +08:00
蒋承
2a6e5915f5 feat: 柱状图折线图添加网格配置 2022-10-14 16:46:41 +08:00
潘潘
656532e43d Merge branch 'dev' of gitee.com:dromara/go-view into dev
Signed-off-by: 潘潘 <97274247@qq.com>
2022-10-14 07:01:47 +00:00
奔跑的面条
d835a904c9 fix: 解决导入时顺序不对的问题 2022-10-14 14:33:14 +08:00
奔跑的面条
a5bf0e9f6a perf: 优化导入失败提示 2022-10-14 09:37:31 +08:00
奔跑的面条
7e3efa46ee perf: 优化亮色模式图层提示 2022-10-14 09:37:12 +08:00
奔跑的面条
ce68910e48 fix: 解决 分组复制 id 会重复的问题 2022-10-13 21:10:32 +08:00
奔跑的面条
6dce639a0e perf: 优化按下文案tishi 2022-10-13 20:48:12 +08:00
奔跑的面条
c738103fec perf: 优化键盘按下的提示 2022-10-13 20:45:51 +08:00
奔跑的面条
801f298448 perf: 优化报错信息捕获,去除setPosition自带函数 2022-10-13 20:21:33 +08:00
奔跑的面条
e08a7723b6 perf: 优化格式 2022-10-13 19:52:41 +08:00
奔跑的面条
166b5b98e8 !78 feat: 过滤方法增加api返回顶级对象作为参数,增强过滤器兼容性
Merge pull request !78 from dodu/dev-api-filter
2022-10-13 10:05:39 +00:00
奔跑的面条
07e56631da perf: 优化异常处理 2022-10-12 21:57:30 +08:00
奔跑的面条
1435789a6c !77 feat: 折线图和柱状图配置label选项
Merge pull request !77 from jiangcheng/dev
2022-10-12 13:35:36 +00:00
奔跑的面条
7c5fa7610d fix: 新增三维组件限制 2022-10-12 21:30:08 +08:00
奔跑的面条
084e680e12 perf: 优化三维地球内存泄漏 2022-10-12 21:15:08 +08:00
奔跑的面条
380a6b95b7 feat: 新增三维地球 2022-10-12 21:08:24 +08:00
tnt group
9467feff23 fix: 移除开发标记 2022-10-12 16:50:25 +08:00
tnt group
a6f4267915 feat: 过滤方法增加api返回顶级对象作为参数,增强过滤器兼容性 2022-10-12 16:41:49 +08:00
jiangcheng
cfe9efd4dd Merge branch 'dev' of https://gitee.com/jiang_cheng_1/go-view into dev 2022-10-12 11:31:05 +08:00
jiangcheng
971b03620a fix: 格式问题 2022-10-12 11:30:54 +08:00
潘潘
92339dae28 feat: 组件生命周期事件 2022-10-11 18:31:29 +08:00
奔跑的面条
c84f6d9c33 perf: 优化补丁的判单方式 2022-10-11 16:02:43 +08:00
奔跑的面条
e187c012cc perf: 修改ctrl默认值 2022-10-11 15:15:03 +08:00
奔跑的面条
af1c280f28 perf: 优化 ctrl 监听 2022-10-10 18:47:07 +08:00
奔跑的面条
aef155e039 fix: 修复组件导入数据错乱的问题 2022-10-09 16:37:30 +08:00
蒋承
016e48cc69 feat: 折线图和柱状图配置label 2022-10-09 16:04:29 +08:00
蒋承
6dcd39983f feat: 更新 2022-10-09 15:50:46 +08:00
蒋承
b055b6c511 Merge branch 'dev' of https://gitee.com/jiang_cheng_1/go-view into dev 2022-10-09 15:36:00 +08:00
蒋承
1d18c86dc7 feat: 动态数据维度不同,导致整合维度bug 2022-10-09 13:58:29 +08:00
奔跑的面条
443573455b Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-10-09 09:28:38 +08:00
奔跑的面条
d811ab7dfa build: 撤回错误版本号 2022-10-08 21:12:58 +08:00
奔跑的面条
5f79ab6e6d build: 升级版本到1.1.2 2022-10-08 21:12:27 +08:00
奔跑的面条
502edbca5d Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-10-08 21:05:48 +08:00
奔跑的面条
1a04267cb7 build: 升级版本到1.1.1 2022-10-08 20:59:55 +08:00
奔跑的面条
9c2c1bdfa1 Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-10-08 20:59:25 +08:00
奔跑的面条
72e93f3768 Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-09-27 20:16:07 +08:00
奔跑的面条
e6f2066d42 Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-09-22 14:15:00 +08:00
奔跑的面条
2812c39524 Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-09-21 19:54:29 +08:00
奔跑的面条
491485856e Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-09-19 20:46:37 +08:00
奔跑的面条
abde7e176d Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-09-19 20:13:50 +08:00
奔跑的面条
bab8faadee add default pipeline template yaml 2022-09-19 11:58:32 +00:00
奔跑的面条
427d72fb8b Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-09-18 16:48:28 +08:00
奔跑的面条
dc5b20a329 build: 升级版本到1.1.0 2022-09-18 16:33:26 +08:00
奔跑的面条
4cb934eef3 Merge branch 'dev' of https://gitee.com/MTrun/go-view 2022-09-18 16:32:34 +08:00
奔跑的面条
35e8cea0b5 docs: 更新已完成图表说明 2022-09-17 23:00:51 +08:00
231 changed files with 5424 additions and 2670 deletions

12
.env
View File

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

View File

@@ -0,0 +1,51 @@
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

@@ -0,0 +1,49 @@
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

36
.workflow/pr-pipeline.yml Normal file
View File

@@ -0,0 +1,36 @@
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

209
README.md
View File

@@ -1,122 +1,161 @@
## 总览
![logo](readme/logo-t-y.png)
<p align="center">
<img src="readme/logo-t-y.png" alt="go-view" />
</p>
**`master-fetch` 分支是带有后端接口请求的分支**
<h4 align="center">开源、精美、便捷的「数据可视化」低代码开发平台</h4>
**后端项目地址:[https://gitee.com/MTrun/go-view-serve](https://gitee.com/MTrun/go-view-serve)**
#### 😶 **纯前端** 分支: **`master`**
**接口说明地址:[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)**
#### 👻 携带 **后端** 请求分支: **`master-fetch`**
## 使用
#### 📚 GoView **文档** 地址:[https://www.mtruning.club/](https://www.mtruning.club/)
所有的接口地址位置:`src\api\path\*`
项目纯前端-Demo 地址:[https://vue.mtruning.club/](https://vue.mtruning.club/)
接口地址修改:`.env`
项目带后端-Demo 地址:[https://demo.mtruning.club/](https://demo.mtruning.club/)
### 🤯 后端项目
文档-源码地址:[https://gitee.com/MTrun/go-view-doc](https://gitee.com/MTrun/go-view-doc)
后端项目gitee地址[https://gitee.com/MTrun/go-view-serve](https://gitee.com/MTrun/go-view-serve)
#### 🤯 后端项目看这里!
后端项目 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)
```shell
# port
VITE_DEV_PORT = '8080'
技术点:
# development path
VITE_DEV_PATH = 'http://127.0.0.1:8080'
- 框架:基于 `Vue3` 框架编写,使用 `hooks` 写法抽离部分逻辑,使代码结构更加清晰;
# production path
VITE_PRO_PATH = 'http://127.0.0.1:8080'
```
- 类型:使用 `TypeScript` 进行类型约束,减少未知错误发生概率,可以大胆修改逻辑内容;
公共前缀修改:`src\settings\httpSetting.ts`
- 性能:多处性能优化,使用页面懒加载、组件动态注册、数据滚动加载等方式,提升页面渲染速度;
- 存储:拥有本地记忆,部分配置项采用 `storage` 存储本地,提升使用体验;
- 封装:项目进行了详细的工具类封装如:路由、存储、加/解密、文件处理、主题、NaiveUI 全局方法、组件等
说明文档:
![说明文档](readme/go-view-doc.png)
工作台:
![工作台](readme/go-view-canvas.png)
请求配置:
![请求配置](readme/go-view-fetch.png)
数据过滤:
![数据过滤](readme/go-view-filter.png)
高级事件编辑:
![高级事件编辑](readme/go-view-event.png)
快捷主页:
![快捷主页](readme/go-view-indexpage.png)
主题色:
![主题色](readme/go-view-color.png)
亮白主题:
![亮白主题](readme/go-view-theme.png)
主要技术栈为:
| 名称 | 版本 | 名称 | 版本 |
| ------------------- | ----- | ----------- | ------ |
| Vue | 3.2.x | TypeScript4 | 4.6.x |
| Vite | 2.9.x | NaiveUI | 2.27.x |
| ECharts | 5.3.x | Pinia | 2.0.x |
| 详见 `package.json` | 😁 | 🥰 | 🤗 |
开发环境:
| 名称 | 版本 | 名称 | 版本 |
| ---- | ------- | ------- | ----- |
| node | 16.14.x | npm | 8.5.x |
| pnpm | 7.1.x | windows | 11 |
已完成图表:
| 分类 | 名称 | 名称 | 名称 | 名称 |
| ------ | ---------------- | ---------- | -------------- | ------------------------ |
| 图表 | 柱状图 | 横向柱状图 | 折线图 | 单/多 折线面积图(渐变色) |
| \* | 饼图 | 环形图 | 水球图 | 雷达图 |
| \* | NaiveUI 多种进度 | 散点图 | 对数回归散点图 | 热力图 |
| \* | 漏斗图 | 中国地图 | 高德地图 | 🦊 |
| 信息 | 文字 | 渐变文字 | 词云 | 嵌套网页 |
| \* | 图片 | 视频 | 😺 | 🐯 |
| 列表 | 滚动排名列表 | 滚动表格 | 🐮 | 🐐 |
| 小组件 | 边框-01~13 | 装饰-01~05 | 数字翻牌 | 通用时间 |
| \* | 数字计数 | 倒计时 | 时钟 | 🦁 |
## 浏览器支持
开发和测试平台均在 `Google` 和最新版 `EDGE` 上完成,暂未测试 `IE11` 等其它浏览器,如有需求请自行测试与兼容。
## 安装
本项目采用` pnpm` 进行包管理
```shell
// 请求前缀
export const axiosPre = '/api/goview'
#建议使用 nrm 切换到淘宝源 https://registry.npmmirror.com/
#pnpm
pnpm install
#yarn
yarn install
#npm
npm install
```
接口封装:`src\api\http.ts`
## 启动
```ts
import axiosInstance from './axios'
import { RequestHttpEnum, ContentTypeEnum } from '@/enums/httpEnum'
```shell
#pnpm
pnpm dev
export const get = (url: string, params?: object) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.GET,
params: params,
})
}
# npm
npm run dev
export const post = (url: string, data?: object, headersType?: string) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.POST,
data: data,
headers: {
'Content-Type': headersType || ContentTypeEnum.JSON
}
})
}
#yarn
yarn dev
export const put = (url: string, data?: object, headersType?: string) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.PUT,
data: data,
headers: {
'Content-Type': headersType || ContentTypeEnum.JSON
}
})
}
#Makefile
make dev
```
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
```shell
#pnpm
pnpm run build
case RequestHttpEnum.POST:
return post
# npm
npm run build
case RequestHttpEnum.PUT:
return put
#yarn
yarn run build
case RequestHttpEnum.DELETE:
return del
default:
return get
}
}
#Makefile
make dist
```
## 代码提交
* feat: 新功能
* fix: 修复 Bug
* docs: 文档修改
* perf: 性能优化
* revert: 版本回退
* ci: CICD集成相关
* test: 添加测试代码
* refactor: 代码重构
* build: 影响项目构建或依赖修改
* style: 不影响程序逻辑的代码修改
* chore: 不属于以上类型的其他类型(日常事务)
- feat: 新功能
- fix: 修复 Bug
- docs: 文档修改
- perf: 性能优化
- revert: 版本回退
- ci: CICD 集成相关
- test: 添加测试代码
- refactor: 代码重构
- build: 影响项目构建或依赖修改
- style: 不影响程序逻辑的代码修改
- chore: 不属于以上类型的其他类型(日常事务)
## 交流

View File

@@ -0,0 +1,9 @@
/**
* 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

@@ -1,6 +1,6 @@
{
"name": "go-view",
"version": "2.0.91",
"version": "1.1.4",
"scripts": {
"dev": "vite --host",
"build": "vue-tsc --noEmit && vite build",
@@ -11,6 +11,8 @@
"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",
"@types/color": "^3.0.3",
"@types/crypto-js": "^4.1.1",
"@types/keymaster": "^1.6.30",
@@ -22,6 +24,7 @@
"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",
"keymaster": "^1.6.2",
@@ -29,9 +32,10 @@
"naive-ui": "2.33.4",
"pinia": "^2.0.13",
"screenfull": "^6.0.1",
"three": "^0.145.0",
"vue": "^3.2.31",
"vue-demi": "^0.13.1",
"vue-i18n": "9.1.10",
"vue-i18n": "9.1.9",
"vue-router": "4.0.12",
"vue3-lazyload": "^0.2.5-beta",
"vue3-sketch-ruler": "^1.3.3",
@@ -41,6 +45,7 @@
"@commitlint/cli": "^17.0.2",
"@commitlint/config-conventional": "^17.0.2",
"@types/node": "^16.11.26",
"@types/three": "^0.144.0",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"@vicons/carbon": "^0.12.0",

114
pnpm-lock.yaml generated
View File

@@ -1,6 +1,8 @@
lockfileVersion: 5.4
specifiers:
'@amap/amap-jsapi-loader': ^1.0.1
'@amap/amap-jsapi-types': ^0.0.8
'@commitlint/cli': ^17.0.2
'@commitlint/config-conventional': ^17.0.2
'@types/color': ^3.0.3
@@ -8,6 +10,7 @@ specifiers:
'@types/keymaster': ^1.6.30
'@types/lodash': ^4.14.184
'@types/node': ^16.11.26
'@types/three': ^0.144.0
'@typescript-eslint/eslint-plugin': ^5.18.0
'@typescript-eslint/parser': ^5.18.0
'@vicons/carbon': ^0.12.0
@@ -31,6 +34,7 @@ specifiers:
eslint-plugin-import: ^2.26.0
eslint-plugin-prettier: ^4.0.0
eslint-plugin-vue: ^8.5.0
gsap: ^3.11.3
highlight.js: ^11.5.0
html2canvas: ^1.4.1
husky: ^8.0.1
@@ -45,6 +49,7 @@ specifiers:
sass: ^1.49.11
sass-loader: ^12.6.0
screenfull: ^6.0.1
three: ^0.145.0
typescript: 4.6.3
vite: 2.9.9
vite-plugin-compression: ^0.5.1
@@ -54,7 +59,7 @@ specifiers:
vue: ^3.2.31
vue-demi: ^0.13.1
vue-echarts: ^6.0.2
vue-i18n: 9.1.10
vue-i18n: 9.1.9
vue-router: 4.0.12
vue-tsc: ^0.28.10
vue3-lazyload: ^0.2.5-beta
@@ -62,6 +67,8 @@ specifiers:
vuedraggable: ^4.1.0
dependencies:
'@amap/amap-jsapi-loader': 1.0.1
'@amap/amap-jsapi-types': 0.0.8
'@types/color': 3.0.3
'@types/crypto-js': 4.1.1
'@types/keymaster': 1.6.30
@@ -73,6 +80,7 @@ dependencies:
echarts-liquidfill: 3.1.0_echarts@5.3.3
echarts-stat: 1.2.0
echarts-wordcloud: 2.0.0_echarts@5.3.3
gsap: 3.11.3
highlight.js: 11.5.1
html2canvas: 1.4.1
keymaster: 1.6.2
@@ -80,9 +88,10 @@ dependencies:
naive-ui: 2.33.4_vue@3.2.37
pinia: 2.0.14_ub5l46u3nefphax5x2tezui4oq
screenfull: 6.0.1
three: 0.145.0
vue: 3.2.37
vue-demi: 0.13.1_vue@3.2.37
vue-i18n: 9.1.10_vue@3.2.37
vue-i18n: 9.1.9_vue@3.2.37
vue-router: 4.0.12_vue@3.2.37
vue3-lazyload: 0.2.5-beta_2yymnzrok6eda47acnj2yjm3ae
vue3-sketch-ruler: 1.3.4_vue@3.2.37
@@ -92,6 +101,7 @@ devDependencies:
'@commitlint/cli': 17.0.2
'@commitlint/config-conventional': 17.0.2
'@types/node': 16.11.40
'@types/three': 0.144.0
'@typescript-eslint/eslint-plugin': 5.28.0_evi7yu7wunhzwb24olrfvzynny
'@typescript-eslint/parser': 5.28.0_sfmgizikprcxt7r54j7cnzjamu
'@vicons/carbon': 0.12.0
@@ -126,6 +136,14 @@ devDependencies:
packages:
/@amap/amap-jsapi-loader/1.0.1:
resolution: {integrity: sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw==}
dev: false
/@amap/amap-jsapi-types/0.0.8:
resolution: {integrity: sha512-q0FyZDIJcXjsMLGc3oS9rjfJsErOvt9rcp6AgzY4k14vo7bBhdq4eKwoSdVp/pYjR/rfaKBns5v10ycZOFwf/A==}
dev: false
/@ampproject/remapping/2.2.0:
resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
engines: {node: '>=6.0.0'}
@@ -653,60 +671,60 @@ packages:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
dev: true
/@intlify/core-base/9.1.10:
resolution: {integrity: sha512-So9CNUavB/IsZ+zBmk2Cv6McQp6vc2wbGi1S0XQmJ8Vz+UFcNn9MFXAe9gY67PreIHrbLsLxDD0cwo1qsxM1Nw==}
/@intlify/core-base/9.1.9:
resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==}
engines: {node: '>= 10'}
dependencies:
'@intlify/devtools-if': 9.1.10
'@intlify/message-compiler': 9.1.10
'@intlify/message-resolver': 9.1.10
'@intlify/runtime': 9.1.10
'@intlify/shared': 9.1.10
'@intlify/vue-devtools': 9.1.10
'@intlify/devtools-if': 9.1.9
'@intlify/message-compiler': 9.1.9
'@intlify/message-resolver': 9.1.9
'@intlify/runtime': 9.1.9
'@intlify/shared': 9.1.9
'@intlify/vue-devtools': 9.1.9
dev: false
/@intlify/devtools-if/9.1.10:
resolution: {integrity: sha512-SHaKoYu6sog3+Q8js1y3oXLywuogbH1sKuc7NSYkN3GElvXSBaMoCzW+we0ZSFqj/6c7vTNLg9nQ6rxhKqYwnQ==}
/@intlify/devtools-if/9.1.9:
resolution: {integrity: sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==}
engines: {node: '>= 10'}
dependencies:
'@intlify/shared': 9.1.10
'@intlify/shared': 9.1.9
dev: false
/@intlify/message-compiler/9.1.10:
resolution: {integrity: sha512-+JiJpXff/XTb0EadYwdxOyRTB0hXNd4n1HaJ/a4yuV960uRmPXaklJsedW0LNdcptd/hYUZtCkI7Lc9J5C1gxg==}
/@intlify/message-compiler/9.1.9:
resolution: {integrity: sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==}
engines: {node: '>= 10'}
dependencies:
'@intlify/message-resolver': 9.1.10
'@intlify/shared': 9.1.10
'@intlify/message-resolver': 9.1.9
'@intlify/shared': 9.1.9
source-map: 0.6.1
dev: false
/@intlify/message-resolver/9.1.10:
resolution: {integrity: sha512-5YixMG/M05m0cn9+gOzd4EZQTFRUu8RGhzxJbR1DWN21x/Z3bJ8QpDYj6hC4FwBj5uKsRfKpJQ3Xqg98KWoA+w==}
/@intlify/message-resolver/9.1.9:
resolution: {integrity: sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==}
engines: {node: '>= 10'}
dev: false
/@intlify/runtime/9.1.10:
resolution: {integrity: sha512-7QsuByNzpe3Gfmhwq6hzgXcMPpxz8Zxb/XFI6s9lQdPLPe5Lgw4U1ovRPZTOs6Y2hwitR3j/HD8BJNGWpJnOFA==}
/@intlify/runtime/9.1.9:
resolution: {integrity: sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==}
engines: {node: '>= 10'}
dependencies:
'@intlify/message-compiler': 9.1.10
'@intlify/message-resolver': 9.1.10
'@intlify/shared': 9.1.10
'@intlify/message-compiler': 9.1.9
'@intlify/message-resolver': 9.1.9
'@intlify/shared': 9.1.9
dev: false
/@intlify/shared/9.1.10:
resolution: {integrity: sha512-Om54xJeo1Vw+K1+wHYyXngE8cAbrxZHpWjYzMR9wCkqbhGtRV5VLhVc214Ze2YatPrWlS2WSMOWXR8JktX/IgA==}
/@intlify/shared/9.1.9:
resolution: {integrity: sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==}
engines: {node: '>= 10'}
dev: false
/@intlify/vue-devtools/9.1.10:
resolution: {integrity: sha512-5l3qYARVbkWAkagLu1XbDUWRJSL8br1Dj60wgMaKB0+HswVsrR6LloYZTg7ozyvM621V6+zsmwzbQxbVQyrytQ==}
/@intlify/vue-devtools/9.1.9:
resolution: {integrity: sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==}
engines: {node: '>= 10'}
dependencies:
'@intlify/message-resolver': 9.1.10
'@intlify/runtime': 9.1.10
'@intlify/shared': 9.1.10
'@intlify/message-resolver': 9.1.9
'@intlify/runtime': 9.1.9
'@intlify/shared': 9.1.9
dev: false
/@jridgewell/gen-mapping/0.1.1:
@@ -905,7 +923,7 @@ packages:
dev: true
/@types/node/17.0.43:
resolution: {integrity: sha512-jnUpgw8fL9kP2iszfIDyBQtw5Mf4/XSqy0Loc1J9pI14ejL83XcCEvSf50Gs/4ET0I9VCCDoOfufQysj0S66xA==, registry: https://registry.npm.taobao.org/}
resolution: {integrity: sha512-jnUpgw8fL9kP2iszfIDyBQtw5Mf4/XSqy0Loc1J9pI14ejL83XcCEvSf50Gs/4ET0I9VCCDoOfufQysj0S66xA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/@types/node/-/node-17.0.43.tgz}
/@types/normalize-package-data/2.4.1:
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
@@ -921,12 +939,22 @@ packages:
'@types/node': 17.0.43
dev: true
/@types/three/0.144.0:
resolution: {integrity: sha512-psvEs6q5rLN50jUYZ3D4pZMfxTbdt3A243blt0my7/NcL6chaCZpHe2csbCtx0SOD9fI/XnF3wnVUAYZGqCSYg==}
dependencies:
'@types/webxr': 0.5.0
dev: true
/@types/through/0.0.30:
resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==}
dependencies:
'@types/node': 17.0.43
dev: true
/@types/webxr/0.5.0:
resolution: {integrity: sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==}
dev: true
/@typescript-eslint/eslint-plugin/5.28.0_evi7yu7wunhzwb24olrfvzynny:
resolution: {integrity: sha512-DXVU6Cg29H2M6EybqSg2A+x8DgO9TCUBRp4QEXQHJceLS7ogVDP0g3Lkg/SZCqcvkAP/RruuQqK0gdlkgmhSUA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -1884,7 +1912,7 @@ packages:
dev: true
/csstype/2.6.20:
resolution: {integrity: sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==, registry: https://registry.npm.taobao.org/}
resolution: {integrity: sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/csstype/-/csstype-2.6.20.tgz}
dev: false
/csstype/3.0.11:
@@ -2091,7 +2119,7 @@ packages:
dev: false
/echarts-wordcloud/2.0.0_echarts@5.3.3:
resolution: {integrity: sha512-K7l6pTklqdW7ZWzT/1CS0KhBSINr/cd7c5N1fVMzZMwLQHEwT7x+nivK7g5hkVh7WNcAv4Dn6/ZS5zMKRozC1g==, registry: https://registry.npm.taobao.org/}
resolution: {integrity: sha512-K7l6pTklqdW7ZWzT/1CS0KhBSINr/cd7c5N1fVMzZMwLQHEwT7x+nivK7g5hkVh7WNcAv4Dn6/ZS5zMKRozC1g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/echarts-wordcloud/-/echarts-wordcloud-2.0.0.tgz}
peerDependencies:
echarts: ^5.0.1
dependencies:
@@ -3032,6 +3060,10 @@ packages:
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
dev: true
/gsap/3.11.3:
resolution: {integrity: sha512-xc/iIJy+LWiMbRa4IdMtdnnKa/7PXEK6NNzV71gdOYUVeTZN7UWnLU0fB7Hi1iwiz4ZZoYkBZPPYGg+2+zzFHA==}
dev: false
/handlebars/4.7.7:
resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==}
engines: {node: '>=0.4.7'}
@@ -4825,6 +4857,10 @@ packages:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true
/three/0.145.0:
resolution: {integrity: sha512-EKoHQEtEJ4CB6b2BGMBgLZrfwLjXcSUfoI/MiIXUuRpeYsfK5aPWbYhdtIVWOH+x6X0TouldHKHBuc/LAiFzAw==}
dev: false
/through/2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
dev: true
@@ -5349,15 +5385,15 @@ packages:
- supports-color
dev: true
/vue-i18n/9.1.10_vue@3.2.37:
resolution: {integrity: sha512-jpr7gV5KPk4n+sSPdpZT8Qx3XzTcNDWffRlHV/cT2NUyEf+sEgTTmLvnBAibjOFJ0zsUyZlVTAWH5DDnYep+1g==}
/vue-i18n/9.1.9_vue@3.2.37:
resolution: {integrity: sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==}
engines: {node: '>= 10'}
peerDependencies:
vue: ^3.0.0
dependencies:
'@intlify/core-base': 9.1.10
'@intlify/shared': 9.1.10
'@intlify/vue-devtools': 9.1.10
'@intlify/core-base': 9.1.9
'@intlify/shared': 9.1.9
'@intlify/vue-devtools': 9.1.9
'@vue/devtools-api': 6.1.4
vue: 3.2.37
dev: false

BIN
readme/go-view-doc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

BIN
readme/go-view-theme.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

View File

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

View File

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

View File

@@ -1,38 +1,19 @@
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'
import { ErrorPageNameMap } from "@/enums/pageEnum"
import { redirectErrorPage } from '@/utils'
const axiosInstance = axios.create({
baseURL: `${import.meta.env.PROD ? import.meta.env.VITE_PRO_PATH : ''}${axiosPre}`,
baseURL: import.meta.env.DEV ? import.meta.env.VITE_DEV_PATH : import.meta.env.VITE_PRO_PATH,
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)
(error: AxiosRequestConfig) => {
Promise.reject(error)
}
)
@@ -40,27 +21,9 @@ axiosInstance.interceptors.request.use(
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))
if (code === ResultEnum.DATA_SUCCESS) return Promise.resolve(res.data)
// 重定向
if (ErrorPageNameMap.get(code)) redirectErrorPage(code)
return Promise.resolve(res.data)
},
(err: AxiosResponse) => {

View File

@@ -13,7 +13,7 @@ export const get = (url: string, params?: object) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.GET,
params: params,
params: params
})
}

View File

@@ -15,8 +15,10 @@ 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'
const mockObject: MockMethod[] = [
{
@@ -81,6 +83,11 @@ const mockObject: MockMethod[] = [
method: RequestHttpEnum.GET,
response: () => test.fetchMap
},
{
url: capsuleUrl,
method: RequestHttpEnum.GET,
response: () => test.fetchCapsule
},
{
url: wordCloudUrl,
method: RequestHttpEnum.GET,
@@ -91,6 +98,11 @@ const mockObject: MockMethod[] = [
method: RequestHttpEnum.GET,
response: () => test.fetchTreemap
},
{
url: threeEarth01Url,
method: RequestHttpEnum.GET,
response: () => test.threeEarth01Data
},
]
export default mockObject

View File

@@ -1,79 +1,9 @@
{
"point": [
"markers|50": [
{
"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]
"name": "某某地市",
"value": "@integer(2, 20)",
"position": ["@float(115, 117, 1, 6)", "@float(38, 40, 1, 6)"]
}
],
"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

@@ -11,27 +11,7 @@ export default {
msg: '请求成功',
data: {
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
},
'source|50': [
{
product: '@name',
'dataOne|0-900': 3
@@ -39,6 +19,22 @@ export default {
]
}
},
// 胶囊图
fetchCapsule: {
code: 0,
status: 200,
msg: '请求成功',
data: {
dimensions: ['name', 'value'],
source: [
{ name: '厦门', 'value|0-40': 20 },
{ name: '南阳', 'value|20-60': 40 },
{ name: '北京', 'value|40-80': 60 },
{ name: '上海', 'value|60-100': 80 },
{ name: '新疆', value: 100 }
]
}
},
// 图表
fetchMockData: {
code: 0,
@@ -46,32 +42,7 @@ export default {
msg: '请求成功',
data: {
dimensions: ['product', 'dataOne', 'dataTwo'],
source: [
{
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
},
'source|50': [
{
product: '@name',
'dataOne|100-900': 3,
@@ -85,21 +56,7 @@ export default {
code: 0,
status: 200,
msg: '请求成功',
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 }
]
'data|50': [{ name: '@name', 'value|100-900': 5 }]
},
// 轮播表格
fetchScrollBoard: {
@@ -254,4 +211,16 @@ export default {
msg: '请求成功',
data: tTreemapJson
},
// 三维地球
threeEarth01Data: {
code: 0,
status: 200,
msg: '请求成功',
data: [
{
startArray: { name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' },
'endArray|10': [{ name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' }]
}
]
}
}

View File

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

View File

@@ -1,84 +0,0 @@
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

@@ -1,33 +0,0 @@
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.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -11,7 +11,7 @@
</template>
<n-list-item>
<n-space :size="20">
<n-space class="go-my-2" :size="20">
<n-text class="item-left">版权声明</n-text>
<n-text>
GoView 版权属于
@@ -21,8 +21,7 @@
</n-list-item>
<n-list-item>
<n-divider style="margin-top: 0" />
<n-space :size="20">
<n-space class="go-my-2" :size="20">
<n-text class="item-left">协议备注</n-text>
<n-text>
请遵守开源 MIT 协议以上声明 <n-text type="error">不可删除</n-text>否则视作侵权行为后果自负
@@ -31,8 +30,7 @@
</n-list-item>
<n-list-item>
<n-divider style="margin-top: 0" />
<n-space :size="20">
<n-space class="go-mt-2" :size="20">
<n-text class="item-left">商业授权</n-text>
<n-text>
若不想保留版权声明请通过仓库/交流群 联系项目作者进行授权

View File

@@ -21,6 +21,23 @@
</setting-item-box>
</collapse-item>
<collapse-item v-if="grid" name="容器">
<setting-item-box name="距离">
<setting-item name="左侧距离">
<n-input v-model:value="grid.left" size="small"></n-input>
</setting-item>
<setting-item name="右侧距离">
<n-input v-model:value="grid.right" size="small"></n-input>
</setting-item>
<setting-item name="上侧距离">
<n-input v-model:value="grid.top" size="small"></n-input>
</setting-item>
<setting-item name="下侧距离">
<n-input v-model:value="grid.bottom" size="small"></n-input>
</setting-item>
</setting-item-box>
</collapse-item>
<collapse-item v-if="xAxis" name="X轴">
<template #header>
<n-switch v-model:value="xAxis.show" size="small"></n-switch>
@@ -51,6 +68,9 @@
<setting-item name="大小">
<n-input-number v-model:value="xAxis.axisLabel.fontSize" :min="8" size="small"></n-input-number>
</setting-item>
<setting-item name="偏移量">
<n-input-number v-model:value="xAxis.axisLabel.rotate" :min="-90" :max="90" size="small"></n-input-number>
</setting-item>
</setting-item-box>
<setting-item-box name="轴线">
<setting-item name="展示">
@@ -140,6 +160,9 @@
<setting-item name="大小">
<n-input-number v-model:value="yAxis.axisLabel.fontSize" :min="8" size="small"></n-input-number>
</setting-item>
<setting-item name="偏移量">
<n-input-number v-model:value="yAxis.axisLabel.rotate" :min="-90" :max="90" size="small"></n-input-number>
</setting-item>
</setting-item-box>
<setting-item-box name="轴线">
<setting-item name="展示">
@@ -289,6 +312,10 @@ const legend = computed(() => {
return props.optionData.legend
})
const grid = computed(() => {
return props.optionData.grid
})
const visualMap = computed(() => {
return props.optionData.visualMap
})

View File

@@ -1,5 +1,5 @@
<template>
<setting-item-box name="位置">
<setting-item-box v-if="targetData" name="位置">
<setting-item :name="`偏移 X${targetData.left || 0}px`">
<n-input-number v-model:value="targetData.left" size="small" step="10"></n-input-number>
</setting-item>

View File

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

View File

@@ -1,19 +1,13 @@
<template>
<div class="M-Flipper" :class="[flipType, { go: isFlipping }]">
<div class="go-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 } from 'vue'
import { FlipType } from '.'
import { FlipType } from './index'
const props = defineProps({
flipType: {
@@ -131,7 +125,7 @@ $lineColor: #4a9ef8;
}
// #endregion
.M-Flipper {
.go-Flipper {
display: inline-block;
position: relative;
width: $width;

View File

@@ -9,6 +9,12 @@ export enum DragKeyEnum {
DRAG_KEY = 'ChartData'
}
// 不同页面保存操作
export enum SavePageEnum {
CHART = 'SaveChart',
JSON = 'SaveJSON'
}
// 操作枚举
export enum MenuEnum {
// 移动
@@ -40,6 +46,7 @@ export enum MenuEnum {
UN_GROUP = 'unGroup',
// 后退
BACK = 'back',
// 前进
FORWORD = 'forward',
// 保存
SAVE = 'save',
@@ -73,15 +80,3 @@ export enum MacKeyboard {
SHIFT_SOURCE_KEY = '⇧',
ALT_SOURCE_KEY = '⌥'
}
// 同步状态枚举
export enum SyncEnum {
// 等待
PENDING,
// 开始
START,
// 成功
SUCCESS,
// 失败
FAILURE
}

View File

@@ -1,18 +1,13 @@
// 模块 Path 前缀分类
export enum ModuleTypeEnum {
SYSTEM = 'sys',
PROJECT = 'project',
}
// 请求结果集
/**
* @description: 请求结果集
*/
export enum ResultEnum {
DATA_SUCCESS = 0,
SUCCESS = 200,
SERVER_ERROR = 500,
SERVER_FORBIDDEN = 403,
NOT_FOUND = 404,
TOKEN_OVERDUE = 886,
TIMEOUT = 60000,
TIMEOUT = 60000
}
// 数据相关
@@ -31,13 +26,9 @@ export enum RequestContentTypeEnum {
SQL = 1
}
// 头部
export enum RequestHttpHeaderEnum {
TOKEN = 'Token',
COOKIE = 'Cookie'
}
// 请求方法
/**
* @description: 请求方法
*/
export enum RequestHttpEnum {
GET = 'get',
POST = 'post',
@@ -118,7 +109,9 @@ export type RequestParams = {
}
}
// 常用的contentTyp类型
/**
* @description: 常用的contentTyp类型
*/
export enum ContentTypeEnum {
// json
JSON = 'application/json;charset=UTF-8',

View File

@@ -12,6 +12,12 @@ 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',
@@ -20,15 +26,10 @@ 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

@@ -1,8 +1,10 @@
export enum StorageEnum {
// 全局设置
GO_SETTING_STORE = 'GO_SETTING',
GO_SYSTEM_SETTING_STORE = 'GO_SYSTEM_SETTING',
// token 等信息
GO_ACCESS_TOKEN_STORE = 'GO_ACCESS_TOKEN',
// 登录信息
GO_SYSTEM_STORE = 'GO_SYSTEM',
GO_LOGIN_INFO_STORE = 'GO_LOGIN_INFO',
// 语言
GO_LANG_STORE = 'GO_LANG',
// 当前选择的主题

View File

@@ -2,4 +2,5 @@ export * from '@/hooks/useTheme.hook'
export * from '@/hooks/usePreviewScale.hook'
export * from '@/hooks/useCode.hook'
export * from '@/hooks/useChartDataFetch.hook'
export * from '@/hooks/useSystemInit.hook'
export * from '@/hooks/useLifeHandler.hook'
export * from '@/hooks/useLang.hook'

View File

@@ -59,18 +59,18 @@ export const useChartDataFetch = (
const fetchFn = async () => {
const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.requestGlobalConfig))
if (res && res.data) {
if (res) {
try {
const filter = targetComponent.filter
// eCharts 组件配合 vChart 库更新方式
if (chartFrame === ChartFrameEnum.ECHARTS) {
if (vChartRef.value) {
vChartRef.value.setOption({ dataset: newFunctionHandle(res.data, filter) })
vChartRef.value.setOption({ dataset: newFunctionHandle(res?.data, res, filter) })
}
}
// 更新回调函数
if (updateCallback) {
updateCallback(newFunctionHandle(res.data, filter))
updateCallback(newFunctionHandle(res?.data, res, filter))
}
} catch (error) {
console.error(error)
@@ -90,7 +90,7 @@ export const useChartDataFetch = (
}
// eslint-disable-next-line no-empty
} catch (error) {
console.log(error);
console.log(error)
}
}

24
src/hooks/useLang.hook.ts Normal file
View File

@@ -0,0 +1,24 @@
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

@@ -0,0 +1,47 @@
import { CreateComponentType, EventLife } from '@/packages/index.d'
import * as echarts from 'echarts'
// 所有图表组件集合对象
const components: { [K in string]?: any } = {}
// 项目提供的npm 包变量
export const npmPkgs = { echarts }
export const useLifeHandler = (chartConfig: CreateComponentType) => {
const events = chartConfig.events || {}
// 生成生命周期事件
const lifeEvents = {
[EventLife.BEFORE_MOUNT](e: any) {
// 存储组件
components[chartConfig.id] = e.component
const fnStr = (events[EventLife.BEFORE_MOUNT] || '').trim()
generateFunc(fnStr, e)
},
[EventLife.MOUNTED](e: any) {
const fnStr = (events[EventLife.MOUNTED] || '').trim()
generateFunc(fnStr, e)
}
}
return lifeEvents
}
/**
*
* @param fnStr 用户方法体代码
* @param e 执行生命周期的动态组件实例
*/
function generateFunc(fnStr: string, e: any) {
try {
// npmPkgs 便于拷贝 echarts 示例时设置option 的formatter等相关内容
Function(`
"use strict";
return (
async function(e, components, node_modules){
const {${Object.keys(npmPkgs).join()}} = node_modules;
${fnStr}
}
)`)().bind(e?.component)(e, components, npmPkgs)
} catch (error) {
console.error(error)
}
}

View File

@@ -1,23 +0,0 @@
import { useSystemStore } from '@/store/modules/systemStore/systemStore'
import { SystemStoreEnum } from '@/store/modules/systemStore/systemStore.d'
import { ResultEnum } from '@/enums/httpEnum'
import { ossUrlApi } from '@/api/path/'
// * 初始化
export const useSystemInit = async () => {
const systemStore = useSystemStore()
// 获取 OSS 信息
const getOssUrl = async () => {
const res = await ossUrlApi({}) as unknown as MyResponseType
if (res.code === ResultEnum.SUCCESS) {
systemStore.setItem(SystemStoreEnum.FETCH_INFO, {
OSSUrl: res.data?.bucketURL
})
}
}
// 执行
getOssUrl()
}

View File

@@ -11,8 +11,6 @@ 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',
@@ -28,14 +26,8 @@ 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,8 +1,6 @@
export default {
create_btn: 'Creat',
create_success: 'Creat Success!',
create_failure: 'Failed to create, please try again later',
create_tip: 'Please select a content for development!',
create_tip: 'Please select a content for development',
project: 'Project',
my: 'My',
new_project: 'New Project',

View File

@@ -11,8 +11,6 @@ const global = {
help: '帮助中心',
contact: '关于软件',
logout: '退出登录',
logout_success: '退出成功!',
logout_failure: '退出失败!',
// 系统设置
sys_set: '系统设置',
lang_set: '语言设置',
@@ -20,27 +18,16 @@ 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: "登录成功!",
}

View File

@@ -1,8 +1,6 @@
export default {
// aside
create_btn: '新建',
create_success: '新建成功!',
create_failure: '新建失败,请稍后重试!',
create_tip: '从哪里出发好呢?',
project: '项目',
my: '我的',

View File

@@ -4,17 +4,21 @@ import { CreateComponentType } from '@/packages/index.d'
import cloneDeep from 'lodash/cloneDeep'
import dataJson from './data.json'
export const includes = ['legend', 'xAxis', 'yAxis']
export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
export const seriesItem = {
type: 'bar',
barWidth: null,
barWidth: 15,
label: {
show: true,
position: 'top',
color: '#fff',
fontSize: 12
},
itemStyle: {
color: null,
borderRadius: 0
borderRadius: 2
}
}
export const option = {
tooltip: {
show: true,
@@ -24,10 +28,7 @@ export const option = {
type: 'shadow'
}
},
legend: {
show: true
},
xAxis: {
xAxis: {
show: true,
type: 'category'
},

View File

@@ -1,25 +1,8 @@
<template>
<!-- Echarts 全局设置 -->
<!-- Echarts 全局设置 -->
<global-setting :optionData="optionData"></global-setting>
<CollapseItem
v-for="(item, index) in seriesList"
:key="index"
:name="`柱状图-${index + 1}`"
:expanded="true"
>
<CollapseItem v-for="(item, index) in seriesList" :key="index" :name="`柱状图-${index + 1}`" :expanded="true">
<SettingItemBox name="图形">
<SettingItem name="颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="item.itemStyle.color"
></n-color-picker>
</SettingItem>
<SettingItem>
<n-button size="small" @click="item.itemStyle.color = null">
恢复默认
</n-button>
</SettingItem>
<SettingItem name="宽度">
<n-input-number
v-model:value="item.barWidth"
@@ -30,24 +13,40 @@
></n-input-number>
</SettingItem>
<SettingItem name="圆角">
<n-input-number
v-model:value="item.itemStyle.borderRadius"
:min="0"
size="small"
></n-input-number>
<n-input-number v-model:value="item.itemStyle.borderRadius" :min="0" size="small"></n-input-number>
</SettingItem>
</SettingItemBox>
<setting-item-box name="标签">
<setting-item>
<n-space>
<n-switch v-model:value="item.label.show" size="small" />
<n-text>展示标签</n-text>
</n-space>
</setting-item>
<setting-item name="大小">
<n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number>
</setting-item>
<setting-item name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker>
</setting-item>
<setting-item name="位置">
<n-select
v-model:value="item.label.position"
:options="[
{ label: 'top', value: 'top' },
{ label: 'left', value: 'left' },
{ label: 'right', value: 'right' },
{ label: 'bottom', value: 'bottom' }
]"
/>
</setting-item>
</setting-item-box>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, computed } from 'vue'
import {
GlobalSetting,
CollapseItem,
SettingItemBox,
SettingItem
} from '@/components/Pages/ChartItemSetting'
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
const props = defineProps({

View File

@@ -53,17 +53,21 @@ const option = computed(() => {
watch(
() => props.chartConfig.option.dataset,
(newData: { dimensions: any }, oldData) => {
if (!isObject(newData) || !('dimensions' in newData)) return
if (newData?.dimensions.length !== oldData?.dimensions.length) {
const seriesArr = []
for (let i = 0; i < newData.dimensions.length - 1; i++) {
seriesArr.push(seriesItem)
try {
if (!isObject(newData) || !('dimensions' in newData)) return
if (Array.isArray(newData?.dimensions)) {
const seriesArr = []
for (let i = 0; i < newData.dimensions.length - 1; i++) {
seriesArr.push(seriesItem)
}
replaceMergeArr.value = ['series']
props.chartConfig.option.series = seriesArr
nextTick(() => {
replaceMergeArr.value = []
})
}
replaceMergeArr.value = ['series']
props.chartConfig.option.series = seriesArr
nextTick(() => {
replaceMergeArr.value = []
})
} catch (error) {
console.log(error)
}
},
{

View File

@@ -4,17 +4,21 @@ import { CreateComponentType } from '@/packages/index.d'
import cloneDeep from 'lodash/cloneDeep'
import dataJson from './data.json'
export const includes = ['legend', 'xAxis', 'yAxis']
export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
export const seriesItem = {
type: 'bar',
barWidth: null,
label: {
show: true,
position: 'right',
color: '#fff',
fontSize: 12
},
itemStyle: {
color: null,
borderRadius: 0
}
}
export const option = {
tooltip: {
show: true,
@@ -24,12 +28,9 @@ export const option = {
type: 'shadow'
}
},
legend: {
show: true
},
xAxis: {
show: true,
type: 'value',
type: 'value'
},
yAxis: {
show: true,

View File

@@ -3,12 +3,6 @@
<global-setting :optionData="optionData"></global-setting>
<CollapseItem v-for="(item, index) in seriesList" :key="index" :name="`柱状图-${index+1}`" :expanded="true">
<SettingItemBox name="图形">
<SettingItem name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="item.itemStyle.color"></n-color-picker>
</SettingItem>
<SettingItem>
<n-button size="small" @click="item.itemStyle.color = null">恢复默认</n-button>
</SettingItem>
<SettingItem name="宽度">
<n-input-number
v-model:value="item.barWidth"
@@ -26,6 +20,39 @@
></n-input-number>
</SettingItem>
</SettingItemBox>
<setting-item-box name="标签">
<setting-item>
<n-space>
<n-switch v-model:value="item.label.show" size="small" />
<n-text>展示标签</n-text>
</n-space>
</setting-item>
<setting-item name="大小">
<n-input-number
v-model:value="item.label.fontSize"
size="small"
:min="1"
></n-input-number>
</setting-item>
<setting-item name="颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="item.label.color"
></n-color-picker>
</setting-item>
<setting-item name="位置">
<n-select
v-model:value="item.label.position"
:options="[
{ label: 'top', value: 'top' },
{ label: 'left', value: 'left' },
{ label: 'right', value: 'right' },
{ label: 'bottom', value: 'bottom' },
]"
/>
</setting-item>
</setting-item-box>
</CollapseItem>
</template>

View File

@@ -52,17 +52,21 @@ const option = computed(() => {
watch(
() => props.chartConfig.option.dataset,
(newData: { dimensions: any }, oldData) => {
if (!isObject(newData) || !('dimensions' in newData)) return
if (newData?.dimensions.length !== oldData?.dimensions.length) {
const seriesArr = []
for (let i = 0; i < newData.dimensions.length - 1; i++) {
seriesArr.push(seriesItem)
try {
if (!isObject(newData) || !('dimensions' in newData)) return
if (Array.isArray(newData?.dimensions)) {
const seriesArr = []
for (let i = 0; i < newData.dimensions.length - 1; i++) {
seriesArr.push(seriesItem)
}
replaceMergeArr.value = ['series']
props.chartConfig.option.series = seriesArr
nextTick(() => {
replaceMergeArr.value = []
})
}
replaceMergeArr.value = ['series']
props.chartConfig.option.series = seriesArr
nextTick(() => {
replaceMergeArr.value = []
})
} catch (error) {
console.log(error)
}
},
{

View File

@@ -0,0 +1,25 @@
import { PublicConfigClass } from '@/packages/public'
import { CapsuleChartConfig } from './index'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig } from '@/settings/designSetting'
import cloneDeep from 'lodash/cloneDeep'
import dataJson from './data.json'
export const option = {
dataset: dataJson,
colors: ['#c4ebad', '#6be6c1', '#a0a7e6', '#96dee8', '#3fb1e3' ],
unit: '',
itemHeight: 10,
valueFontSize: 16,
paddingRight: 50,
paddingLeft: 50,
showValue: true
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key: string = CapsuleChartConfig.key
public attr = { ...chartInitConfig, zIndex: -1 }
public chartConfig = cloneDeep(CapsuleChartConfig)
public option = cloneDeep(option)
}

View File

@@ -0,0 +1,53 @@
<template>
<!-- Echarts 全局设置 -->
<global-setting :optionData="optionData"> </global-setting>
<!-- 胶囊柱图 -->
<collapse-item name="胶囊柱图" expanded>
<SettingItemBox name="布局">
<setting-item name="左侧边距">
<n-input-number v-model:value="optionData.paddingLeft" :min="10" :step="1" size="small"></n-input-number>
</setting-item>
<setting-item name="右侧边距">
<n-input-number v-model:value="optionData.paddingRight" :min="10" :step="1" size="small"></n-input-number>
</setting-item>
<setting-item name="每块高度(px)">
<n-input-number v-model:value="optionData.itemHeight" :min="0" :step="1" size="small"></n-input-number>
</setting-item>
</SettingItemBox>
<SettingItemBox name="文本">
<setting-item name="所有文字大小">
<n-input-number v-model:value="optionData.valueFontSize" :min="0" :step="1" size="small"></n-input-number>
</setting-item>
<setting-item name="单位">
<n-input v-model:value="optionData.unit" size="small"></n-input>
</setting-item>
<SettingItem>
<n-space>
<n-switch v-model:value="optionData.showValue" size="small"></n-switch>
<n-text>显示数值</n-text>
</n-space>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="颜色">
<setting-item v-for="(item, index) in optionData.colors" :key="index" :name="`颜色${index}`">
<n-color-picker v-model:value="optionData.colors[index]" size="small" :modes="['hex']"></n-color-picker>
</setting-item>
</SettingItemBox>
</collapse-item>
</template>
<script setup lang="ts">
import { PropType, computed } from 'vue'
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import { option } from './config'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option & GlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -0,0 +1,10 @@
{
"dimensions": ["name", "value"],
"source": [
{ "name": "厦门", "value": 20 },
{ "name": "南阳", "value": 40 },
{ "name": "北京", "value": 60 },
{ "name": "上海", "value": 80 },
{ "name": "新疆", "value": 100 }
]
}

View File

@@ -0,0 +1,15 @@
import image from '@/assets/images/chart/charts/capsule.png'
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const CapsuleChartConfig: ConfigType = {
key: 'CapsuleChart',
chartKey: 'VCapsuleChart',
conKey: 'VCCapsuleChart',
title: '胶囊柱图',
category: ChatCategoryEnum.BAR,
categoryName: ChatCategoryEnumName.BAR,
package: PackagesCategoryEnum.CHARTS,
chartFrame: ChartFrameEnum.COMMON,
image
}

View File

@@ -0,0 +1,228 @@
<template>
<div
v-if="state.mergedConfig"
class="go-dv-capsule-chart"
:style="{
fontSize: numberSizeHandle(state.mergedConfig.valueFontSize),
paddingLeft: numberSizeHandle(state.mergedConfig.paddingLeft),
paddingRight: numberSizeHandle(state.mergedConfig.paddingRight)
}"
>
<div class="label-column">
<div
v-for="item in state.mergedConfig.dataset.source"
:key="item[state.mergedConfig.dataset.dimensions[0]]"
:style="{ height: state.capsuleItemHeight, lineHeight: state.capsuleItemHeight }"
>
{{ item[state.mergedConfig.dataset.dimensions[0]] }}
</div>
<div class="laset">&nbsp;</div>
</div>
<div class="capsule-container">
<div
v-for="(capsule, index) in state.capsuleLength"
:key="index"
class="capsule-item"
:style="{ height: state.capsuleItemHeight }"
>
<div
class="capsule-item-column"
:style="`width: ${capsule * 100}%; background-color: ${
state.mergedConfig.colors[index % state.mergedConfig.colors.length]
};height:calc(100% - ${2}px);`"
>
<div v-if="state.mergedConfig.showValue" class="capsule-item-value">
{{ state.capsuleValue[index] }}
</div>
</div>
</div>
<div class="unit-label">
<div v-for="(label, index) in state.labelData" :key="label + index">
{{ label }}
</div>
</div>
</div>
<div v-if="state.mergedConfig.unit" class="unit-text">
{{ state.mergedConfig.unit }}
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, watch, reactive, PropType } from 'vue'
import merge from 'lodash/merge'
import cloneDeep from 'lodash/cloneDeep'
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import config, { option } from './config'
type DataProps = {
name: string | number
value: string | number
[key: string]: string | number
}
interface StateProps {
defaultConfig: {
dataset: {
dimensions: Array<string>
source: Array<DataProps>
}
colors: Array<string>
unit: string
showValue: boolean
itemHeight: number
valueFontSize: number
paddingLeft: number
paddingRight: number
}
mergedConfig: any
capsuleLength: Array<number>
capsuleValue: Array<string | Object>
labelData: Array<number>
capsuleItemHeight: string
}
const props = defineProps({
chartConfig: {
type: Object as PropType<config>,
default: () => ({})
}
})
const state = reactive<StateProps>({
defaultConfig: option,
mergedConfig: null,
capsuleLength: [],
capsuleValue: [],
labelData: [],
capsuleItemHeight: ''
})
watch(
() => props.chartConfig.option,
newVal => {
calcData(newVal)
},
{
deep: true
}
)
const calcData = (data: any) => {
mergeConfig(props.chartConfig.option)
calcCapsuleLengthAndLabelData()
}
const mergeConfig = (data: any) => {
state.mergedConfig = merge(cloneDeep(state.defaultConfig), data || {})
}
// 数据解析
const calcCapsuleLengthAndLabelData = () => {
const { source } = state.mergedConfig.dataset
if (!source.length) return
state.capsuleItemHeight = numberSizeHandle(state.mergedConfig.itemHeight)
const capsuleValue = source.map((item: DataProps) => item[state.mergedConfig.dataset.dimensions[1]])
const maxValue = Math.max(...capsuleValue)
state.capsuleValue = capsuleValue
state.capsuleLength = capsuleValue.map((v: any) => (maxValue ? v / maxValue : 0))
const oneFifth = maxValue / 5
const labelData = Array.from(new Set(new Array(6).fill(0).map((v, i) => Math.ceil(i * oneFifth))))
state.labelData = labelData
}
const numberSizeHandle = (val: string | number) => {
return val + 'px'
}
onMounted(() => {
calcData(props.chartConfig.option)
})
// 预览
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
calcData(newData)
})
</script>
<style lang="scss" scoped>
@include go('dv-capsule-chart') {
position: relative;
display: flex;
flex-direction: row;
box-sizing: border-box;
padding: 20px;
padding-right: 50px;
color: #b9b8cc;
.label-column {
display: flex;
flex-direction: column;
justify-content: space-between;
box-sizing: border-box;
padding-right: 10px;
text-align: right;
> div:not(:last-child) {
margin: 5px 0;
}
}
.capsule-container {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.capsule-item {
box-shadow: 0 0 3px #999;
height: 10px;
margin: 5px 0px;
border-radius: 5px;
.capsule-item-column {
position: relative;
height: 8px;
margin-top: 1px;
border-radius: 5px;
transition: all 0.3s;
display: flex;
justify-content: flex-end;
align-items: center;
.capsule-item-value {
padding-left: 10px;
transform: translateX(100%);
}
}
}
.unit-label {
height: 20px;
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
}
.unit-text {
text-align: right;
display: flex;
align-items: flex-end;
line-height: 20px;
margin-left: 10px;
}
}
</style>

View File

@@ -1,4 +1,5 @@
import { BarCommonConfig } from './BarCommon/index'
import { BarCrossrangeConfig } from './BarCrossrange/index'
import { CapsuleChartConfig } from './CapsuleChart/index'
export default [BarCommonConfig, BarCrossrangeConfig]
export default [BarCommonConfig, BarCrossrangeConfig, CapsuleChartConfig]

View File

@@ -4,17 +4,24 @@ import { CreateComponentType } from '@/packages/index.d'
import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
import dataJson from './data.json'
export const includes = ['legend', 'xAxis', 'yAxis']
export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
export const seriesItem = {
type: 'line',
label: {
show: true,
position: 'top',
color: '#fff',
fontSize: 12
},
symbolSize: 5, //设定实心点的大小
itemStyle: {
color: null,
borderRadius: 0
},
lineStyle: {
type: 'solid',
width: 3,
itemStyle: {
color: null,
borderRadius: 0
}
color: null
}
}
@@ -26,9 +33,6 @@ export const option = {
type: 'line'
}
},
legend: {
show: true
},
xAxis: {
show: true,
type: 'category'

View File

@@ -1,12 +1,7 @@
<template>
<!-- Echarts 全局设置 -->
<global-setting :optionData="optionData"></global-setting>
<CollapseItem
v-for="(item, index) in seriesList"
:key="index"
:name="`折线图-${index + 1}`"
:expanded="true"
>
<CollapseItem v-for="(item, index) in seriesList" :key="index" :name="`折线图-${index + 1}`" :expanded="true">
<SettingItemBox name="线条">
<SettingItem name="宽度">
<n-input-number
@@ -15,16 +10,48 @@
:max="100"
size="small"
placeholder="自动计算"
></n-input-number>
></n-input-number>
</SettingItem>
<SettingItem name="类型">
<n-select
v-model:value="item.lineStyle.type"
size="small"
:options="lineConf.lineStyle.type"
></n-select>
<n-select v-model:value="item.lineStyle.type" size="small" :options="lineConf.lineStyle.type"></n-select>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="实心点">
<SettingItem name="大小">
<n-input-number
v-model:value="item.symbolSize"
:min="1"
:max="100"
size="small"
placeholder="自动计算"
></n-input-number>
</SettingItem>
</SettingItemBox>
<setting-item-box name="标签">
<setting-item>
<n-space>
<n-switch v-model:value="item.label.show" size="small" />
<n-text>展示标签</n-text>
</n-space>
</setting-item>
<setting-item name="大小">
<n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number>
</setting-item>
<setting-item name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker>
</setting-item>
<setting-item name="位置">
<n-select
v-model:value="item.label.position"
:options="[
{ label: 'top', value: 'top' },
{ label: 'left', value: 'left' },
{ label: 'right', value: 'right' },
{ label: 'bottom', value: 'bottom' }
]"
/>
</setting-item>
</setting-item-box>
</CollapseItem>
</template>
@@ -32,12 +59,7 @@
import { PropType, computed } from 'vue'
import { lineConf } from '@/packages/chartConfiguration/echarts/index'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import {
GlobalSetting,
CollapseItem,
SettingItemBox,
SettingItem
} from '@/components/Pages/ChartItemSetting'
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
const props = defineProps({
optionData: {

View File

@@ -53,17 +53,21 @@ const option = computed(() => {
watch(
() => props.chartConfig.option.dataset,
(newData: { dimensions: any }, oldData) => {
if (!isObject(newData) || !('dimensions' in newData)) return
if (newData?.dimensions.length !== oldData?.dimensions.length) {
const seriesArr = []
for (let i = 0; i < newData.dimensions.length - 1; i++) {
seriesArr.push(seriesItem)
try {
if (!isObject(newData) || !('dimensions' in newData)) return
if (Array.isArray(newData?.dimensions)) {
const seriesArr = []
for (let i = 0; i < newData.dimensions.length - 1; i++) {
seriesArr.push(seriesItem)
}
replaceMergeArr.value = ['series']
props.chartConfig.option.series = seriesArr
nextTick(() => {
replaceMergeArr.value = []
})
}
replaceMergeArr.value = ['series']
props.chartConfig.option.series = seriesArr
nextTick(() => {
replaceMergeArr.value = []
})
} catch (error) {
console.log(error)
}
},
{

View File

@@ -5,7 +5,7 @@ import { graphic } from 'echarts/core'
import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
import dataJson from './data.json'
export const includes = ['legend', 'xAxis', 'yAxis']
export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
const options = {
tooltip: {
@@ -15,9 +15,6 @@ const options = {
type: 'line'
}
},
legend: {
show: true
},
xAxis: {
show: true,
type: 'category'
@@ -31,6 +28,13 @@ const options = {
{
type: 'line',
smooth: false,
symbolSize: 5, //设定实心点的大小
label: {
show: true,
position: 'top',
color: '#fff',
fontSize: 12
},
lineStyle: {
type: 'solid',
width: 3

View File

@@ -1,12 +1,7 @@
<template>
<!-- Echarts 全局设置 -->
<global-setting :optionData="optionData"></global-setting>
<CollapseItem
v-for="(item, index) in seriesList"
:key="index"
name="单折线面积图"
:expanded="true"
>
<CollapseItem v-for="(item, index) in seriesList" :key="index" name="单折线面积图" :expanded="true">
<SettingItemBox name="线条">
<SettingItem name="宽度">
<n-input-number
@@ -18,13 +13,45 @@
></n-input-number>
</SettingItem>
<SettingItem name="类型">
<n-select
v-model:value="item.lineStyle.type"
size="small"
:options="lineConf.lineStyle.type"
></n-select>
<n-select v-model:value="item.lineStyle.type" size="small" :options="lineConf.lineStyle.type"></n-select>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="实心点">
<SettingItem name="大小">
<n-input-number
v-model:value="item.symbolSize"
:min="1"
:max="100"
size="small"
placeholder="自动计算"
></n-input-number>
</SettingItem>
</SettingItemBox>
<setting-item-box name="标签">
<setting-item>
<n-space>
<n-switch v-model:value="item.label.show" size="small" />
<n-text>展示标签</n-text>
</n-space>
</setting-item>
<setting-item name="大小">
<n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number>
</setting-item>
<setting-item name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker>
</setting-item>
<setting-item name="位置">
<n-select
v-model:value="item.label.position"
:options="[
{ label: 'top', value: 'top' },
{ label: 'left', value: 'left' },
{ label: 'right', value: 'right' },
{ label: 'bottom', value: 'bottom' }
]"
/>
</setting-item>
</setting-item-box>
</CollapseItem>
</template>
@@ -32,18 +59,13 @@
import { PropType, computed } from 'vue'
import { lineConf } from '@/packages/chartConfiguration/echarts/index'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import {
GlobalSetting,
CollapseItem,
SettingItemBox,
SettingItem
} from '@/components/Pages/ChartItemSetting'
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
const props = defineProps({
optionData: {
type: Object as PropType<GlobalThemeJsonType>,
required: true
},
}
})
const seriesList = computed(() => {

View File

@@ -43,23 +43,27 @@ const option = reactive({
watch(
() => chartEditStore.getEditCanvasConfig.chartThemeColor,
(newColor: keyof typeof chartColorsSearch) => {
if (!isPreview()) {
const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
props.chartConfig.option.series.forEach((value: any, index: number) => {
value.areaStyle.color = new graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: themeColor[3]
},
{
offset: 1,
color: 'rgba(0,0,0, 0)'
}
])
})
try {
if (!isPreview()) {
const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
props.chartConfig.option.series.forEach((value: any, index: number) => {
value.areaStyle.color = new graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: themeColor[3]
},
{
offset: 1,
color: 'rgba(0,0,0, 0)'
}
])
})
}
option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
props.chartConfig.option = option.value
} catch (error) {
console.log(error)
}
option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
props.chartConfig.option = option.value
},
{
immediate: true
@@ -70,9 +74,6 @@ watch(
() => props.chartConfig.option.dataset,
() => {
option.value = props.chartConfig.option
},
{
deep: false
}
)

View File

@@ -5,12 +5,9 @@ import { graphic } from 'echarts/core'
import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
import dataJson from './data.json'
export const includes = ['legend', 'xAxis', 'yAxis']
export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
const option = {
legend: {
show: true
},
tooltip: {
show: true,
trigger: 'axis',
@@ -31,6 +28,13 @@ const option = {
{
type: 'line',
smooth: false,
symbolSize: 5, //设定实心点的大小
label: {
show: true,
position: 'top',
color: '#fff',
fontSize: 12
},
lineStyle: {
width: 3,
type: 'solid'
@@ -52,6 +56,12 @@ const option = {
{
type: 'line',
smooth: false,
label: {
show: true,
position: 'top',
color: '#fff',
fontSize: 12
},
lineStyle: {
width: 3,
type: 'solid'

View File

@@ -12,7 +12,6 @@
<n-input-number
v-model:value="item.lineStyle.width"
:min="1"
:max="100"
size="small"
placeholder="自动计算"
></n-input-number>
@@ -25,6 +24,50 @@
></n-select>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="实心点">
<SettingItem name="大小">
<n-input-number
v-model:value="item.symbolSize"
:min="1"
:max="100"
size="small"
placeholder="自动计算"
></n-input-number>
</SettingItem>
</SettingItemBox>
<setting-item-box name="标签">
<setting-item>
<n-space>
<n-switch v-model:value="item.label.show" size="small" />
<n-text>展示标签</n-text>
</n-space>
</setting-item>
<setting-item name="大小">
<n-input-number
v-model:value="item.label.fontSize"
size="small"
:min="1"
></n-input-number>
</setting-item>
<setting-item name="颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="item.label.color"
></n-color-picker>
</setting-item>
<setting-item name="位置">
<n-select
v-model:value="item.label.position"
:options="[
{ label: 'top', value: 'top' },
{ label: 'left', value: 'left' },
{ label: 'right', value: 'right' },
{ label: 'bottom', value: 'bottom' },
]"
/>
</setting-item>
</setting-item-box>
</CollapseItem>
</template>

View File

@@ -42,23 +42,27 @@ const option = reactive({
watch(
() => chartEditStore.getEditCanvasConfig.chartThemeColor,
(newColor: keyof typeof chartColorsSearch) => {
if (!isPreview()) {
const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
props.chartConfig.option.series.forEach((value: any, index: number) => {
value.areaStyle.color = new graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: themeColor[3 + index]
},
{
offset: 1,
color: 'rgba(0,0,0, 0)'
}
])
})
try {
if (!isPreview()) {
const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
props.chartConfig.option.series.forEach((value: any, index: number) => {
value.areaStyle.color = new graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: themeColor[3 + index]
},
{
offset: 1,
color: 'rgba(0,0,0, 0)'
}
])
})
}
option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
props.chartConfig.option = option.value
} catch (error) {
console.log(error)
}
option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
props.chartConfig.option = option.value
},
{
immediate: true
@@ -71,6 +75,5 @@ watch(
option.value = props.chartConfig.option
}
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
</script>

View File

@@ -4,7 +4,7 @@ import { CreateComponentType } from '@/packages/index.d'
import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
import dataJson from './data.json'
export const includes = ['legend', 'xAxis', 'yAxis']
export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
export const option = {
tooltip: {
@@ -14,9 +14,6 @@ export const option = {
type: 'line'
}
},
legend: {
show: true
},
xAxis: {
show: true,
type: 'category'
@@ -29,6 +26,7 @@ export const option = {
series: [
{
type: 'line',
symbolSize: 5, //设定实心点的大小
lineStyle: {
type: 'solid',
width: 3,

View File

@@ -39,6 +39,17 @@
></n-select>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="实心点">
<SettingItem name="大小">
<n-input-number
v-model:value="item.symbolSize"
:min="1"
:max="100"
size="small"
placeholder="自动计算"
></n-input-number>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="阴影" :alone="true">
<SettingItem name="颜色">
<n-color-picker

View File

@@ -43,17 +43,21 @@ const option = reactive({
watch(
() => chartEditStore.getEditCanvasConfig.chartThemeColor,
(newColor: keyof typeof chartColorsSearch) => {
if (!isPreview()) {
const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
props.chartConfig.option.series.forEach((value: any) => {
value.lineStyle.shadowColor = themeColor[2]
value.lineStyle.color.colorStops.forEach((v: { color: string }, i: number) => {
v.color = themeColor[i]
try {
if (!isPreview()) {
const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
props.chartConfig.option.series.forEach((value: any) => {
value.lineStyle.shadowColor = themeColor[2]
value.lineStyle.color.colorStops.forEach((v: { color: string }, i: number) => {
v.color = themeColor[i]
})
})
})
}
option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
props.chartConfig.option = option.value
} catch (error) {
console.log(error)
}
option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
props.chartConfig.option = option.value
},
{
immediate: true

View File

@@ -0,0 +1,83 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { MapAmapConfig } from './index'
import { chartInitConfig } from '@/settings/designSetting'
import cloneDeep from 'lodash/cloneDeep'
import dataJson from './data.json'
export enum ThemeEnum {
NORMAL = 'normal',
DARK = 'dark',
LIGHT = 'light',
WHITES_MOKE = 'whitesmoke',
FRESH = 'fresh',
GREY = 'grey',
GRAFFITI = 'graffiti',
MACARON = 'macaron',
BLUE = 'blue',
DARKBLUE = 'darkblue',
WINE = 'wine'
}
export enum LangEnum {
ZH_CN = 'zh_cn',
EN = 'en',
ZH_EN = 'zh_en'
}
export enum ViewModeEnum {
PLANE = '2D',
STEREOSCOPIC = '3D'
}
export enum FeaturesEnum {
BG = 'bg',
POINT = 'point',
ROAD = 'road',
BUILDING = 'building'
}
export enum MarkerEnum {
// 圆圈
CIRCLE_MARKER = 'CircleMarker',
// 定位标点
MARKER = 'Marker',
// 暂无
NONE = 'none'
}
export const option = {
dataset: dataJson,
mapOptions: {
pitch: 60,
skyColor: '#53A9DE',
amapKey: 'd5f3e16589dbecae64d05fe90e2ba4f2',
amapStyleKey: ThemeEnum.DARK,
amapStyleKeyCustom: '',
amapLon: 116.397428,
amapLat: 39.90923,
amapZindex: 11,
marker: {
fillColor: '#E98984FF',
fillOpacity: 0.5,
strokeColor: 'white',
strokeWeight: 2,
strokeOpacity: 0.5,
zIndex: 10,
bubble: true,
cursor: 'pointer',
clickable: true
},
mapMarkerType: MarkerEnum.CIRCLE_MARKER,
viewMode: ViewModeEnum.PLANE,
lang: LangEnum.ZH_CN,
features: [FeaturesEnum.BG, FeaturesEnum.POINT, FeaturesEnum.ROAD, FeaturesEnum.BUILDING]
}
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = MapAmapConfig.key
public attr = { ...chartInitConfig, w: 1000, h: 800, zIndex: -1 }
public chartConfig = cloneDeep(MapAmapConfig)
public option = cloneDeep(option)
}

View File

@@ -0,0 +1,199 @@
<template>
<collapse-item name="基础" :expanded="true">
<setting-item-box name="语言类型" :alone="true">
<setting-item>
<n-select size="small" v-model:value="optionData.mapOptions.lang" :options="langOptions" />
</setting-item>
</setting-item-box>
<setting-item-box name="Key" :alone="true">
<setting-item name="请务必使用自己的高德应用 key">
<n-input v-model:value="optionData.mapOptions.amapKey" size="small"></n-input>
</setting-item>
</setting-item-box>
<setting-item-box name="自定义地图样式ID" :alone="true">
<setting-item>
<n-input size="small" v-model:value="optionData.mapOptions.amapStyleKeyCustom" />
</setting-item>
</setting-item-box>
</collapse-item>
<collapse-item name="地图" :expanded="true">
<setting-item-box name="主题">
<setting-item>
<n-select size="small" v-model:value="optionData.mapOptions.amapStyleKey" :options="themeOptions" />
</setting-item>
</setting-item-box>
<setting-item-box name="内容" :alone="true">
<n-checkbox-group v-model:value="optionData.mapOptions.features">
<n-space item-style="display: flex;">
<n-checkbox :value="item.value" :label="item.label" v-for="(item, index) in featuresOptions" :key="index" />
</n-space>
</n-checkbox-group>
</setting-item-box>
<setting-item-box name="位置">
<setting-item name="经度">
<n-input-number v-model:value="optionData.mapOptions.amapLon" :show-button="false" size="small">
<template #suffix>°</template>
</n-input-number>
</setting-item>
<setting-item name="纬度">
<n-input-number v-model:value="optionData.mapOptions.amapLat" :show-button="false" size="small">
<template #suffix>°</template>
</n-input-number>
</setting-item>
<setting-item name="初始缩放">
<n-input-number v-model:value="optionData.mapOptions.amapZindex" :min="0" size="small"></n-input-number>
</setting-item>
</setting-item-box>
<setting-item-box name="模式" :alone="true">
<setting-item>
<n-radio-group v-model:value="optionData.mapOptions.viewMode" name="radiogroup">
<n-space>
<n-radio v-for="song in viewModeOptions" :key="song.value" :value="song.value">
{{ song.label }}
</n-radio>
</n-space>
</n-radio-group>
</setting-item>
</setting-item-box>
<template v-if="optionData.mapOptions.viewMode === '3D'">
<setting-item-box>
<setting-item name="天空色">
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.mapOptions.skyColor"></n-color-picker>
</setting-item>
<setting-item name="俯仰角">
<n-input-number v-model:value="optionData.mapOptions.pitch" :min="0" :max="83" size="small"></n-input-number>
</setting-item>
</setting-item-box>
</template>
</collapse-item>
<collapse-item name="标记" :expanded="true">
<setting-item-box name="样式">
<setting-item name="类型">
<n-select size="small" v-model:value="optionData.mapOptions.mapMarkerType" :options="MarkerOptions" />
</setting-item>
<setting-item name="颜色">
<n-color-picker v-model:value="optionData.mapOptions.marker.fillColor" size="small"></n-color-picker>
</setting-item>
</setting-item-box>
</collapse-item>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { option, MarkerEnum, ThemeEnum, LangEnum, ViewModeEnum, FeaturesEnum } from './config'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true
}
})
const themeOptions = [
{
value: ThemeEnum.NORMAL,
label: '标准'
},
{
value: ThemeEnum.DARK,
label: '幻影黑'
},
{
value: ThemeEnum.LIGHT,
label: '月光银'
},
{
value: ThemeEnum.WHITES_MOKE,
label: '远山黛'
},
{
value: ThemeEnum.FRESH,
label: '草色青'
},
{
value: ThemeEnum.GREY,
label: '雅士灰'
},
{
value: ThemeEnum.GRAFFITI,
label: '涂鸦'
},
{
value: ThemeEnum.MACARON,
label: '马卡龙'
},
{
value: ThemeEnum.BLUE,
label: '靛青蓝'
},
{
value: ThemeEnum.DARKBLUE,
label: '极夜蓝'
},
{
value: ThemeEnum.WINE,
label: '酱籽'
}
]
const langOptions = [
{
value: LangEnum.ZH_CN,
label: '中文简体'
},
{
value: LangEnum.EN,
label: '英文'
},
{
value: LangEnum.ZH_EN,
label: '中英文对照'
}
]
const viewModeOptions = [
{
value: ViewModeEnum.PLANE,
label: '2D'
},
{
value: ViewModeEnum.STEREOSCOPIC,
label: '3D'
}
]
const featuresOptions = [
{
value: FeaturesEnum.BG,
label: '显示地图背景'
},
{
value: FeaturesEnum.POINT,
label: '显示标识'
},
{
value: FeaturesEnum.ROAD,
label: '显示道路'
},
{
value: FeaturesEnum.BUILDING,
label: '显示建筑'
}
]
const MarkerOptions = [
{
value: MarkerEnum.CIRCLE_MARKER,
label: '圆形标点'
},
{
value: MarkerEnum.MARKER,
label: '定位标点'
},
{
value: MarkerEnum.NONE,
label: '隐藏标点'
}
]
</script>

View File

@@ -0,0 +1,19 @@
{
"markers": [
{
"name": "某某地市",
"value": 10,
"position": [116.300467, 39.907761]
},
{
"name": "某某地市",
"value": 15,
"position": [116.400567, 39.908761]
},
{
"name": "某某地市",
"value": 20,
"position": [116.200467, 39.937761]
}
]
}

View File

@@ -0,0 +1,15 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import image from '@/assets/images/chart/charts/map_amap.png'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const MapAmapConfig: ConfigType = {
key: 'MapAmap',
chartKey: 'VMapAmap',
conKey: 'VCMapAmap',
title: '高德地图',
category: ChatCategoryEnum.MAP,
categoryName: ChatCategoryEnumName.MAP,
package: PackagesCategoryEnum.CHARTS,
chartFrame: ChartFrameEnum.COMMON,
image
}

View File

@@ -0,0 +1,130 @@
<template>
<div ref="vChartRef"></div>
</template>
<script setup lang="ts">
import { ref, PropType, toRefs, watch } from 'vue'
import AMapLoader from '@amap/amap-jsapi-loader'
import { CreateComponentType } from '@/packages/index.d'
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { MarkerEnum } from './config'
import { isArray } from '@/utils'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
let {
amapKey,
amapStyleKey,
amapLon,
amapLat,
amapZindex,
mapMarkerType,
lang,
amapStyleKeyCustom,
features,
viewMode,
pitch,
skyColor,
marker
} = toRefs(props.chartConfig.option.mapOptions)
let mapIns: any = null
let markers: any = []
let AMapIns: any = null
const vChartRef = ref<HTMLElement>()
const initMap = (newData: any) => {
// 初始化
AMapLoader.load({
key: amapKey.value, //api服务key--另外需要在public中使用安全密钥
version: '1.4.8', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.PlaceSearch', 'AMap.AutoComplete'] // 需要使用的的插件列表
})
.then(AMap => {
AMapIns = AMap
mapIns = new AMap.Map(vChartRef.value, {
resizeEnable: true,
zoom: amapZindex.value, // 地图显示的缩放级别
center: [amapLon.value, amapLat.value],
mapStyle: `amap://styles/${amapStyleKeyCustom.value !== '' ? amapStyleKeyCustom.value : amapStyleKey.value}`, //自定义地图的显示样式
lang: lang.value,
features: features.value,
pitch: pitch.value, // 地图俯仰角度,有效范围 0 度- 83 度
skyColor: skyColor.value,
viewMode: viewMode.value, // 地图模式
willReadFrequently: true
})
dataHandle(props.chartConfig.option.dataset)
})
.catch(e => {})
}
const dataHandle = (newData: any) => {
if (!mapIns && !AMapIns) {
initMap(props.chartConfig.option)
return
}
if (isArray(newData.markers)) {
// 先清除旧标记
mapIns.remove(markers)
markers = []
// 记录新标记
if (mapMarkerType.value === MarkerEnum.MARKER) {
newData.markers.forEach((markerItem: any) => {
const markerInstance = new AMapIns.Marker({
position: [markerItem.position[0], markerItem.position[1]],
offset: new AMapIns.Pixel(-13, -30)
})
markers.push(markerInstance)
markerInstance.setMap(mapIns)
})
} else if (mapMarkerType.value === MarkerEnum.CIRCLE_MARKER) {
newData.markers.forEach((markerItem: any) => {
const markerInstance = new AMapIns.CircleMarker({
center: [markerItem.position[0], markerItem.position[1]],
radius: markerItem.value,
...marker.value
})
markers.push(markerInstance)
markerInstance.setMap(mapIns)
})
}
}
}
const stopWatch = watch(
() => props.chartConfig.option.mapOptions,
option => {
initMap(option)
},
{
immediate: true,
deep: true
}
)
watch(
() => props.chartConfig.option.dataset,
newData => {
try {
dataHandle(newData)
} catch (error) {
console.log(error)
}
},
{
deep: false
}
)
// 预览
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
stopWatch()
dataHandle(newData)
})
</script>

View File

@@ -64,9 +64,9 @@ registerMap(props.chartConfig.option.mapRegion.adcode, { geoJSON: {} as any, spe
// 进行更换初始化地图 如果为china 单独处理
const registerMapInitAsync = async () => {
await nextTick()
if (props.chartConfig.option.mapRegion.adcode!="china") {
if (props.chartConfig.option.mapRegion.adcode != 'china') {
await getGeojson(props.chartConfig.option.mapRegion.adcode)
}else{
} else {
await hainanLandsHandle(props.chartConfig.option.mapRegion.showHainanIsLands)
}
vEchartsSetOption()
@@ -90,12 +90,12 @@ const dataSetHandle = async (dataset: any) => {
isPreview() && vEchartsSetOption()
}
// 处理海南群岛
const hainanLandsHandle=async(newData:boolean)=>{
const hainanLandsHandle = async (newData: boolean) => {
if (newData) {
await getGeojson('china')
} else {
registerMap('china', { geoJSON: mapJsonWithoutHainanIsLands as any, specialAreas: {} })
}
await getGeojson('china')
} else {
registerMap('china', { geoJSON: mapJsonWithoutHainanIsLands as any, specialAreas: {} })
}
}
//监听 dataset 数据发生变化
watch(
@@ -113,8 +113,12 @@ watch(
watch(
() => props.chartConfig.option.mapRegion.showHainanIsLands,
async newData => {
await hainanLandsHandle(newData)
vEchartsSetOption()
try {
await hainanLandsHandle(newData)
vEchartsSetOption()
} catch (error) {
console.log(error)
}
},
{
deep: false
@@ -125,12 +129,16 @@ watch(
watch(
() => props.chartConfig.option.mapRegion.adcode,
async newData => {
await getGeojson(newData)
props.chartConfig.option.geo.map = newData
props.chartConfig.option.series.forEach((item: any) => {
if (item.type === 'map') item.map = newData
})
vEchartsSetOption()
try {
await getGeojson(newData)
props.chartConfig.option.geo.map = newData
props.chartConfig.option.series.forEach((item: any) => {
if (item.type === 'map') item.map = newData
})
vEchartsSetOption()
} catch (error) {
console.log(error)
}
},
{
deep: false

View File

@@ -1,3 +1,4 @@
import { MapBaseConfig } from './MapBase/index'
import { MapAmapConfig } from './MapAmap/index'
export default [ MapBaseConfig ]
export default [MapBaseConfig, MapAmapConfig]

View File

@@ -4,7 +4,7 @@ import { CreateComponentType } from '@/packages/index.d'
import { cloneDeep } from 'lodash'
import dataJson from './data.json'
export const includes = ['xAxis', 'yAxis']
export const includes = ['xAxis', 'yAxis', 'grid']
export const option = {
dataset: { ...dataJson },

View File

@@ -75,7 +75,11 @@ const dataSetHandle = (dataset: typeof dataJson) => {
watch(
() => props.chartConfig.option.dataset,
newData => {
dataSetHandle(newData)
try {
dataSetHandle(newData)
} catch (error) {
console.log(error)
}
},
{
deep: false

View File

@@ -57,7 +57,11 @@ const option = shallowReactive({
watch(
() => props.chartConfig.option.dataset,
(newData: any) => {
option.dataset = toNumber(newData, 2)
try {
option.dataset = toNumber(newData, 2)
} catch (error) {
console.log(error)
}
},
{
deep: false

View File

@@ -57,7 +57,11 @@ const dataSetHandle = (dataset: typeof dataJson) => {
watch(
() => props.chartConfig.option.dataset,
newData => {
dataSetHandle(newData)
try {
dataSetHandle(newData)
} catch (error) {
console.log(error)
}
},
{
deep: false

View File

@@ -49,8 +49,12 @@ const dataSetHandle = (dataset: typeof dataJson) => {
watch(
() => props.chartConfig.option.dataset,
newData => {
if(!isArray(newData)) return
dataSetHandle(newData)
try {
if (!isArray(newData)) return
dataSetHandle(newData)
} catch (error) {
console.log(error)
}
},
{
deep: false

View File

@@ -42,23 +42,27 @@ const option = reactive({
watch(
() => chartEditStore.getEditCanvasConfig.chartThemeColor,
(newColor: keyof typeof chartColorsSearch) => {
if (!isPreview()) {
const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
// 背景颜色
props.chartConfig.option.series[0].backgroundStyle.color = themeColor[2]
// 水球颜色
props.chartConfig.option.series[0].color[0].colorStops = [
{
offset: 0,
color: themeColor[0]
},
{
offset: 1,
color: themeColor[1]
}
]
try {
if (!isPreview()) {
const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
// 背景颜色
props.chartConfig.option.series[0].backgroundStyle.color = themeColor[2]
// 水球颜色
props.chartConfig.option.series[0].color[0].colorStops = [
{
offset: 0,
color: themeColor[0]
},
{
offset: 1,
color: themeColor[1]
}
]
}
option.value = props.chartConfig.option
} catch (error) {
console.log(error)
}
option.value = props.chartConfig.option
},
{
immediate: true
@@ -75,7 +79,7 @@ const dataHandle = (newData: number | string) => {
watch(
() => props.chartConfig.option.dataset,
newData => {
if(!isString(newData) && !isNumber(newData)) return
if (!isString(newData) && !isNumber(newData)) return
props.chartConfig.option.series[0].data = [dataHandle(newData)]
option.value = props.chartConfig.option
},

View File

@@ -48,7 +48,13 @@ const dataHandle = (newData: any) => {
// 配置时
watch(
() => props.chartConfig.option.dataset,
newData => dataHandle(newData),
newData => {
try {
dataHandle(newData)
} catch (error) {
console.log(error)
}
},
{
immediate: true,
deep: false

View File

@@ -39,15 +39,19 @@ const option = computed(() => {
watch(
() => props.chartConfig.option.type,
newData => {
if (newData === 'nomal') {
props.chartConfig.option.series[0].radius = '70%'
props.chartConfig.option.series[0].roseType = false
} else if (newData === 'ring') {
props.chartConfig.option.series[0].radius = ['40%', '65%']
props.chartConfig.option.series[0].roseType = false
} else {
props.chartConfig.option.series[0].radius = '70%'
props.chartConfig.option.series[0].roseType = true
try {
if (newData === 'nomal') {
props.chartConfig.option.series[0].radius = '70%'
props.chartConfig.option.series[0].roseType = false
} else if (newData === 'ring') {
props.chartConfig.option.series[0].radius = ['40%', '65%']
props.chartConfig.option.series[0].roseType = false
} else {
props.chartConfig.option.series[0].radius = '70%'
props.chartConfig.option.series[0].roseType = true
}
} catch (error) {
console.log(error)
}
},
{ deep: false, immediate: true }

View File

@@ -4,7 +4,7 @@ import { CreateComponentType } from '@/packages/index.d'
import cloneDeep from 'lodash/cloneDeep'
import dataJson from './data.json'
export const includes = ['legend', 'xAxis', 'yAxis']
export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
export const seriesItem = {
type: 'scatter',

View File

@@ -69,17 +69,21 @@ const option = computed(() => {
watch(
() => props.chartConfig.option.dataset,
(newData, oldData) => {
if (!isArray(newData)) return
if (newData?.length !== oldData?.length) {
replaceMergeArr.value = ['series']
// eslint-disable-next-line vue/no-mutating-props
props.chartConfig.option.series = newData.map((item: { dimensions: any[] }, index: number) => ({
...seriesItem,
datasetIndex: index
}))
nextTick(() => {
replaceMergeArr.value = []
})
try {
if (!isArray(newData)) return
if (Array.isArray(newData)) {
replaceMergeArr.value = ['series']
// eslint-disable-next-line vue/no-mutating-props
props.chartConfig.option.series = newData.map((item: { dimensions: any[] }, index: number) => ({
...seriesItem,
datasetIndex: index
}))
nextTick(() => {
replaceMergeArr.value = []
})
}
} catch (error) {
console.log(error)
}
},
{

View File

@@ -4,7 +4,7 @@ import { CreateComponentType } from '@/packages/index.d'
import cloneDeep from 'lodash/cloneDeep'
import dataJson from './data.json'
export const includes = ['legend', 'xAxis', 'yAxis']
export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
export const option = {
dataset: dataJson,

View File

@@ -3,7 +3,7 @@ import { CreateComponentType } from '@/packages/index.d'
import { CountDownConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
import { chartInitConfig } from '@/settings/designSetting'
import { FlipType } from '@/components/Flipper'
import { FlipType } from '@/components/Pages/Flipper'
type STYLE = '时分秒' | '冒号'

View File

@@ -76,7 +76,7 @@
<script setup lang="ts">
import { PropType, toRefs, watch, ref, onMounted } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { Flipper } from '@/components/Flipper'
import { Flipper } from '@/components/Pages/Flipper'
import { OptionType } from './config'
import { CountdownInst, CountdownProps } from 'naive-ui/es/countdown/src/Countdown'
@@ -133,10 +133,14 @@ const renderCountdown: CountdownProps['render'] = ({ hours, minutes, seconds })
}
const updateTotalDuration = () => {
countdownActive.value = false
totalDuration.value = useEndDate.value ? endDate.value - new Date().getTime() : dataset.value * 1000
countdownRef.value?.reset && countdownRef.value?.reset()
countdownActive.value = true
try {
countdownActive.value = false
totalDuration.value = useEndDate.value ? endDate.value - new Date().getTime() : dataset.value * 1000
countdownRef.value?.reset && countdownRef.value?.reset()
countdownActive.value = true
} catch (error) {
console.log(error)
}
}
watch(

View File

@@ -3,7 +3,7 @@ import { CreateComponentType } from '@/packages/index.d'
import { FlipperNumberConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
import { chartInitConfig } from '@/settings/designSetting'
import { FlipType } from '@/components/Flipper'
import { FlipType } from '@/components/Pages/Flipper'
export interface OptionType {
dataset: number | string

View File

@@ -21,7 +21,7 @@ import { PropType, toRefs, watch, ref } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { useChartDataFetch } from '@/hooks'
import { Flipper } from '@/components/Flipper'
import { Flipper } from '@/components/Pages/Flipper'
import { OptionType } from './config'
const props = defineProps({
@@ -60,7 +60,11 @@ const updateDatasetHandler = (newVal: string | number) => {
watch(
() => props.chartConfig.option,
newVal => {
updateDatasetHandler((newVal as OptionType).dataset)
try {
updateDatasetHandler((newVal as OptionType).dataset)
} catch (error) {
console.log(error)
}
},
{
immediate: true,

View File

@@ -11,23 +11,23 @@
</template>
<script setup lang="ts">
import { PropType, toRefs, ref, reactive, watch, onMounted, onUnmounted } from "vue";
import { CreateComponentType } from "@/packages/index.d";
import { useChartEditStore } from "@/store/modules/chartEditStore/chartEditStore";
import { useChartDataFetch } from "@/hooks";
import { PropType, toRefs, ref, reactive, watch, onMounted, onUnmounted } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { useChartDataFetch } from '@/hooks'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true,
},
});
let yearMonthDay = ref("2021-2-3");
let nowData = ref("08:00:00");
let newData = ref("2021-2-3 08:00:00");
let boxShadow = ref("none");
required: true
}
})
let yearMonthDay = ref('2021-2-3')
let nowData = ref('08:00:00')
let newData = ref('2021-2-3 08:00:00')
let boxShadow = ref('none')
const { w, h } = toRefs(props.chartConfig.attr);
const { w, h } = toRefs(props.chartConfig.attr)
let {
timeColor,
@@ -39,54 +39,55 @@ let {
hShadow,
vShadow,
blurShadow,
colorShadow,
} = toRefs(props.chartConfig.option);
colorShadow
} = toRefs(props.chartConfig.option)
watch(
props.chartConfig.option,
() => {
if (props.chartConfig.option.showShadow) {
boxShadow.value = `${props.chartConfig.option.hShadow}px ${props.chartConfig.option.vShadow}px ${props.chartConfig.option.blurShadow}px ${props.chartConfig.option.colorShadow}`;
} else {
boxShadow.value = "none";
try {
if (props.chartConfig.option.showShadow) {
boxShadow.value = `${props.chartConfig.option.hShadow}px ${props.chartConfig.option.vShadow}px ${props.chartConfig.option.blurShadow}px ${props.chartConfig.option.colorShadow}`
} else {
boxShadow.value = 'none'
}
} catch (error) {
console.log(error)
}
},
{
immediate: true,
immediate: true
}
);
)
onMounted(() => {
const timer = setInterval(() => {
var datetime = new Date();
var year = datetime.getFullYear();
var month =
datetime.getMonth() + 1 < 10
? "0" + (datetime.getMonth() + 1)
: datetime.getMonth() + 1;
var date = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
var hh = datetime.getHours(); // 时
var mm = datetime.getMinutes(); // 分
var ss = datetime.getSeconds(); // 分
let time = "";
if (hh < 10) time += "0";
time += hh + ":";
if (mm < 10) time += "0";
time += mm + ":";
if (ss < 10) time += "0";
time += ss;
yearMonthDay.value = `${year}-${month}-${date}`;
nowData.value = time;
newData.value = yearMonthDay.value + " " + nowData.value;
}, 500);
});
var datetime = new Date()
var year = datetime.getFullYear()
var month = datetime.getMonth() + 1 < 10 ? '0' + (datetime.getMonth() + 1) : datetime.getMonth() + 1
var date = datetime.getDate() < 10 ? '0' + datetime.getDate() : datetime.getDate()
var hh = datetime.getHours() // 时
var mm = datetime.getMinutes() // 分
var ss = datetime.getSeconds() // 分
let time = ''
if (hh < 10) time += '0'
time += hh + ':'
if (mm < 10) time += '0'
time += mm + ':'
if (ss < 10) time += '0'
time += ss
yearMonthDay.value = `${year}-${month}-${date}`
nowData.value = time
newData.value = yearMonthDay.value + ' ' + nowData.value
}, 500)
})
onUnmounted(() => {
clearInterval();
});
useChartDataFetch(props.chartConfig, useChartEditStore);
clearInterval()
})
useChartDataFetch(props.chartConfig, useChartEditStore)
</script>
<style lang="scss" scoped>
@include go("decorates-number") {
@include go('decorates-number') {
text-align: center;
}
</style>

View File

@@ -0,0 +1,236 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import {
ArcCurve,
BufferAttribute,
BufferGeometry,
Color,
Line,
LineBasicMaterial,
Points,
PointsMaterial,
Quaternion,
Vector3
} from 'three'
import { lon2xyz } from './common'
/*
* 绘制一条圆弧飞线
* 5个参数含义( 飞线圆弧轨迹半径, 开始角度, 结束角度)
*/
function createFlyLine(radius, startAngle, endAngle, color) {
const geometry = new BufferGeometry() //声明一个几何体对象BufferGeometry
// ArcCurve创建圆弧曲线
const arc = new ArcCurve(0, 0, radius, startAngle, endAngle, false)
//getSpacedPoints是基类Curve的方法返回一个vector2对象作为元素组成的数组
const pointsArr = arc.getSpacedPoints(100) //分段数80返回81个顶点
geometry.setFromPoints(pointsArr) // setFromPoints方法从pointsArr中提取数据改变几何体的顶点属性vertices
// 每个顶点对应一个百分比数据attributes.percent 用于控制点的渲染大小
const percentArr = [] //attributes.percent的数据
for (let i = 0; i < pointsArr.length; i++) {
percentArr.push(i / pointsArr.length)
}
const percentAttribue = new BufferAttribute(new Float32Array(percentArr), 1)
// 通过顶点数据percent点模型从大到小变化产生小蝌蚪形状飞线
geometry.attributes.percent = percentAttribue
// 批量计算所有顶点颜色数据
const colorArr = []
for (let i = 0; i < pointsArr.length; i++) {
const color1 = new Color(0xec8f43) //轨迹线颜色 青色
const color2 = new Color(0xf3ae76) //黄色
const color = color1.lerp(color2, i / pointsArr.length)
colorArr.push(color.r, color.g, color.b)
}
// 设置几何体顶点颜色数据
geometry.attributes.color = new BufferAttribute(new Float32Array(colorArr), 3)
const size = 1.3
// 点模型渲染几何体每个顶点
const material = new PointsMaterial({
size, //点大小
// vertexColors: VertexColors, //使用顶点颜色渲染
transparent: true,
depthWrite: false
})
// 修改点材质的着色器源码(注意:不同版本细节可能会稍微会有区别,不过整体思路是一样的)
material.onBeforeCompile = function (shader) {
// 顶点着色器中声明一个attribute变量:百分比
shader.vertexShader = shader.vertexShader.replace(
'void main() {',
[
'attribute float percent;', //顶点大小百分比变量,控制点渲染大小
'void main() {'
].join('\n') // .join()把数组元素合成字符串
)
// 调整点渲染大小计算方式
shader.vertexShader = shader.vertexShader.replace(
'gl_PointSize = size;',
['gl_PointSize = percent * size;'].join('\n') // .join()把数组元素合成字符串
)
}
const FlyLine = new Points(geometry, material)
material.color = new Color(color)
FlyLine.name = '飞行线'
return FlyLine
}
/**输入地球上任意两点的经纬度坐标通过函数flyArc可以绘制一个飞线圆弧轨迹
* lon1,lat1:轨迹线起点经纬度坐标
* lon2,lat2轨迹线结束点经纬度坐标
*/
function flyArc(radius, lon1, lat1, lon2, lat2, options) {
const sphereCoord1 = lon2xyz(radius, lon1, lat1) //经纬度坐标转球面坐标
// startSphereCoord轨迹线起点球面坐标
const startSphereCoord = new Vector3(sphereCoord1.x, sphereCoord1.y, sphereCoord1.z)
const sphereCoord2 = lon2xyz(radius, lon2, lat2)
// startSphereCoord轨迹线结束点球面坐标
const endSphereCoord = new Vector3(sphereCoord2.x, sphereCoord2.y, sphereCoord2.z)
//计算绘制圆弧需要的关于y轴对称的起点、结束点和旋转四元数
const startEndQua = _3Dto2D(startSphereCoord, endSphereCoord)
// 调用arcXOY函数绘制一条圆弧飞线轨迹
const arcline = arcXOY(radius, startEndQua.startPoint, startEndQua.endPoint, options)
arcline.quaternion.multiply(startEndQua.quaternion)
return arcline
}
/*
* 把3D球面上任意的两个飞线起点和结束点绕球心旋转到到XOY平面上
* 同时保持关于y轴对称借助旋转得到的新起点和新结束点绘制
* 一个圆弧,最后把绘制的圆弧反向旋转到原来的起点和结束点即可
*/
function _3Dto2D(startSphere, endSphere) {
/*计算第一次旋转的四元数:表示从一个平面如何旋转到另一个平面*/
const origin = new Vector3(0, 0, 0) //球心坐标
const startDir = startSphere.clone().sub(origin) //飞线起点与球心构成方向向量
const endDir = endSphere.clone().sub(origin) //飞线结束点与球心构成方向向量
// dir1和dir2构成一个三角形.cross()叉乘计算该三角形法线normal
const normal = startDir.clone().cross(endDir).normalize()
const xoyNormal = new Vector3(0, 0, 1) //XOY平面的法线
//.setFromUnitVectors()计算从normal向量旋转达到xoyNormal向量所需要的四元数
// quaternion表示把球面飞线旋转到XOY平面上需要的四元数
const quaternion3D_XOY = new Quaternion().setFromUnitVectors(normal, xoyNormal)
/*第一次旋转飞线起点、结束点从3D空间第一次旋转到XOY平面*/
const startSphereXOY = startSphere.clone().applyQuaternion(quaternion3D_XOY)
const endSphereXOY = endSphere.clone().applyQuaternion(quaternion3D_XOY)
/*计算第二次旋转的四元数*/
// middleV3startSphereXOY和endSphereXOY的中点
const middleV3 = startSphereXOY.clone().add(endSphereXOY).multiplyScalar(0.5)
const midDir = middleV3.clone().sub(origin).normalize() // 旋转前向量midDir中点middleV3和球心构成的方向向量
const yDir = new Vector3(0, 1, 0) // 旋转后向量yDir即y轴
// .setFromUnitVectors()计算从midDir向量旋转达到yDir向量所需要的四元数
// quaternion2表示让第一次旋转到XOY平面的起点和结束点关于y轴对称需要的四元数
const quaternionXOY_Y = new Quaternion().setFromUnitVectors(midDir, yDir)
/*第二次旋转使旋转到XOY平面的点再次旋转实现关于Y轴对称*/
const startSpherXOY_Y = startSphereXOY.clone().applyQuaternion(quaternionXOY_Y)
const endSphereXOY_Y = endSphereXOY.clone().applyQuaternion(quaternionXOY_Y)
/**一个四元数表示一个旋转过程
*.invert()方法表示四元数的逆,简单说就是把旋转过程倒过来
* 两次旋转的四元数执行.invert()求逆,然后执行.multiply()相乘
*新版本.invert()对应旧版本.invert()
*/
const quaternionInverse = quaternion3D_XOY.clone().invert().multiply(quaternionXOY_Y.clone().invert())
return {
// 返回两次旋转四元数的逆四元数
quaternion: quaternionInverse,
// 范围两次旋转后在XOY平面上关于y轴对称的圆弧起点和结束点坐标
startPoint: startSpherXOY_Y,
endPoint: endSphereXOY_Y
}
}
/**通过函数arcXOY()可以在XOY平面上绘制一个关于y轴对称的圆弧曲线
* startPoint, endPoint表示圆弧曲线的起点和结束点坐标值起点和结束点关于y轴对称
* 同时在圆弧轨迹的基础上绘制一段飞线*/
function arcXOY(radius, startPoint, endPoint, options) {
// 计算两点的中点
const middleV3 = new Vector3().addVectors(startPoint, endPoint).multiplyScalar(0.5)
// 弦垂线的方向dir(弦的中点和圆心构成的向量)
const dir = middleV3.clone().normalize()
// 计算球面飞线的起点、结束点和球心构成夹角的弧度值
const earthRadianAngle = radianAOB(startPoint, endPoint, new Vector3(0, 0, 0))
/*设置飞线轨迹圆弧的中间点坐标
弧度值 * radius * 0.2:表示飞线轨迹圆弧顶部距离地球球面的距离
起点、结束点相聚越远,构成的弧线顶部距离球面越高*/
const arcTopCoord = dir.multiplyScalar(radius + earthRadianAngle * radius * 0.2) // 黄色飞行线的高度
//求三个点的外接圆圆心(飞线圆弧轨迹的圆心坐标)
const flyArcCenter = threePointCenter(startPoint, endPoint, arcTopCoord)
// 飞线圆弧轨迹半径flyArcR
const flyArcR = Math.abs(flyArcCenter.y - arcTopCoord.y)
/*坐标原点和飞线起点构成直线和y轴负半轴夹角弧度值
参数分别是飞线圆弧起点、y轴负半轴上一点、飞线圆弧圆心*/
const flyRadianAngle = radianAOB(startPoint, new Vector3(0, -1, 0), flyArcCenter)
const startAngle = -Math.PI / 2 + flyRadianAngle //飞线圆弧开始角度
const endAngle = Math.PI - startAngle //飞线圆弧结束角度
// 调用圆弧线模型的绘制函数
const arcline = circleLine(flyArcCenter.x, flyArcCenter.y, flyArcR, startAngle, endAngle, options.color)
// const arcline = new Group();// 不绘制轨迹线,使用 Group替换circleLine()即可
arcline.center = flyArcCenter //飞线圆弧自定一个属性表示飞线圆弧的圆心
arcline.topCoord = arcTopCoord //飞线圆弧自定一个属性表示飞线圆弧中间也就是顶部坐标
// const flyAngle = Math.PI/ 10; //飞线圆弧固定弧度
const flyAngle = (endAngle - startAngle) / 7 //飞线圆弧的弧度和轨迹线弧度相关
// 绘制一段飞线,圆心做坐标原点
const flyLine = createFlyLine(flyArcR, startAngle, startAngle + flyAngle, options.flyLineColor)
flyLine.position.y = flyArcCenter.y //平移飞线圆弧和飞线轨迹圆弧重合
//飞线段flyLine作为飞线轨迹arcLine子对象继承飞线轨迹平移旋转等变换
arcline.add(flyLine)
//飞线段运动范围startAngle~flyEndAngle
flyLine.flyEndAngle = endAngle - startAngle - flyAngle
flyLine.startAngle = startAngle
// arcline.flyEndAngle飞线段当前角度位置这里设置了一个随机值用于演示
flyLine.AngleZ = arcline.flyEndAngle * Math.random()
// flyLine.rotation.z = arcline.AngleZ;
// arcline.flyLine指向飞线段,便于设置动画是访问飞线段
arcline.userData['flyLine'] = flyLine
return arcline
}
/*计算球面上两点和球心构成夹角的弧度值
参数point1, point2:表示地球球面上两点坐标Vector3
计算A、B两点和顶点O构成的AOB夹角弧度值*/
function radianAOB(A, B, O) {
// dir1、dir2球面上两个点和球心构成的方向向量
const dir1 = A.clone().sub(O).normalize()
const dir2 = B.clone().sub(O).normalize()
//点乘.dot()计算夹角余弦值
const cosAngle = dir1.clone().dot(dir2)
const radianAngle = Math.acos(cosAngle) //余弦值转夹角弧度值,通过余弦值可以计算夹角范围是0~180度
return radianAngle
}
/*绘制一条圆弧曲线模型Line
5个参数含义(圆心横坐标, 圆心纵坐标, 飞线圆弧轨迹半径, 开始角度, 结束角度)*/
function circleLine(x, y, r, startAngle, endAngle, color) {
const geometry = new BufferGeometry() //声明一个几何体对象Geometry
// ArcCurve创建圆弧曲线
const arc = new ArcCurve(x, y, r, startAngle, endAngle, false)
//getSpacedPoints是基类Curve的方法返回一个vector2对象作为元素组成的数组
const points = arc.getSpacedPoints(80) //分段数50返回51个顶点
geometry.setFromPoints(points) // setFromPoints方法从points中提取数据改变几何体的顶点属性vertices
const material = new LineBasicMaterial({
color: color || 0xd18547
}) //线条材质
const line = new Line(geometry, material) //线条模型对象
return line
}
//求三个点的外接圆圆心p1, p2, p3表示三个点的坐标Vector3。
function threePointCenter(p1, p2, p3) {
const L1 = p1.lengthSq() //p1到坐标原点距离的平方
const L2 = p2.lengthSq()
const L3 = p3.lengthSq()
const x1 = p1.x,
y1 = p1.y,
x2 = p2.x,
y2 = p2.y,
x3 = p3.x,
y3 = p3.y
const S = x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2
const x = (L2 * y3 + L1 * y2 + L3 * y1 - L2 * y1 - L3 * y2 - L1 * y3) / S / 2
const y = (L3 * x2 + L2 * x1 + L1 * x3 - L1 * x2 - L2 * x3 - L3 * x1) / S / 2
// 三点外接圆圆心坐标
const center = new Vector3(x, y, 0)
return center
}
export { arcXOY, flyArc }

View File

@@ -0,0 +1,137 @@
import {
CatmullRomCurve3,
DoubleSide,
Group,
Mesh,
MeshBasicMaterial,
PlaneGeometry,
Texture,
TubeGeometry,
Vector3
} from 'three'
import { punctuation } from '../world/Earth'
/**
* 经纬度坐标转球面坐标
* @param {地球半径} R
* @param {经度(角度值)} longitude
* @param {维度(角度值)} latitude
*/
export const lon2xyz = (R: number, longitude: number, latitude: number): Vector3 => {
let lon = (longitude * Math.PI) / 180 // 转弧度值
const lat = (latitude * Math.PI) / 180 // 转弧度值
lon = -lon // js坐标系z坐标轴对应经度-90度而不是90度
// 经纬度坐标转球面坐标计算公式
const x = R * Math.cos(lat) * Math.cos(lon)
const y = R * Math.sin(lat)
const z = R * Math.cos(lat) * Math.sin(lon)
// 返回球面坐标
return new Vector3(x, y, z)
}
// 创建波动光圈
export const createWaveMesh = (options: { radius: number; lon: number; lat: number; textures: any }) => {
const geometry = new PlaneGeometry(1, 1) //默认在XOY平面上
const texture = options.textures.aperture
const material = new MeshBasicMaterial({
color: 0xe99f68,
map: texture,
transparent: true, //使用背景透明的png贴图注意开启透明计算
opacity: 1.0,
depthWrite: false //禁止写入深度缓冲区数据
})
const mesh = new Mesh(geometry, material)
// 经纬度转球面坐标
const coord = lon2xyz(options.radius * 1.001, options.lon, options.lat)
const size = options.radius * 0.12 //矩形平面Mesh的尺寸
mesh.scale.set(size, size, size) //设置mesh大小
mesh.userData['size'] = size //自顶一个属性表示mesh静态大小
mesh.userData['scale'] = Math.random() * 1.0 //自定义属性._s表示mesh在原始大小基础上放大倍数 光圈在原来mesh.size基础上1~2倍之间变化
mesh.position.set(coord.x, coord.y, coord.z)
const coordVec3 = new Vector3(coord.x, coord.y, coord.z).normalize()
const meshNormal = new Vector3(0, 0, 1)
mesh.quaternion.setFromUnitVectors(meshNormal, coordVec3)
return mesh
}
// 创建柱状
export const createLightPillar = (options: {
radius: number
lon: number
lat: number
index: number
textures: Record<string, Texture>
punctuation: punctuation
}) => {
const height = options.radius * 0.3
const geometry = new PlaneGeometry(options.radius * 0.05, height)
geometry.rotateX(Math.PI / 2)
geometry.translate(0, 0, height / 2)
const material = new MeshBasicMaterial({
map: options.textures.light_column,
color: options.index == 0 ? options.punctuation.lightColumn.startColor : options.punctuation.lightColumn.endColor,
transparent: true,
side: DoubleSide,
depthWrite: false //是否对深度缓冲区有任何的影响
})
const mesh = new Mesh(geometry, material)
const group = new Group()
// 两个光柱交叉叠加
group.add(mesh, mesh.clone().rotateZ(Math.PI / 2)) //几何体绕x轴旋转了所以mesh旋转轴变为z
// 经纬度转球面坐标
const SphereCoord = lon2xyz(options.radius, options.lon, options.lat) //SphereCoord球面坐标
group.position.set(SphereCoord.x, SphereCoord.y, SphereCoord.z) //设置mesh位置
const coordVec3 = new Vector3(SphereCoord.x, SphereCoord.y, SphereCoord.z).normalize()
const meshNormal = new Vector3(0, 0, 1)
group.quaternion.setFromUnitVectors(meshNormal, coordVec3)
return group
}
// 光柱底座矩形平面
export const createPointMesh = (options: { radius: number; lon: number; lat: number; material: MeshBasicMaterial }) => {
const geometry = new PlaneGeometry(1, 1) //默认在XOY平面上
const mesh = new Mesh(geometry, options.material)
// 经纬度转球面坐标
const coord = lon2xyz(options.radius * 1.001, options.lon, options.lat)
const size = options.radius * 0.05 // 矩形平面Mesh的尺寸
mesh.scale.set(size, size, size) // 设置mesh大小
// 设置mesh位置
mesh.position.set(coord.x, coord.y, coord.z)
const coordVec3 = new Vector3(coord.x, coord.y, coord.z).normalize()
const meshNormal = new Vector3(0, 0, 1)
mesh.quaternion.setFromUnitVectors(meshNormal, coordVec3)
return mesh
}
// 获取点
export const getCirclePoints = (option: any) => {
const list = []
for (let j = 0; j < 2 * Math.PI - 0.1; j += (2 * Math.PI) / (option.number || 100)) {
list.push([
parseFloat((Math.cos(j) * (option.radius || 10)).toFixed(2)),
0,
parseFloat((Math.sin(j) * (option.radius || 10)).toFixed(2))
])
}
if (option.closed) list.push(list[0])
return list
}
// 创建线
/**
* 创建动态的线
*/
export const createAnimateLine = (option: any) => {
// 由多个点数组构成的曲线 通常用于道路
const l: Array<any> = []
option.pointList.forEach((e: Array<any>) => l.push(new Vector3(e[0], e[1], e[2])))
const curve = new CatmullRomCurve3(l) // 曲线路径
// 管道体
const tubeGeometry = new TubeGeometry(curve, option.number || 50, option.radius || 1, option.radialSegments)
return new Mesh(tubeGeometry, option.material)
}

View File

@@ -0,0 +1,4 @@
export interface IEvents {
resize: () => void
}

View File

@@ -0,0 +1,6 @@
export interface IWord {
dom: HTMLElement
data: any
width: number
height: number
}

View File

@@ -0,0 +1,23 @@
uniform vec3 glowColor;
uniform float bias;
uniform float power;
uniform float time;
varying vec3 vp;
varying vec3 vNormal;
varying vec3 vPositionNormal;
uniform float scale;
// 获取纹理
uniform sampler2D map;
// 纹理坐标
varying vec2 vUv;
void main(void){
float a = pow( bias + scale * abs(dot(vNormal, vPositionNormal)), power );
if(vp.y > time && vp.y < time + 20.0) {
float t = smoothstep(0.0, 0.8, (1.0 - abs(0.5 - (vp.y - time) / 20.0)) / 3.0 );
gl_FragColor = mix(gl_FragColor, vec4(glowColor, 1.0), t * t );
}
gl_FragColor = mix(gl_FragColor, vec4( glowColor, 1.0 ), a);
float b = 0.8;
gl_FragColor = gl_FragColor + texture2D( map, vUv );
}

View File

@@ -0,0 +1,12 @@
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vp;
varying vec3 vPositionNormal;
void main(void){
vUv = uv;
vNormal = normalize( normalMatrix * normal ); // 转换到视图空间
vp = position;
vPositionNormal = normalize(( modelViewMatrix * vec4(position, 1.0) ).xyz);
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

View File

@@ -0,0 +1,34 @@
/**
* 资源文件
* 把模型和图片分开进行加载
*/
interface ITextures {
name: string
url: string
}
export interface IResources {
textures?: ITextures[]
}
const fileSuffix = ['earth', 'gradient', 'redCircle', 'label', 'aperture', 'glow', 'light_column', 'aircraft']
const textures: ITextures[] = []
const modules = import.meta.globEager("../../images/earth/*");
for(let item in modules) {
const n = item.split('/').pop()
if(n) {
textures.push({
name: n.split('.')[0],
url: modules[item].default
})
}
}
const resources: IResources = {
textures
}
export { resources }

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