Compare commits

...

180 Commits

Author SHA1 Message Date
奔跑的面条
9cf023fcc8 Merge branch 'dev' 2023-07-09 15:54:38 +08:00
奔跑的面条
7076c4deb7 feat: 新增预览拖拽 2023-07-09 15:54:01 +08:00
奔跑的面条
28f0585a5c Merge branch 'dev' 2023-07-08 22:00:35 +08:00
奔跑的面条
e979149cfb feat: 不符合接口格式时添加拦截 2023-07-08 21:59:41 +08:00
奔跑的面条
5fc43d1d02 build: 升级版本到1.2.7 2023-07-08 21:52:07 +08:00
奔跑的面条
084441d365 Merge branch 'dev' 2023-07-08 21:48:53 +08:00
奔跑的面条
d0d031d1bb feat: 还原修改成组旋转的问题,优化预览放大的交互 2023-07-08 21:33:50 +08:00
奔跑的面条
9127e6f44c feat: 新增预览放大功能 2023-07-08 21:18:37 +08:00
xlys999
bb610ff7ae update src/views/chart/ContentEdit/components/EditRule/index.vue.
坐标尺刻度错位问题

Signed-off-by: xlys999 <xlys998@163.com>
2023-07-08 18:45:03 +08:00
奔跑的面条
2dc12b1f0b !177 update src/views/chart/ContentEdit/components/EditRule/index.vue.
Merge pull request !177 from xlys999/N/A
2023-07-08 10:42:13 +00:00
奔跑的面条
c575cff727 perf: 优化滤镜预设展示 2023-07-08 18:36:36 +08:00
奔跑的面条
5443c9647b !176 新增预设滤镜功能
Merge pull request !176 from 张江胜/dev_feat
2023-07-08 10:35:31 +00:00
xlys999
ace6b0f0d2 update src/views/chart/ContentEdit/components/EditRule/index.vue.
坐标尺刻度错位问题

Signed-off-by: xlys999 <xlys998@163.com>
2023-07-03 14:10:00 +00:00
张江胜
271a02ff12 feat: 增加预设滤镜功能
1.滤镜变换:新增了快捷预设滤镜
2023-06-29 18:45:59 +08:00
张江胜
f747fd3506 fix: 解决单个组件旋转后,再组合分组渲染预览会错位的问题
1.解决单个组件旋转后,和其他组件组合分组后再次旋转,进行预览后该分组会错位的问题
2.解决TS类型报错
2023-06-29 17:39:17 +08:00
奔跑的面条
b59c28b8ae fix: 解决 TS 类型错误问题 2023-06-27 18:11:37 +08:00
奔跑的面条
b1a0e7b5e2 fix: 修改 axios 类型错误 2023-06-27 17:54:17 +08:00
奔跑的面条
0d762f7d30 style: 优化 pr_173 的问题 2023-06-27 17:48:08 +08:00
奔跑的面条
cc156a0a6b !173 1.时间选择器增加默认值类型选项
Merge pull request !173 from dingxs/dingxs
2023-06-27 09:47:39 +00:00
奔跑的面条
a77f4d6bf4 !174 feat: 为 naive-ui 组件配置感知、属性提示、自动完成功能
Merge pull request !174 from dodu/dev-commet
2023-06-25 01:32:43 +00:00
tnt group
9e5676e5ed feat: 为 naive-ui 组件提供感知能力 2023-06-23 11:54:52 +08:00
dingxs
bc79e2f01f fix:用in判断对象是否具有某个特定的key,而不是value,解决当value为null时永远得不到修改 2023-06-19 14:39:00 +08:00
dingxs
80f06afa02 feat:时间选择器增加默认值类型选项 2023-06-19 14:35:17 +08:00
奔跑的面条
58ab3dee6d !172 环形饼图增加轨道宽度设置,并优化数字显示(如无小数位,则不强制显示小数位)
Merge pull request !172 from dodu/dev-commet
2023-06-15 11:52:47 +00:00
奔跑的面条
d7a3bb7ba0 !169 feat:标签选择器增加默认值选项
Merge pull request !169 from h5coder/dev-h5coder
2023-06-15 19:50:27 +08:00
奔跑的面条
5387a7ad90 !171 fix: 修复旧版本没有legend配置导致报settting color错误
Merge pull request !171 from h5coder/h5coder-fix
2023-06-15 19:46:13 +08:00
tnt group
fe37435f79 chore: 设置选择框size 2023-06-14 07:54:36 +08:00
tnt group
1ef6d9797a feat: 环形饼图增加轨道宽度设置,并优化数字显示 2023-06-13 10:27:08 +08:00
奔跑的面条
c70d54c622 style: 删除冗余代码 2023-06-08 16:39:18 +08:00
Ming
d74645d7b0 feat: 组件交互对公共api支持配置页 2023-06-07 15:13:48 +08:00
Ming
86910de3b2 fix: 数据初始化 2023-06-07 15:08:52 +08:00
Ming
47636ee680 feat: 支持对公共api进行交互 2023-06-07 15:06:51 +08:00
Ming
9485931f5d feat: 更新公共api对组件动态绑定交互 2023-06-07 15:03:59 +08:00
Ming
20fa9e4afe fix: declare module '@iconify/vue' 2023-06-07 15:00:28 +08:00
Ming
cbd629d7f8 feat: 更新交互组件更新方式 2023-06-07 15:00:06 +08:00
Ming
1551aa9ded fix: 语言转换为英文后,创建按钮的英文拼写错误 2023-05-29 10:29:44 +08:00
奔跑的面条
471610963b build: 升级版本到1.2.6 2023-05-28 17:29:19 +08:00
奔跑的面条
f6424245fd Merge branch 'dev' 2023-05-28 17:28:34 +08:00
奔跑的面条
d9834482f3 !166 fix: 调整组件列表下图片的展现方式,保持图片不被变形
Merge pull request !166 from dodu/dev-commet
2023-05-28 09:27:17 +00:00
tnt group
6fe20c4655 fix: 调整组件列表下图片的展现方式,保持图片不被变形 2023-05-28 17:23:59 +08:00
奔跑的面条
6afbe59828 Merge branch 'dev' 2023-05-28 17:20:36 +08:00
奔跑的面条
52389fa6fc !162 ecahrts全局公共图例新增详细配置参数
Merge pull request !162 from tanhao/feat-echarts-legend
2023-05-28 09:09:26 +00:00
奔跑的面条
bcb2ca1cb5 !159 地图组件新增卫星地图主题
Merge pull request !159 from tanhao/feat-map-theme
2023-05-28 16:15:58 +08:00
奔跑的面条
346c8d439a !164 feat: 增加管道组件
Merge pull request !164 from wsc/dev
2023-05-28 07:50:00 +00:00
奔跑的面条
c7f7c6a731 !163 fix: 修复原来动态导入的问题,保留 redirectComponent 配置的灵活性
Merge pull request !163 from dodu/dev-commet
2023-05-27 07:00:44 +00:00
tnt group
6063408d94 fix: 上次遗漏的修复 #e463b2cd 2023-05-27 13:21:17 +08:00
tnt group
ff4140cbfb chore: 使用模板字符串方式表示 redirectComponent 规则,更为精准,避免手误 2023-05-27 12:06:43 +08:00
tnt group
d8bcb4ab4f fix: 修复图标组件持久化(或导出再导入)时保存的分类不正确导致无法载入的问题 2023-05-27 00:02:40 +08:00
Wang sc
3d4e05b514 feat: 增加管道组件 2023-05-26 18:07:35 +08:00
tnt group
bb48745484 fix: 修复动态导入的问题,保留 redirectComponent 配置的灵活性 2023-05-26 16:51:05 +08:00
tanhao
f9adeac742 feat: echarts公共图例新增详细配置信息 2023-05-25 11:52:10 +08:00
奔跑的面条
8867a489a2 fix: 处理编译之后无法生成新数据的bug 2023-05-24 22:49:25 +08:00
奔跑的面条
adfd8c63e2 !159 地图组件新增卫星地图主题
Merge pull request !159 from tanhao/feat-map-theme
2023-05-24 12:58:20 +00:00
奔跑的面条
d8c55b6325 perf: 优化提示文案 2023-05-24 20:53:26 +08:00
奔跑的面条
44ace74ca9 fix: 新增删除功能 2023-05-24 17:59:27 +08:00
奔跑的面条
2e84b3d196 feat: 修改代码结构,新增删除按钮 2023-05-24 16:56:39 +08:00
奔跑的面条
8f82c1fa59 !157 fix:修复[时间选择器无法获取实际所选时间范围]Bug并且设置默认值
Merge pull request !157 from dingxs/dev-fix
2023-05-24 07:54:08 +00:00
奔跑的面条
7098e2e5e7 !158 chore: 图标和图片虚拟组件设置成static静态资源
Merge pull request !158 from dodu/dev-commet
2023-05-24 07:40:14 +00:00
tanhao
11af941590 feat: 在地图组件基础上新增卫星地图主题 2023-05-24 14:44:46 +08:00
tnt group
051ddcc2d4 chore: 图标和图片动态组件设置成static静态资源 2023-05-24 11:49:24 +08:00
奔跑的面条
773ae65085 perf: 处理提示词 2023-05-23 21:13:54 +08:00
奔跑的面条
e4db7cb8ff feat: 新增图片和图标 2023-05-23 20:55:24 +08:00
dingxs
8bb0f5e3a5 fix:修复[时间选择器无法获取实际所选时间范围]Bug并且设置默认值 2023-05-23 11:50:47 +08:00
Ming
93ed31f093 feat: 防止前端编译工具安全警告报错 2023-05-22 11:43:54 +08:00
Ming
76e511daa0 feat: 动态日期,初始化日期 2023-05-22 11:38:38 +08:00
Ming
a2184b19b3 feat: 动态日期config面板 2023-05-22 11:36:43 +08:00
Ming
ec9210d7e7 feat: 动态日期config 2023-05-22 11:34:59 +08:00
奔跑的面条
e0fe8f673c !155 增加图片、图标侧边栏功能
Merge pull request !155 from dodu/dev-commet
2023-05-22 03:06:22 +00:00
tnt group
95466aa076 feat: 添加天气分组图标 2023-05-20 13:53:15 +08:00
tnt group
6e8e38e737 fix: 修复搜索列表不显示图标的问题 2023-05-19 17:19:34 +08:00
tnt group
3008073829 fix: 修复图标无法拖拽的问题 2023-05-19 17:15:24 +08:00
tnt group
0e0794338a fix: 修复合并丢掉的虚拟组件配置,导致photos my 下的虚拟组件异常 2023-05-18 20:20:58 +08:00
tnt group
5059c9ea65 chore: 移除 pnpm-lock,避免冲突 2023-05-18 19:59:25 +08:00
tnt group
50a8342c49 Merge branch 'dev-photos' into dev-commet 2023-05-18 19:56:31 +08:00
tnt group
afad8e49b1 feat: 增加图标边栏选项卡 2023-05-18 19:44:27 +08:00
tnt group
a3d97e56ab feat: 增加两种图标类别及虚拟组件 2023-05-18 19:43:36 +08:00
tnt group
816c11a877 feat: 扩展 ConfigType,以支持虚拟图标组件 2023-05-18 19:41:30 +08:00
tnt group
6fee63600b feat: 创建通用图标组件 2023-05-18 19:39:21 +08:00
tnt group
1dd055294c feat: 添加 iconify 库及图标素材 2023-05-18 19:36:58 +08:00
tnt group
4aa41bbe36 chore: 忽略vite警告 2023-05-18 12:00:31 +08:00
tnt group
9585bd07a3 feat: 扩展 ConfigType 添加 clickHandle 属性,为上传图片组件增加点击事件,完善点击上传(暂时不走网络,读取其base64) 2023-05-18 09:46:18 +08:00
tnt group
aecba9c95e feat: 扩展 ConfigType,添加禁用属性,并添加一个上传按钮组件,在拖拽、双击、搜索模式让配置了禁用项的组件不可用 2023-05-18 09:45:41 +08:00
奔跑的面条
57ad9a2c7a build: 升级依赖 2023-05-18 09:21:54 +08:00
奔跑的面条
e71fb22b22 build: 升级依赖 2023-05-18 09:19:02 +08:00
tnt group
d177ebc900 feat: 侧边栏中加入图片类型选项卡 2023-05-17 20:30:50 +08:00
tnt group
e18cf847cd feat: 组件 Image 支持延迟加载 2023-05-17 20:23:55 +08:00
tnt group
31ed863d53 feat: 在通过拖拽、双击、搜索不同方式动态创建组件时,支持创建虚拟组件,并根据预设值和预设标题,初始化组件 2023-05-17 20:23:05 +08:00
tnt group
c7b2daa0b8 feat: 创建Photos虚拟组件(设置 virtualComponent 组件路径),预设我的|共享类别 2023-05-17 20:16:36 +08:00
tnt group
3a6990f124 feat: 扩展 ConfigType 组件配置类型,支持虚拟组件路径和预设组件 dataset 值 2023-05-17 20:12:48 +08:00
tnt group
9f41ad1811 feat: 列表组件的image渲染支持url模式 2023-05-17 20:09:03 +08:00
奔跑的面条
0ec0a12b0f feat: 升级 vite 版本到4.3.6 2023-05-16 19:48:23 +08:00
奔跑的面条
c18bc019d3 feat: 新增饼图轮播功能 2023-05-16 19:48:06 +08:00
奔跑的面条
6bbe489162 !154 修复数字翻牌因未等待ui更新导致显示错误的异常,并能实时响应更新
Merge pull request !154 from dodu/dev-commet
2023-05-14 09:09:48 +00:00
tnt group
54cdada560 style: 加重翻牌动画效果 2023-05-14 13:03:25 +08:00
tnt group
048f3426fe fix: 修复因变化快无法显示反动动画效果 2023-05-14 12:34:21 +08:00
tnt group
c508aaa400 feat: 翻牌数字增加内阴影边框属性及配置,默认为 0 2023-05-14 12:12:59 +08:00
tnt group
606cb753c4 fix: 修复因翻牌值变化太快,导致显示错误的异常 2023-05-14 12:09:38 +08:00
奔跑的面条
66e376a87d docs: update 2023-05-13 14:21:49 +08:00
奔跑的面条
318821680a docs: update 2023-05-13 14:19:52 +08:00
奔跑的面条
114fe5626a update README.md.
Signed-off-by: 奔跑的面条 <1262327911@qq.com>
2023-05-13 05:55:38 +00:00
奔跑的面条
afb364b362 docs: add sponsors 2023-05-13 13:52:52 +08:00
tnt group
01be79ee9a Merge branch 'dev' into dev-commet 2023-05-10 11:33:48 +08:00
Ming
3137109c6d fix: 支持分组 2023-05-09 10:13:48 +08:00
Ming
1d876418fe feat: 交互绑定组件,可以绑定分组内组件 2023-05-09 10:09:26 +08:00
Ming
fd37ce6ede fix: 补充枚举字段 2023-05-09 10:08:47 +08:00
Ming
9693c0dedf docs: md 2023-05-09 09:50:13 +08:00
Ming
dc8d7c86d7 perf: 优化颜色 2023-05-09 09:46:00 +08:00
Ming
8499d574dc fix: 小组件外层盒子宽度不一致 2023-05-09 09:44:14 +08:00
奔跑的面条
5c1af11788 build: 升级版本到 1.2.5 2023-05-08 20:42:22 +08:00
奔跑的面条
c953ccd238 Merge branch 'dev' 2023-05-08 20:37:17 +08:00
奔跑的面条
697542f804 perf: 优化发布的提示文案 2023-05-08 20:27:53 +08:00
奔跑的面条
0bb83c4131 perf: 去除多余无用功能 2023-05-08 20:21:23 +08:00
Ming
38d542ce77 feat: 加载完毕 清除历史记录 2023-05-04 17:17:45 +08:00
Ming
663cc94ae1 feat: 文本组件可进自定义换行 2023-05-04 17:13:51 +08:00
Ming
0370bc5c6c feat: 拖拽中无需计算hover 2023-05-04 17:07:35 +08:00
Ming
4dc8725b3e feat: 先获取数据后触发画布 2023-05-04 17:04:08 +08:00
奔跑的面条
7b9da32215 Merge branch 'dev' 2023-04-23 21:14:43 +08:00
奔跑的面条
9ce042cc8a perf: 优化切换图表导致画布缩放的交互 2023-04-23 21:14:28 +08:00
奔跑的面条
9b8cf36135 Merge branch 'dev' 2023-04-23 21:09:21 +08:00
奔跑的面条
a400526108 fix: 修改不返回 code 导致的问题 2023-04-23 21:05:52 +08:00
奔跑的面条
2a8d0717f8 fix: 处理编辑和预览不一致的问题 2023-04-23 20:33:46 +08:00
奔跑的面条
a6194b8fa9 style: 优化写法 2023-04-23 20:05:49 +08:00
tntgroup
09a21f44c5 Merge branch 'dev' into dev-commet 2023-04-22 08:05:21 +08:00
奔跑的面条
de681dbcd4 fix: 修复密码无限制的漏洞 2023-04-16 19:59:04 +08:00
奔跑的面条
465fbf713c fix: update readme 2023-04-11 09:23:33 +08:00
奔跑的面条
3b41bf480c Merge branch 'dev' 2023-04-05 16:39:25 +08:00
奔跑的面条
59b9ca0569 !152 fix: 柱状图传入错误数据后展示问题
Merge pull request !152 from szy/fix/barcommon
2023-04-05 08:27:11 +00:00
headmasterZhao
c930e3433f fix: 柱状图传入错误数据后展示问题 2023-04-04 09:35:28 +08:00
headmasterZhao
8e1731354c fix: 柱状图传入错误数据后展示问题 2023-04-04 09:34:47 +08:00
headmasterZhao
93f398d5a7 fix: 柱状图传入错误数据后展示问题 2023-04-04 08:46:56 +08:00
headmasterZhao
0f61ee728b fix: 柱状图传入错误数据后展示问题 2023-04-04 08:41:26 +08:00
奔跑的面条
234755cd34 Merge branch 'dev' 2023-04-03 20:11:09 +08:00
奔跑的面条
8ef4ab8175 fix: 处理过滤器会让柱状图重复的bug 2023-04-03 20:10:43 +08:00
奔跑的面条
c289252546 style: 去除多余代码 2023-04-02 18:16:27 +08:00
奔跑的面条
5207a98ce4 build: 升级版本号到 1.2.4 2023-04-02 18:15:12 +08:00
奔跑的面条
e411cb9b09 style: 去除多余代码 2023-04-02 18:14:22 +08:00
奔跑的面条
a2f32b0289 Merge branch 'dev' 2023-04-02 18:00:55 +08:00
奔跑的面条
db7a4801d8 feat: 还原实时同步到预览页面的功能 2023-04-01 22:00:26 +08:00
奔跑的面条
47d1dcbc04 build: 升级 vite 到4.x版本, 修改不兼容的代码 2023-04-01 21:33:01 +08:00
奔跑的面条
9fd408e8ef !151 fix: 柱状图组件动态数据发送请求后,配置会被覆盖成默认配置问题
Merge pull request !151 from szy/fix/bar
2023-04-01 12:00:02 +00:00
headmasterZhao
77a5f47e82 fix: 补充 2023-04-01 19:28:39 +08:00
headmasterZhao
4915f9e077 fix: 柱状图组件配置会被覆盖成默认配置问题 2023-04-01 19:18:15 +08:00
奔跑的面条
ce46f609ec !147 feat: 增加表盘组件,后续完善
Merge pull request !147 from szy/dev
2023-04-01 08:10:39 +00:00
headmasterZhao
ba425d44cc feat: 增加仪表盘组件,解决冲突 2023-04-01 15:48:33 +08:00
奔跑的面条
e6338f89dd !148 feat: 增加桑基图组件,桑基图mock接口
Merge pull request !148 from szy/feat-sankey
2023-04-01 06:41:15 +00:00
headmasterZhao
24361e63c2 feat: 增加桑基图,解决冲突 2023-04-01 13:02:22 +08:00
奔跑的面条
5effb94b1d !149 feat: 增加关系图组件,关系图mock接口
Merge pull request !149 from szy/feat/graph
2023-03-31 11:54:29 +00:00
奔跑的面条
f68aee1f3a !150 fix: 修复导入json数据后,修改其中一个图表的功能,所有图表都会修改的问题
Merge pull request !150 from szy/fix/bar
2023-03-31 11:48:16 +00:00
headmasterZhao
233f3210c6 fix: 横向柱状图导入json数据后,修改其中一个图表的功能,所有图表都会修改,和柱状图相同的问题 2023-03-30 19:40:00 +08:00
headmasterZhao
ab52fcaaf9 fix: 柱状图导入json数据后,修改其中一个图表的功能,所有图表都会修改 2023-03-30 19:37:18 +08:00
headmasterZhao
99eadd96af feat: 增加关系图,关系图mock接口 2023-03-30 16:40:30 +08:00
headmasterZhao
480e328306 feat: 增加mock桑基图数据 2023-03-30 13:37:07 +08:00
headmasterZhao
bd52c4454c feat: 增加桑基图组件 2023-03-30 13:28:39 +08:00
“zxzsxf”
6b87618622 feat: 增加表盘组件 2023-03-28 23:01:16 +08:00
奔跑的面条
4e9f96505a fix: 修改聚焦按钮函数错误 2023-03-28 16:10:14 +08:00
奔跑的面条
dde03274c2 !146 fix: 页面布局按钮激活状态不能准确切换问题
Merge pull request !146 from szy/dev
2023-03-28 08:08:52 +00:00
“zxzsxf”
41444026f9 fix: 页面布局按钮激活状态不能准确切换问题 2023-03-27 14:09:20 +08:00
“zxzsxf”
8b76f6e219 fix: 页面布局按钮激活状态不能准确切换问题 2023-03-27 14:07:56 +08:00
“zxzsxf”
5ed3e4e8b1 fix: 页面布局按钮激活状态不能准确切换问题 2023-03-27 13:51:52 +08:00
奔跑的面条
55221ff756 perf: 处理 alive 2023-03-27 10:32:52 +08:00
奔跑的面条
b534619649 !144 修复keep-alive组件复用
Merge pull request !144 from NuroDante/dev
2023-03-27 02:21:45 +00:00
奔跑的面条
421ea3dc84 !145 fix: dock模式工具栏会遮挡滚动条问题,工具栏选择隐藏时,鼠标悬停在下方工具栏闪烁问题
Merge pull request !145 from headmasterZhao/dev
2023-03-27 01:50:56 +00:00
“zxzsxf”
4bb3c34bc2 fix: dock模式工具栏会遮挡滚动条问题,工具栏选择隐藏时,鼠标悬停在下方工具栏闪烁问题 2023-03-26 16:09:55 +08:00
Nuro
2cbb851948 fix: 修复组件缓存问题 2023-03-21 20:18:41 +08:00
tnt group
98b38640d1 Merge branch 'dev' into dev-commet 2023-01-09 23:24:58 +08:00
tntgroup
89194848de Merge branch 'dev' into dev-commet 2022-12-19 13:25:57 +08:00
tnt group
7c1bcd0605 Merge branch 'dev' into dev-commet 2022-12-12 07:55:10 +08:00
tnt group
c1d98b03a9 Merge branch 'dev' into dev-commet 2022-11-29 17:03:05 +08:00
tnt group
66f4768285 Merge branch 'dev' into dev-commet 2022-11-24 11:04:20 +08:00
tnt group
ec6d8dedec Merge branch 'dev' into dev-commet 2022-11-18 13:21:38 +08:00
tnt group
99d20d99f9 Merge branch 'dev' into dev-commet 2022-11-13 16:21:11 +08:00
tnt group
0106d43467 Merge branch 'dev' into dev-commet 2022-11-07 08:25:07 +08:00
tnt group
231fb9dd29 Merge branch 'dev' into dev-commet 2022-11-03 11:06:18 +08:00
tnt group
f2ae08e62a Merge branch 'dev' into dev-commet 2022-11-01 08:28:43 +08:00
tnt group
e930ca1587 Merge branch 'dev' into dev-commet 2022-10-15 18:37:12 +08:00
tnt group
ddb125656a Merge branch 'dev' into dev-commet 2022-10-14 14:18:18 +08:00
tnt group
35cda43d95 Merge branch 'dev' into dev-commet 2022-10-13 22:19:28 +08:00
tnt group
e3faaad5e0 fix: 修复 eslint 报错(移除多余eslint注释) 2022-10-13 18:41:00 +08:00
tnt group
24ef446fd6 Merge branch 'dev' into dev-commet 2022-10-13 18:09:55 +08:00
tnt group
53b9e03e69 fix: 修复编辑动态请求modal,esc关闭后无法再次打开的问题,并增加取消关闭 2022-10-13 16:33:51 +08:00
143 changed files with 8468 additions and 3402 deletions

View File

@@ -1,4 +1,5 @@
#### 总览
<p align="center">
<img src="readme/logo-t-y.png" alt="go-view" />
</p>
@@ -6,6 +7,7 @@
<h4 align="center">开源、精美、便捷的「数据可视化」低代码开发平台</h4>
#### 长期赞助商
<div>
<div align="center" style="column-gap: 20px;">
<a
@@ -20,6 +22,19 @@
<img src="readme/sponsors/ccflow-banner.png" alt="go-view" style="width: 250px;" width="250px" />
</a>
<span> &nbsp;</span>
<a
href="https://fastbee.cn/"
target="_blank"
style="
padding: 10px 20px;
display: inline-block;
border-radius: 10px;
background: #f9f9f9;
">
<img src="readme/sponsors/fb-banner.gif" alt="go-view" style="width: 250px;" width="250px"/>
</a>
<br/>
<br/>
<a
href="https://www.qeasy.cloud/"
target="_blank"
@@ -31,6 +46,18 @@
">
<img src="readme/sponsors/qyy-banner.png" alt="go-view" style="width: 250px;" width="250px"/>
</a>
<span> &nbsp;</span>
<a
href="http://doc.zyplayer.com/#/integrate/zyplayer-doc?utm=goview"
target="_blank"
style="
padding: 10px 20px;
display: inline-block;
border-radius: 10px;
background: #f9f9f9;
">
<img src="readme/sponsors/zyplayer-banner.png" alt="go-view" style="width: 250px;" width="250px"/>
</a>
</div>
</div>
@@ -48,13 +75,13 @@ Cloud IDE 代码在线预览地址:[https://idegitee.com/dromara/go-view](http
#### 🤯 后端项目看这里!
后端项目 gitee 地址:[https://gitee.com/MTrun/go-view-serve](https://gitee.com/MTrun/go-view-serve)
后端地址(社区实现,仅供参考):
接口说明地址:[https://docs.apipost.cn/...](https://docs.apipost.cn/preview/5aa85d10a59d66ce/ddb813732007ad2b?target_id=84dbc5b0-158f-4bcb-8f74-793ac604ada3#3e053622-1e76-43f9-a039-756aee822dbb)
其它后端方案地址:
- 【.NET】[https://gitee.com/sun_xiang_yu/go-view-dotnet](https://gitee.com/sun_xiang_yu/go-view-dotnet)
- `JAVA` [https://gitee.com/MTrun/go-view-serve](https://gitee.com/MTrun/go-view-serve) (当前使用)
- `.NET` [https://gitee.com/sun_xiang_yu/go-view-dotnet](https://gitee.com/sun_xiang_yu/go-view-dotnet)
- `NODE` [https://gitee.com/qwdingyu/led](https://gitee.com/qwdingyu/led)
- `Docker 镜像` [https://gitee.com/AHEAD4/go-view-docker](https://gitee.com/AHEAD4/go-view-docker)
- `接口文档`[https://docs.apipost.cn](https://docs.apipost.cn/preview/5aa85d10a59d66ce/ddb813732007ad2b?target_id=84dbc5b0-158f-4bcb-8f74-793ac604ada3) (不是最新, 以前端代码为准)
#### 整体介绍
@@ -102,7 +129,7 @@ Cloud IDE 代码在线预览地址:[https://idegitee.com/dromara/go-view](http
| 名称 | 版本 | 名称 | 版本 |
| ------------------- | ----- | ----------- | ------ |
| Vue | 3.2.x | TypeScript4 | 4.6.x |
| Vite | 2.9.x | NaiveUI | 2.27.x |
| Vite | 4.2.x | NaiveUI | 2.34.x |
| ECharts | 5.3.x | Pinia | 2.0.x |
| 详见 `package.json` | 😁 | 🥰 | 🤗 |
@@ -110,7 +137,7 @@ Cloud IDE 代码在线预览地址:[https://idegitee.com/dromara/go-view](http
| 名称 | 版本 | 名称 | 版本 |
| ---- | ------- | ------- | ----- |
| node | 16.14.x | npm | 8.5.x |
| node | 16.16.x | npm | 8.5.x |
| pnpm | 7.1.x | windows | 11 |
已完成图表:

View File

@@ -1,6 +1,6 @@
{
"name": "go-view",
"version": "1.2.3",
"version": "1.2.7",
"engines": {
"node": ">=12.0"
},
@@ -33,6 +33,7 @@
"highlight.js": "^11.5.0",
"html2canvas": "^1.4.1",
"keymaster": "^1.6.2",
"mitt": "^3.0.0",
"monaco-editor": "^0.33.0",
"naive-ui": "2.34.3",
"pinia": "^2.0.13",
@@ -49,14 +50,16 @@
"devDependencies": {
"@commitlint/cli": "^17.0.2",
"@commitlint/config-conventional": "^17.0.2",
"@iconify/types": "^2.0.0",
"@iconify/vue": "^4.1.1",
"@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",
"@vicons/ionicons5": "~0.11.0",
"@vitejs/plugin-vue": "^1.10.2",
"@vitejs/plugin-vue-jsx": "^1.3.9",
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"@vue/compiler-sfc": "^3.2.31",
"@vueuse/core": "^7.7.1",
"commitlint": "^17.0.2",
@@ -75,7 +78,7 @@
"sass": "^1.49.11",
"sass-loader": "^12.6.0",
"typescript": "4.6.3",
"vite": "2.9.9",
"vite": "4.3.6",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-importer": "^0.2.5",
"vite-plugin-mock": "^2.9.6",

3221
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

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

View File

@@ -163,7 +163,6 @@ export const customizeHttp = (targetParams: RequestConfigType, globalParams: Req
params = translateStr(params)
// form 类型处理
let formData: FormData = new FormData()
formData.set('default', 'defaultData')
// 类型处理
switch (requestParamsBodyType) {

100
src/api/mock/graph.json Normal file
View File

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

View File

@@ -19,6 +19,8 @@ export const capsuleUrl = '/mock/capsule'
export const wordCloudUrl = '/mock/wordCloud'
export const treemapUrl = '/mock/treemap'
export const threeEarth01Url = '/mock/threeEarth01Data'
export const sankeyUrl = '/mock/sankey'
export const graphUrl = '/mock/graphData'
const mockObject: MockMethod[] = [
{
@@ -103,6 +105,16 @@ const mockObject: MockMethod[] = [
method: RequestHttpEnum.GET,
response: () => test.threeEarth01Data
},
{
url: sankeyUrl,
method: RequestHttpEnum.GET,
response: () => test.fetchSankey
},
{
url: graphUrl,
method: RequestHttpEnum.GET,
response: () => test.graphData
},
]
export default mockObject

86
src/api/mock/sankey.json Normal file
View File

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

View File

@@ -2,6 +2,8 @@ import heatmapJson from './heatMapData.json'
import scatterJson from './scatter.json'
import mapJson from './map.json'
import tTreemapJson from './treemap.json'
import sankeyJson from './sankey.json'
import graphDataJson from './graph.json'
export default {
// 单图表
@@ -219,5 +221,19 @@ export default {
'endArray|10': [{ name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' }]
}
]
}
},
// 桑基图
fetchSankey: {
code: 0,
status: 200,
msg: '请求成功',
data: sankeyJson
},
// 关系图
graphData: {
code: 0,
status: 200,
msg: '请求成功',
data: graphDataJson
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -257,9 +257,36 @@
<n-switch v-model:value="legend.show" size="small"></n-switch>
</template>
<setting-item-box name="图例文字">
<setting-item>
<setting-item name="颜色">
<n-color-picker size="small" v-model:value="legend.textStyle.color"></n-color-picker>
</setting-item>
<setting-item name="大小">
<n-input-number v-model:value="legend.textStyle.fontSize" :min="1" size="small"></n-input-number>
</setting-item>
</setting-item-box>
<setting-item-box name="图例位置">
<setting-item name="x轴">
<n-select v-model:value="legend.x" size="small" :options="legendConfig.lengendX" />
</setting-item>
<setting-item name="y轴">
<n-select v-model:value="legend.y" size="small" :options="legendConfig.lengendY" />
</setting-item>
</setting-item-box>
<setting-item-box name="图例信息">
<setting-item name="方向">
<n-select v-model:value="legend.orient" size="small" :options="legendConfig.orient" />
</setting-item>
<setting-item name="形状">
<n-select v-model:value="legend.icon" size="small" :options="legendConfig.shape" />
</setting-item>
</setting-item-box>
<setting-item-box name="图例大小">
<setting-item name="宽">
<n-input-number v-model:value="legend.itemWidth" :min="1" size="small"></n-input-number>
</setting-item>
<setting-item name="高">
<n-input-number v-model:value="legend.itemHeight" :min="1" size="small"></n-input-number>
</setting-item>
</setting-item-box>
</collapse-item>
@@ -309,9 +336,9 @@
</template>
<script setup lang="ts">
import { PropType, computed } from 'vue'
import { PropType, computed, watch } from 'vue'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import { axisConfig } from '@/packages/chartConfiguration/echarts/index'
import { axisConfig, legendConfig } from '@/packages/chartConfiguration/echarts/index'
import { CollapseItem, SettingItemBox, SettingItem, GlobalSettingPosition } from '@/components/Pages/ChartItemSetting'
import { icon } from '@/plugins'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
@@ -360,4 +387,18 @@ const grid = computed(() => {
const visualMap = computed(() => {
return props.optionData.visualMap
})
// 监听legend color颜色改变type = scroll的颜色
watch(() => legend.value && legend.value.textStyle.color, (newVal) => {
if (legend.value && newVal) {
if (!legend.value.pageTextStyle) {
legend.value.pageTextStyle = { color: newVal }
} else {
legend.value.pageTextStyle.color = newVal
}
}
}, {
immediate: true,
deep: true,
})
</script>

View File

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

View File

@@ -1,13 +1,13 @@
<template>
<div class="go-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" setup>
import { ref, PropType, watch } from 'vue'
import { FlipType } from './index'
import { ref, PropType, watch, nextTick } from 'vue'
import { FlipType } from './index'
const props = defineProps({
flipType: {
@@ -43,6 +43,10 @@ const props = defineProps({
backColor: {
type: String,
default: '#000000'
},
borderWidth: {
type: Number,
default: 2
}
})
@@ -50,19 +54,27 @@ const isFlipping = ref(false)
const frontTextFromData = ref(props.count || 0)
const backTextFromData = ref(props.count || 0)
let timeoutID: any = 0
// 翻牌
const flip = (front: string | number, back: string | number) => {
const flip = async (front: string | number, back: string | number) => {
// 如果处于翻转中,则不执行
if (isFlipping.value) return
if (isFlipping.value) {
isFlipping.value = false // 立即结束此次动画
clearTimeout(timeoutID) // 清除上一个计时器任务
await nextTick()
await flip(front, back) // 开始最后一次翻牌任务
return
}
// 设置翻盘前后数据
backTextFromData.value = back
frontTextFromData.value = front
// 设置翻转状态为true
isFlipping.value = true
// 翻牌结束的行为
setTimeout(() => {
timeoutID = setTimeout(() => {
isFlipping.value = false // 设置翻转状态为false
frontTextFromData.value = back
}, props.duration)
@@ -86,6 +98,7 @@ $radius: v-bind('`${props.radius}px`');
$width: v-bind('`${props.width}px`');
$height: v-bind('`${props.height}px`');
$perspective: v-bind('`${props.height * 2}px`');
$borderWidth: v-bind('`${props.borderWidth * 2}px`');
$speed: v-bind('`${props.duration / 1000}s`');
$shadowColor: #000000;
$lineColor: #4a9ef8;
@@ -125,13 +138,12 @@ $lineColor: #4a9ef8;
}
// #endregion
.go-Flipper {
.go-flipper {
display: inline-block;
position: relative;
width: $width;
height: $height;
line-height: $height;
border: solid 1px $backColor;
border-radius: $radius;
background: $frontColor;
font-size: $width;
@@ -139,6 +151,17 @@ $lineColor: #4a9ef8;
box-shadow: 0 0 6px rgba($color: $shadowColor, $alpha: 0.5); // 阴影部分
text-align: center;
// font-family: 'Helvetica Neue';
&::after {
content: '';
position: absolute;
z-index: 10;
left: 0;
top: 0;
right: 0;
bottom: 0;
box-shadow: inset 0 0 $borderWidth 0 $frontColor; // 内测阴影部分
border-radius: $radius;
}
.digital:before,
.digital:after {
@@ -186,11 +209,13 @@ $lineColor: #4a9ef8;
&.down.go .front:before {
transform-origin: 50% 100%;
animation: frontFlipDown $speed ease-in-out both;
box-shadow: 0 -2px 6px rgba($color: $lineColor, $alpha: 0.3);
box-shadow: 0 -2px $borderWidth 0 $frontColor;
backface-visibility: hidden;
}
&.down.go .back:after {
animation: backFlipDown $speed ease-in-out both;
box-shadow: 0 2px $borderWidth 0 $frontColor;
backface-visibility: hidden;
}
/*向上翻*/
&.up .front:after {
@@ -208,11 +233,13 @@ $lineColor: #4a9ef8;
&.up.go .front:after {
transform-origin: 50% 0;
animation: frontFlipUp $speed ease-in-out both;
box-shadow: 0 2px 6px rgba($color: $lineColor, $alpha: 0.3);
box-shadow: 0 2px $borderWidth 0 $frontColor;
backface-visibility: hidden;
}
&.up.go .back:before {
animation: backFlipUp $speed ease-in-out both;
box-shadow: 0 -2px $borderWidth 0 $frontColor;
backface-visibility: hidden;
}
}
</style>

View File

@@ -12,6 +12,7 @@ export enum DragKeyEnum {
// 不同页面保存操作
export enum SavePageEnum {
CHART = 'SaveChart',
CHART_TO_PREVIEW = 'ChartToPreview',
JSON = 'SaveJSON',
CLOSE = 'close'
}

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
import { toRaw } from 'vue'
import { toRaw, watch, computed, ComputedRef } from 'vue'
import { customizeHttp } from '@/api/http'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
@@ -20,7 +20,7 @@ const mittDataPondMap = new Map<string, DataPondMapType[]>()
// 创建单个数据项轮询接口
const newPondItemInterval = (
requestGlobalConfig: RequestGlobalConfigType,
requestDataPondItem: RequestDataPondItemType,
requestDataPondItem: ComputedRef<RequestDataPondItemType>,
dataPondMapItem?: DataPondMapType[]
) => {
if (!dataPondMapItem) return
@@ -31,8 +31,7 @@ const newPondItemInterval = (
// 请求
const fetchFn = async () => {
try {
const res = await customizeHttp(toRaw(requestDataPondItem.dataPondRequestConfig), toRaw(requestGlobalConfig))
const res = await customizeHttp(toRaw(requestDataPondItem.value.dataPondRequestConfig), toRaw(requestGlobalConfig))
if (res) {
try {
// 遍历更新回调函数
@@ -49,19 +48,32 @@ const newPondItemInterval = (
}
}
watch(
() => requestDataPondItem.value.dataPondRequestConfig.requestParams.Params,
() => {
fetchFn()
},
{
immediate: false,
deep: true
}
)
// 立即调用
fetchFn()
const targetInterval = requestDataPondItem.dataPondRequestConfig.requestInterval
const targetUnit = requestDataPondItem.dataPondRequestConfig.requestIntervalUnit
const targetInterval = requestDataPondItem.value.dataPondRequestConfig.requestInterval
const targetUnit = requestDataPondItem.value.dataPondRequestConfig.requestIntervalUnit
const globalRequestInterval = requestGlobalConfig.requestInterval
const globalUnit = requestGlobalConfig.requestIntervalUnit
// 定时时间
const time = targetInterval ? targetInterval : globalRequestInterval
const time = targetInterval ? targetInterval : globalRequestInterval
// 单位
const unit = targetInterval ? targetUnit : globalUnit
const unit = targetInterval ? targetUnit : globalUnit
// 开启轮询
if (time) fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit))
}
@@ -96,13 +108,16 @@ export const useChartDataPondFetch = () => {
}
// 初始化数据池
const initDataPond = (requestGlobalConfig: RequestGlobalConfigType) => {
const { requestDataPond } = requestGlobalConfig
const initDataPond = (useChartEditStore: ChartEditStoreType) => {
const { requestGlobalConfig } = useChartEditStore()
const chartEditStore = useChartEditStore()
// 根据 mapId 查找对应的数据池配置
for (let pondKey of mittDataPondMap.keys()) {
const requestDataPondItem = requestDataPond.find(item => item.dataPondId === pondKey)
const requestDataPondItem = computed(() => {
return requestGlobalConfig.requestDataPond.find(item => item.dataPondId === pondKey)
}) as ComputedRef<RequestDataPondItemType>
if (requestDataPondItem) {
newPondItemInterval(requestGlobalConfig, requestDataPondItem, mittDataPondMap.get(pondKey))
newPondItemInterval(chartEditStore.requestGlobalConfig, requestDataPondItem, mittDataPondMap.get(pondKey))
}
}
}

View File

@@ -1,4 +1,5 @@
import { toRefs } from 'vue'
import { isPreview } from '@/utils'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
@@ -12,6 +13,7 @@ export const useChartInteract = (
param: { [T: string]: any },
interactEventOn: string
) => {
if (!isPreview()) return
const chartEditStore = useChartEditStore()
const { interactEvents } = chartConfig.events
const fnOnEvent = interactEvents.filter(item => {
@@ -20,20 +22,37 @@ export const useChartInteract = (
if (fnOnEvent.length === 0) return
fnOnEvent.forEach(item => {
const index = chartEditStore.fetchTargetIndex(item.interactComponentId)
if (index === -1) return
const { Params, Header } = toRefs(chartEditStore.componentList[index].request.requestParams)
Object.keys(item.interactFn).forEach(key => {
if (Params.value[key]) {
Params.value[key] = param[item.interactFn[key]]
}
if (Header.value[key]) {
Header.value[key] = param[item.interactFn[key]]
}
})
const globalConfigPindAprndex = chartEditStore.requestGlobalConfig.requestDataPond.findIndex(cItem =>
cItem.dataPondId === item.interactComponentId
)
if (globalConfigPindAprndex !== -1) {
const { Params, Header } = toRefs(chartEditStore.requestGlobalConfig.requestDataPond[globalConfigPindAprndex].dataPondRequestConfig.requestParams)
Object.keys(item.interactFn).forEach(key => {
if (key in Params.value) {
Params.value[key] = param[item.interactFn[key]]
}
if (key in Header.value) {
Header.value[key] = param[item.interactFn[key]]
}
})
} else {
const index = chartEditStore.fetchTargetIndex(item.interactComponentId)
if (index === -1) return
const { Params, Header } = toRefs(chartEditStore.componentList[index].request.requestParams)
Object.keys(item.interactFn).forEach(key => {
if (key in Params.value) {
Params.value[key] = param[item.interactFn[key]]
}
if (key in Header.value) {
Header.value[key] = param[item.interactFn[key]]
}
})
}
})
}
// 联动事件触发的 type 变更时,清除当前绑定内容
export const clearInteractEvent = (chartConfig: CreateComponentType) => {

View File

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

View File

@@ -1,5 +1,5 @@
export default {
create_btn: 'Creat',
create_btn: 'Create',
create_tip: 'Please select a content for development',
project: 'Project',
my: 'My',

View File

@@ -1,13 +1,9 @@
<template>
<router-view>
<template #default="{ Component, route }">
<component
v-if="route.noKeepAlive"
:is="Component"
:key="route.fullPath"
></component>
<component v-if="route.meta.noKeepAlive" :is="Component"></component>
<keep-alive v-else>
<component :is="Component" :key="route.fullPath"></component>
<component :is="Component"></component>
</keep-alive>
</template>
</router-view>

View File

@@ -1,14 +1,14 @@
<template>
<router-view #default="{ Component, route }">
<transition name="fade" mode="out-in" appear>
<component
v-if="route.noKeepAlive"
:is="Component"
:key="route.fullPath"
></component>
<keep-alive v-else>
<component :is="Component" :key="route.fullPath"></component>
</keep-alive>
</transition>
</router-view>
</template>
<template>
<router-view #default="{ Component, route }">
<transition name="fade" mode="out-in" appear>
<component
v-if="route.meta.noKeepAlive"
:is="Component"
:key="route.fullPath"
></component>
<keep-alive v-else>
<component :is="Component" :key="route.fullPath"></component>
</keep-alive>
</transition>
</router-view>
</template>

View File

@@ -1,3 +1,4 @@
export * from './axis'
export * from './line'
export * from './label'
export * from './label'
export * from './legend'

View File

@@ -0,0 +1,70 @@
export const legendConfig = {
// X轴位置
lengendX: [
{
label: '靠左',
value: 'left'
},
{
label: '居中',
value: 'center'
},
{
label: '靠右',
value: 'right'
}
],
// y轴位置
lengendY: [
{
label: '靠上',
value: 'top'
},
{
label: '居中',
value: 'center'
},
{
label: '靠下',
value: 'bottom'
}
],
// 排列方向
orient: [
{
label: '水平',
value: 'horizontal'
},
{
label: '垂直',
value: 'vertical'
}
],
// 形状
shape: [
{
label: '圆形',
value: 'circle'
},
{
label: '方形',
value: 'rect'
},
{
label: '圆角方形',
value: 'roundRect'
},
{
label: '三角形',
value: 'triangle'
},
{
label: '钢笔形',
value: 'pin'
},
{
label: '箭头形',
value: 'arrow'
}
]
}

View File

@@ -1,15 +1,8 @@
<template>
<v-chart
ref="vChartRef"
:init-options="initOptions"
:theme="themeColor"
:option="option"
:manual-update="isPreview()"
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option" :manual-update="isPreview()"
:update-options="{
replaceMerge: replaceMergeArr
}"
autoresize
></v-chart>
}" autoresize></v-chart>
</template>
<script setup lang="ts">
@@ -27,6 +20,7 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
import { isPreview } from '@/utils'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
import isObject from 'lodash/isObject'
import cloneDeep from 'lodash/cloneDeep'
const props = defineProps({
themeSetting: {
@@ -61,11 +55,23 @@ watch(
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)
// 对oldData进行判断防止传入错误数据之后对旧维度判断产生干扰
// 此处计算的是dimensions的Y轴维度若是dimensions.length为0或1则默认为1排除X轴维度干扰
const oldDimensions = Array.isArray(oldData?.dimensions)&&oldData.dimensions.length >= 1 ? oldData.dimensions.length : 1;
const newDimensions = newData.dimensions.length >= 1 ? newData.dimensions.length : 1;
const dimensionsGap = newDimensions - oldDimensions;
if (dimensionsGap < 0) {
props.chartConfig.option.series.splice(newDimensions - 1)
} else if (dimensionsGap > 0) {
if(!oldData || !oldData?.dimensions || !Array.isArray(oldData?.dimensions) || !oldData?.dimensions.length ) {
props.chartConfig.option.series=[]
}
for (let i = 0; i < dimensionsGap; i++) {
seriesArr.push(cloneDeep(seriesItem))
}
props.chartConfig.option.series.push(...seriesArr)
}
replaceMergeArr.value = ['series']
props.chartConfig.option.series = seriesArr
nextTick(() => {
replaceMergeArr.value = []
})

View File

@@ -26,6 +26,7 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
import { isPreview } from '@/utils'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
import isObject from 'lodash/isObject'
import cloneDeep from 'lodash/cloneDeep'
const props = defineProps({
themeSetting: {
@@ -61,7 +62,7 @@ watch(
if (Array.isArray(newData?.dimensions)) {
const seriesArr = []
for (let i = 0; i < newData.dimensions.length - 1; i++) {
seriesArr.push(seriesItem)
seriesArr.push(cloneDeep(seriesItem))
}
replaceMergeArr.value = ['series']
props.chartConfig.option.series = seriesArr

View File

@@ -16,7 +16,8 @@ export enum ThemeEnum {
MACARON = 'macaron',
BLUE = 'blue',
DARKBLUE = 'darkblue',
WINE = 'wine'
WINE = 'wine',
WEIXIN = 'tileLayer'
}
export enum LangEnum {

View File

@@ -134,6 +134,10 @@ const themeOptions = [
{
value: ThemeEnum.WINE,
label: '酱籽'
},
{
value: ThemeEnum.WEIXIN,
label: '卫星'
}
]

View File

@@ -8,7 +8,7 @@ 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 { MarkerEnum, ThemeEnum } from './config'
import { isArray } from '@/utils'
const props = defineProps({
@@ -51,7 +51,6 @@ const initMap = (newData: any) => {
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 度
@@ -60,6 +59,14 @@ const initMap = (newData: any) => {
willReadFrequently: true
})
dataHandle(props.chartConfig.option.dataset)
let satellite = new AMap.TileLayer.Satellite()
let roadNet = new AMap.TileLayer.RoadNet()
if (newData.amapStyleKey === ThemeEnum.WEIXIN) {
mapIns.add([satellite, roadNet])
} else {
mapIns.remove([satellite, roadNet])
mapIns.setMapStyle(`amap://styles/${amapStyleKeyCustom.value !== '' ? amapStyleKeyCustom.value : amapStyleKey.value}`)
}
})
.catch(e => {})
}

View File

@@ -0,0 +1,91 @@
import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
import { DialConfig } from './index'
import { CreateComponentType } from '@/packages/index.d'
import cloneDeep from 'lodash/cloneDeep'
export const includes = []
const option = {
backgroundColor: '#0E1327',
dataset:70,
series: [{
type: "gauge",
data: [{
value: 70,
itemStyle: { // 指针样式
color: '#2AF4FF'
}
}],
min: 0, //最小刻度
max: 100, //最大刻度
splitNumber: 10, //刻度数量
center: ['50%', '55%'],
radius: '80%',
axisLine: {
lineStyle: {
color: [
[0, 'rgba(0,212,230,0.5)'],
[1, 'rgba(28,128,245,0)']
],
width: 170
}
},
axisLabel: { // 文字样式
color: '#eee',
fontSize: 14,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
detail: {
show: false,
},
pointer: {
length: '80%',
width: 4
},
animationDuration: 2000,
},
{
name: '外部刻度',
type: 'gauge',
center: ['50%', '55%'],
radius: '90%',
axisLine: {
show: true,
lineStyle: {
width: 25,
color: [ // 表盘外部颜色
[0, '#1369E380'],
[1, '#1369E380']
],
}
},
axisLabel: {
show:false,
},
axisTick: {
splitNumber: 5,
lineStyle: { //刻度颜色
color: '#42E5FB',
width: 2,
},
},
splitLine: {
length: 15,
lineStyle: {
color: '#42E5FB',
}
},
},
]
};
export default class Config extends PublicConfigClass implements CreateComponentType {
public key: string = DialConfig.key
public chartConfig = cloneDeep(DialConfig)
// 图表配置项
public option = echartOptionProfixHandle(option, includes)
}

View File

@@ -0,0 +1,84 @@
<template>
<!-- 遍历 seriesList -->
<CollapseItem :name="`圆环`" :expanded="true">
<SettingItemBox name="数据">
<SettingItem name="数值">
<n-input-number v-model:value="config.dataset" :min="dialConfig.min" :max="dialConfig.max" :step="1" size="small" placeholder="数值">
</n-input-number>
</SettingItem>
</SettingItemBox>
<!-- Echarts 全局设置 -->
<!-- 表盘刻度字体 -->
<SettingItemBox name="字体">
<SettingItem name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="dialConfig.axisLabel.color"></n-color-picker>
</SettingItem>
<SettingItem name="字体大小">
<n-input-number
v-model:value="dialConfig.axisLabel.fontSize"
:min="0"
:step="1"
size="small"
placeholder="字体大小"
>
</n-input-number>
</SettingItem>
</SettingItemBox>
<!-- 表盘 -->
<SettingItemBox name="表盘外部">
<SettingItem name="颜色" >
<n-color-picker size="small" :modes="['hex']" v-model:value="config.series[1].axisLine.lineStyle.color[1][1]"></n-color-picker>
</SettingItem>
</SettingItemBox>
<!-- 指针 -->
<SettingItemBox name="指针">
<SettingItem name="颜色" >
<n-color-picker size="small" :modes="['hex']" v-model:value="dialConfig.data[0].itemStyle.color"></n-color-picker>
</SettingItem>
<SettingItem name="宽度">
<n-input-number v-model:value="dialConfig.pointer.width" :min="0" :step="1" size="small" placeholder="数值">
</n-input-number>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="刻度">
<SettingItem name="最小值">
<n-input-number v-model:value="dialConfig.min" :min="0" :step="1" size="small" placeholder="数值">
</n-input-number>
</SettingItem>
<SettingItem name="最大值">
<n-input-number v-model:value="dialConfig.max" :min="0" :step="1" size="small" placeholder="数值">
</n-input-number>
</SettingItem>
<SettingItem name="颜色" >
<n-color-picker size="small" :modes="['hex']" v-model:value="config.series[1].axisTick.lineStyle.color" @update:value="updateClick"></n-color-picker>
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, computed } from 'vue'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { GlobalThemeJsonType } from '@/settings/chartThemes'
const props = defineProps({
optionData: {
type: Object as PropType<GlobalThemeJsonType>,
required: true
}
})
const config = computed(() => {
return props.optionData
})
const dialConfig = computed(() => {
return props.optionData.series[0]
})
const updateClick = (val: any) => {
props.optionData.series[1].splitLine.lineStyle.color=val
}
</script>

View File

@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const DialConfig: ConfigType = {
key: 'Dial',
chartKey: 'VDial',
conKey: 'VCDial',
title: '表盘',
category: ChatCategoryEnum.MORE,
categoryName: ChatCategoryEnumName.MORE,
package: PackagesCategoryEnum.CHARTS,
chartFrame: ChartFrameEnum.COMMON,
image:'dial.png'
}

View File

@@ -0,0 +1,69 @@
<template>
<v-chart :theme="themeColor" :init-options="initOptions" :option="option.value" autoresize> </v-chart>
</template>
<script setup lang="ts">
import { PropType, reactive, watch } from 'vue'
import VChart from 'vue-echarts'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart } from 'echarts/charts'
import { mergeTheme } from '@/packages/public/chart'
import config, { includes } from './config'
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent, TitleComponent } from 'echarts/components'
const props = defineProps({
themeSetting: {
type: Object,
required: true
},
themeColor: {
type: Object,
required: true
},
chartConfig: {
type: Object as PropType<config>,
required: true
}
})
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
use([DatasetComponent, CanvasRenderer, PieChart, GridComponent, TooltipComponent, LegendComponent, TitleComponent])
const option = reactive({
value: {}
})
const dataHandle = (newData: any) => {
let config = props.chartConfig.option
config.series[0].data[0].value = newData
option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
option.value = props.chartConfig.option
}
// 配置时
watch(
() => props.chartConfig.option.dataset,
newData => {
try {
dataHandle(newData)
} catch (error) {
console.log(error)
}
},
{
immediate: true,
deep: false
}
)
// 预览时
useChartDataFetch(props.chartConfig, useChartEditStore, (resData: number) => {
// @ts-ignore
option.value.series[0].data[0].value = resData
})
</script>

View File

@@ -0,0 +1,71 @@
import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
import { GraphConfig } from './index'
import { CreateComponentType } from '@/packages/index.d'
import cloneDeep from 'lodash/cloneDeep'
import dataJson from './data.json'
export const includes = []
// 关系图布局
export const GraphLayout = [
{ label: '无', value: 'none' },
{ label: '环形', value: 'circular' }
]
// 标签开关
export const LabelSwitch = [
{ label: '开启', value: 1 },
{ label: '关闭', value: 0 }
]
// 标签位置
export const LabelPosition = [
{ label: '左侧', value: 'left' },
{ label: '右侧', value: 'right' },
{ label: '顶部', value: 'top' },
{ label: '底部', value: 'bottom' },
{ label: '内部', value: 'inside' },
]
export const option = {
dataset: { ...dataJson },
tooltip: {},
legend:{
show:true,
textStyle:{
color:"#eee",
fontSize: 14 ,
},
data: dataJson.categories.map(function (a) {
return a.name;
})
},
series: [
{
type: 'graph',
layout: 'none', // none circular环形布局
data: dataJson.nodes,
links: dataJson.links,
categories: dataJson.categories,
label: { // 标签
show: 1,
position: 'right',
formatter: '{b}'
},
labelLayout: {
hideOverlap: true
},
lineStyle: {
color: 'source', // 线条颜色
curveness: 0.2 // 线条卷曲程度
}
}
]
};
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = GraphConfig.key
public chartConfig = cloneDeep(GraphConfig)
// 图表配置项
public option = echartOptionProfixHandle(option, includes)
}

View File

@@ -0,0 +1,62 @@
<template>
<div>
<CollapseItem name="关系图" :expanded="true">
<SettingItemBox name="样式">
<setting-item name="布局">
<n-select v-model:value="graphConfig.layout" :options="GraphLayout" size="small" />
</setting-item>
</SettingItemBox>
<SettingItemBox name="标签">
<setting-item name="展示">
<n-select v-model:value="graphConfig.label.show" :options="LabelSwitch" size="small" />
</setting-item>
<setting-item name="位置">
<n-select v-model:value="graphConfig.label.position" :options="LabelPosition" size="small" />
</setting-item>
</SettingItemBox>
<SettingItemBox name="线条">
<SettingItem name="弧线">
<!-- 需要输入两位的小数才会变化 -->
<n-input-number
v-model:value="optionData.series[0].lineStyle.curveness"
:min="0"
:step="0.01"
placeholder="弯曲程度"
size="small"
></n-input-number>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="图例">
<SettingItem name="颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.legend.textStyle.color"
></n-color-picker>
</SettingItem>
<SettingItem name="文本">
<n-input-number v-model:value="optionData.legend.textStyle.fontSize" :min="0" :step="1" size="small" placeholder="文字大小">
</n-input-number>
</SettingItem>
</SettingItemBox>
</CollapseItem>
</div>
</template>
<script setup lang="ts">
import { PropType, computed } from 'vue'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { option, GraphLayout, LabelSwitch, LabelPosition } from './config'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option & GlobalThemeJsonType>,
required: true
}
})
const graphConfig = computed<typeof option.series[0]>(() => {
return props.optionData.series[0]
})
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const GraphConfig: ConfigType = {
key: 'Graph',
chartKey: 'VGraph',
conKey: 'VCGraph',
title: '关系图',
category: ChatCategoryEnum.MORE,
categoryName: ChatCategoryEnumName.MORE,
package: PackagesCategoryEnum.CHARTS,
chartFrame: ChartFrameEnum.COMMON,
image: 'graph.png'
}

View File

@@ -0,0 +1,80 @@
<template>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option" :manual-update="isPreview()" autoresize></v-chart>
</template>
<script setup lang="ts">
import { ref, computed, PropType, watch } from 'vue'
import VChart from 'vue-echarts'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import dataJson from './data.json'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { RadarChart } from 'echarts/charts'
import { includes } from './config'
import { mergeTheme, setOption } from '@/packages/public/chart'
import { useChartDataFetch } from '@/hooks'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { isPreview } from '@/utils'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
const props = defineProps({
themeSetting: {
type: Object,
required: true
},
themeColor: {
type: Object,
required: true
},
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
use([DatasetComponent, CanvasRenderer, RadarChart, GridComponent, TooltipComponent, LegendComponent])
const vChartRef = ref<typeof VChart>()
const option = computed(() => {
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
})
const dataSetHandle = (dataset: typeof dataJson) => {
if (dataset.nodes) {
props.chartConfig.option.series[0].data = dataset.nodes
}
if (dataset.links) {
props.chartConfig.option.series[0].links = dataset.links
}
if (dataset.categories) {
props.chartConfig.option.series[0].categories = dataset.categories
// @ts-ignore
props.chartConfig.option.legend.data = dataset.categories.map((i: { name: string }) => i.name)
}
if (vChartRef.value && isPreview()) {
setOption(vChartRef.value, props.chartConfig.option)
}
}
watch(
() => props.chartConfig.option.dataset,
newData => {
try {
dataSetHandle(newData)
} catch (error) {
console.log(error)
}
},
{
deep: false
}
)
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: typeof dataJson) => {
dataSetHandle(newData)
})
</script>

View File

@@ -0,0 +1,43 @@
import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
import { SankeyConfig } from './index'
import { CreateComponentType } from '@/packages/index.d'
import cloneDeep from 'lodash/cloneDeep'
import dataJson from './data.json'
export const includes = ['legend']
// 图表方向
export const orientList = [
{ label: '水平', value: 'horizontal' },
{ label: '垂直', value: 'vertical' }
]
// 标签展示
export const toolTipSwitch = [
{ label: '开启', value: 1 },
{ label: '关闭', value: 0 }
]
export const option = {
dataset: { ...dataJson },
tooltip: {
show: 1,
trigger: 'item',
triggerOn: 'mousemove'
},
series: {
type: 'sankey',
layout: 'none',
orient:'horizontal',
data: dataJson.label,
links: dataJson.links,
levels: dataJson.levels
}
};
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = SankeyConfig.key
public chartConfig = cloneDeep(SankeyConfig)
// 图表配置项
public option = echartOptionProfixHandle(option, includes)
}

View File

@@ -0,0 +1,43 @@
<template>
<div>
<CollapseItem name="桑基图" :expanded="true">
<SettingItemBox name="样式">
<SettingItem name="方向">
<n-select
v-model:value="sankeyConfig.orient"
size="small"
:options="orientList"
placeholder="选择方向"
/>
</SettingItem>
<SettingItem name="提示标签">
<n-select
v-model:value="optionData.tooltip.show"
size="small"
:options="toolTipSwitch"
placeholder="是否开启"
/>
</SettingItem>
</SettingItemBox>
</CollapseItem>
</div>
</template>
<script setup lang="ts">
import { PropType, computed } from 'vue'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { option, orientList, toolTipSwitch } from './config'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option & GlobalThemeJsonType>,
required: true
}
})
const sankeyConfig = computed<typeof option.series>(() => {
return props.optionData.series
})
</script>

View File

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

View File

@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const SankeyConfig: ConfigType = {
key: 'Sankey',
chartKey: 'VSankey',
conKey: 'VCSankey',
title: '桑基图',
category: ChatCategoryEnum.MORE,
categoryName: ChatCategoryEnumName.MORE,
package: PackagesCategoryEnum.CHARTS,
chartFrame: ChartFrameEnum.COMMON,
image: 'sankey.png'
}

View File

@@ -0,0 +1,78 @@
<template>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option" :manual-update="isPreview()" autoresize></v-chart>
</template>
<script setup lang="ts">
import { ref, computed, PropType, watch } from 'vue'
import VChart from 'vue-echarts'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import dataJson from './data.json'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { RadarChart } from 'echarts/charts'
import { includes } from './config'
import { mergeTheme, setOption } from '@/packages/public/chart'
import { useChartDataFetch } from '@/hooks'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { isPreview } from '@/utils'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
const props = defineProps({
themeSetting: {
type: Object,
required: true
},
themeColor: {
type: Object,
required: true
},
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
use([DatasetComponent, CanvasRenderer, RadarChart, GridComponent, TooltipComponent, LegendComponent])
const vChartRef = ref<typeof VChart>()
const option = computed(() => {
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
})
const dataHandle = (dataset: typeof dataJson) => {
if (dataset.label) {
props.chartConfig.option.series.data = dataset.label
}
if (dataset.links) {
props.chartConfig.option.series.links = dataset.links
}
if (dataset.levels) {
props.chartConfig.option.series.levels = dataset.levels
}
if (vChartRef.value && isPreview()) {
setOption(vChartRef.value, props.chartConfig.option)
}
}
watch(
() => props.chartConfig.option.dataset,
newData => {
try {
dataHandle(newData)
} catch (error) {
console.log(error)
}
},
{
deep: true
}
)
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: typeof dataJson) => {
dataHandle(newData)
})
</script>

View File

@@ -4,5 +4,8 @@ import { FunnelConfig } from './Funnel/index'
import { HeatmapConfig } from './Heatmap/index'
import { WaterPoloConfig } from './WaterPolo/index'
import { TreeMapConfig } from './TreeMap/index'
import { DialConfig } from './Dial/index'
import { SankeyConfig } from './Sankey/index'
import { GraphConfig } from './Graph/index'
export default [ProcessConfig, RadarConfig, FunnelConfig, HeatmapConfig, WaterPoloConfig, TreeMapConfig]
export default [ProcessConfig, RadarConfig, FunnelConfig, HeatmapConfig, WaterPoloConfig, TreeMapConfig, GraphConfig, SankeyConfig, DialConfig]

View File

@@ -7,6 +7,22 @@
</n-input-number>
</SettingItem>
</SettingItemBox>
<!-- 中心标题 -->
<SettingItemBox v-if="config.title" name="标题">
<SettingItem name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="config.title.textStyle.color"></n-color-picker>
</SettingItem>
<SettingItem name="字体大小">
<n-input-number
v-model:value="config.title.textStyle.fontSize"
:min="0"
:step="1"
size="small"
placeholder="字体大小"
>
</n-input-number>
</SettingItem>
</SettingItemBox>
<!-- Echarts 全局设置 -->
<SettingItemBox name="进度条">
<SettingItem name="颜色">
@@ -31,24 +47,8 @@
></n-color-picker>
</SettingItem>
</SettingItemBox>
<!-- 中心标题 -->
<SettingItemBox v-if="config.title" name="标题">
<SettingItem name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="config.title.textStyle.color"></n-color-picker>
</SettingItem>
<SettingItem name="字体大小">
<n-input-number
v-model:value="config.title.textStyle.fontSize"
:min="0"
:step="1"
size="small"
placeholder="字体大小"
>
</n-input-number>
</SettingItem>
</SettingItemBox>
<!-- 其他样式 -->
<SettingItemBox name="轨道样式">
<SettingItemBox name="轨道">
<SettingItem name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="item.data[1].itemStyle.color"></n-color-picker>
</SettingItem>
@@ -69,6 +69,18 @@
v-model:value="item.data[1].itemStyle.shadowColor"
></n-color-picker>
</SettingItem>
<SettingItem name="轨道宽度">
<n-select
v-model:value="item.radius[0]"
size="small"
:options="[
{ label: '窄', value: '75%' },
{ label: '中', value: '60%' },
{ label: '宽', value: '45%' },
{ label: '更宽', value: '30%' }
]"
/>
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template>

View File

@@ -41,7 +41,7 @@ const option = reactive({
const dataHandle = (newData: any) => {
const d = parseFloat(`${newData}`) * 100
let config = props.chartConfig.option
config.title.text = d.toFixed(2) + '%'
config.title.text = `${+d.toFixed(2)}%`
config.series[0].data[0].value[0] = d
config.series[0].data[1].value[0] = 100 - d
option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
@@ -68,7 +68,7 @@ watch(
useChartDataFetch(props.chartConfig, useChartEditStore, (resData: number) => {
let d = parseFloat(`${resData}`) * 100
// @ts-ignore
option.value.title.text = d.toFixed(2) + '%'
option.value.title.text = `${+d.toFixed(2)}%`
// @ts-ignore
option.value.series[0].data[0].value[0] = d
// @ts-ignore

View File

@@ -18,7 +18,14 @@ export const PieTypeObject = {
[PieTypeEnum.ROSE]: 'rose'
}
// 其它配置
const otherConfig = {
// 轮播动画
isCarousel: false,
}
const option = {
...otherConfig,
type: 'ring',
tooltip: {
show: true,

View File

@@ -1,88 +1,99 @@
<template>
<!-- Echarts 全局设置 -->
<global-setting :optionData="optionData"></global-setting>
<CollapseItem name="饼图配置" :expanded="true">
<SettingItemBox name="类型">
<SettingItem>
<n-select v-model:value="optionData.type" size="small" :options="fontWeightOptions" />
</SettingItem>
</SettingItemBox>
<SettingItemBox name="标签">
<SettingItem>
<n-space>
<n-switch v-model:value="optionData.series[0].label.show" size="small"></n-switch>
<n-text>展示标签</n-text>
</n-space>
</SettingItem>
<setting-item>
<n-space>
<n-switch v-model:value="optionData.series[0].labelLine.show" size="small"></n-switch>
<n-text>引导线</n-text>
</n-space>
</setting-item>
<SettingItem name="位置">
<n-select v-model:value="optionData.series[0].label.position" size="small" :options="labelConfig.position" />
</SettingItem>
<setting-item name="展示类型">
<n-select v-model:value="optionData.series[0].label.formatter" size="small" :options="labelFormatterOptions" />
</setting-item>
</SettingItemBox>
<setting-item-box name="圆角">
<setting-item>
<n-space>
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderRadius"
size="small"
:min="0"
></n-input-number>
<n-text>圆角大小</n-text>
</n-space>
</setting-item>
<setting-item>
<n-space>
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderWidth"
size="small"
:min="0"
></n-input-number>
<n-text>线条宽度</n-text>
</n-space>
</setting-item>
</setting-item-box>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, watch } from 'vue'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { PieTypeObject, PieTypeEnum } from './config'
import { labelConfig } from '@/packages/chartConfiguration/echarts'
const props = defineProps({
optionData: {
type: Object as PropType<GlobalThemeJsonType>,
required: true
}
})
const fontWeightOptions = [
{
label: PieTypeEnum.NORMAL,
value: PieTypeObject[PieTypeEnum.NORMAL]
},
{
label: PieTypeEnum.RING,
value: PieTypeObject[PieTypeEnum.RING]
},
{
label: PieTypeEnum.ROSE,
value: PieTypeObject[PieTypeEnum.ROSE]
}
]
const labelFormatterOptions = [
{ label: '数据名', value: '{b}' },
{ label: '百分比', value: '{d}' },
{ label: '列名:百分比', value: '{b}:{d}%' }
]
</script>
<template>
<!-- Echarts 全局设置 -->
<global-setting :optionData="optionData"></global-setting>
<CollapseItem name="饼图配置" :expanded="true">
<SettingItemBox name="类型">
<SettingItem>
<n-select v-model:value="optionData.type" size="small" :options="fontWeightOptions" />
</SettingItem>
</SettingItemBox>
<SettingItemBox name="动画" :alone="true">
<SettingItem>
<n-space>
<n-switch v-model:value="optionData.isCarousel" size="small"></n-switch>
<n-text>开启<n-text :depth="3">将自动隐藏图例</n-text></n-text>
</n-space>
</SettingItem>
<SettingItem>
<n-text :depth="3">无鼠标点击图例场景时可强行打开图例</n-text>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="标签">
<SettingItem>
<n-space>
<n-switch v-model:value="optionData.series[0].label.show" size="small"></n-switch>
<n-text>展示标签</n-text>
</n-space>
</SettingItem>
<setting-item>
<n-space>
<n-switch v-model:value="optionData.series[0].labelLine.show" size="small"></n-switch>
<n-text>引导线</n-text>
</n-space>
</setting-item>
<SettingItem name="位置">
<n-select v-model:value="optionData.series[0].label.position" size="small" :options="labelConfig.position" />
</SettingItem>
<setting-item name="展示类型">
<n-select v-model:value="optionData.series[0].label.formatter" size="small" :options="labelFormatterOptions" />
</setting-item>
</SettingItemBox>
<setting-item-box name="圆角">
<setting-item>
<n-space>
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderRadius"
size="small"
:min="0"
></n-input-number>
<n-text>圆角大小</n-text>
</n-space>
</setting-item>
<setting-item>
<n-space>
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderWidth"
size="small"
:min="0"
></n-input-number>
<n-text>线条宽度</n-text>
</n-space>
</setting-item>
</setting-item-box>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, watch } from 'vue'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { PieTypeObject, PieTypeEnum } from './config'
import { labelConfig } from '@/packages/chartConfiguration/echarts'
const props = defineProps({
optionData: {
type: Object as PropType<GlobalThemeJsonType>,
required: true
}
})
const fontWeightOptions = [
{
label: PieTypeEnum.NORMAL,
value: PieTypeObject[PieTypeEnum.NORMAL]
},
{
label: PieTypeEnum.RING,
value: PieTypeObject[PieTypeEnum.RING]
},
{
label: PieTypeEnum.ROSE,
value: PieTypeObject[PieTypeEnum.ROSE]
}
]
const labelFormatterOptions = [
{ label: '数据名', value: '{b}' },
{ label: '百分比', value: '{d}' },
{ label: '列名:百分比', value: '{b}:{d}%' }
]
</script>

View File

@@ -1,64 +1,146 @@
<template>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option" :manual-update="isPreview()" autoresize></v-chart>
</template>
<script setup lang="ts">
import { computed, PropType, reactive, watch } from 'vue'
import VChart from 'vue-echarts'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart } from 'echarts/charts'
import { mergeTheme } from '@/packages/public/chart'
import config, { includes } from './config'
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { isPreview } from '@/utils'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
const props = defineProps({
themeSetting: {
type: Object,
required: true
},
themeColor: {
type: Object,
required: true
},
chartConfig: {
type: Object as PropType<config>,
required: true
}
})
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
use([DatasetComponent, CanvasRenderer, PieChart, GridComponent, TooltipComponent, LegendComponent])
const option = computed(() => {
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
})
watch(
() => props.chartConfig.option.type,
newData => {
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 }
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
</script>
<template>
<v-chart
ref="vChartRef"
autoresize
:init-options="initOptions"
:theme="themeColor"
:option="option"
:manual-update="isPreview()"
@mouseover="handleHighlight"
@mouseout="handleDownplay"
></v-chart>
</template>
<script setup lang="ts">
import { computed, PropType, onMounted, watch } from 'vue'
import VChart from 'vue-echarts'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart } from 'echarts/charts'
import { mergeTheme } from '@/packages/public/chart'
import config, { includes } from './config'
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { isPreview } from '@/utils'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
import dataJson from './data.json'
const props = defineProps({
themeSetting: {
type: Object,
required: true
},
themeColor: {
type: Object,
required: true
},
chartConfig: {
type: Object as PropType<config>,
required: true
}
})
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
let seriesDataNum = -1
let seriesDataMaxLength = 0
let intervalInstance: any = null
use([DatasetComponent, CanvasRenderer, PieChart, GridComponent, TooltipComponent, LegendComponent])
const option = computed(() => {
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
})
// 会重新选择需要选中和展示的数据
const handleSeriesData = () => {
if (seriesDataNum > -1) {
vChartRef.value?.dispatchAction({
type: 'downplay',
dataIndex: seriesDataNum
})
}
seriesDataNum = seriesDataNum >= seriesDataMaxLength - 1 ? 0 : seriesDataNum + 1
vChartRef.value?.dispatchAction({
type: 'highlight',
dataIndex: seriesDataNum
})
}
// 新增轮播
const addPieInterval = (newData?: typeof dataJson, skipPre = false) => {
if (!skipPre && !Array.isArray(newData?.source)) return
if (!skipPre) seriesDataMaxLength = newData?.source.length || 0
clearInterval(intervalInstance)
intervalInstance = setInterval(() => {
handleSeriesData()
}, 1000)
}
// 取消轮播
const clearPieInterval = () => {
vChartRef.value?.dispatchAction({
type: 'downplay',
dataIndex: seriesDataNum
})
clearInterval(intervalInstance)
intervalInstance = null
}
// 处理鼠标聚焦高亮内容
const handleHighlight = () => {
clearPieInterval()
}
// 处理鼠标取消悬浮
const handleDownplay = () => {
if (props.chartConfig.option.isCarousel && !intervalInstance) {
// 恢复轮播
addPieInterval(undefined, true)
}
}
watch(
() => props.chartConfig.option.type,
newData => {
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 }
)
watch(
() => props.chartConfig.option.isCarousel,
newData => {
if (newData) {
addPieInterval(undefined, true)
props.chartConfig.option.legend.show = false
} else {
props.chartConfig.option.legend.show = true
clearPieInterval()
}
}
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: typeof dataJson) => {
addPieInterval(newData)
})
onMounted(() => {
seriesDataMaxLength = dataJson.source.length
if (props.chartConfig.option.isCarousel) {
addPieInterval(undefined, true)
}
})
</script>

View File

@@ -16,6 +16,7 @@ export interface OptionType {
flipperGap: number
flipperType: FlipType
flipperSpeed: number
flipperBorderWidth: number
}
export const option: OptionType = {
@@ -28,7 +29,8 @@ export const option: OptionType = {
flipperRadius: 5,
flipperGap: 10,
flipperType: 'down',
flipperSpeed: 450
flipperSpeed: 450,
flipperBorderWidth: 0,
}
export default class Config extends PublicConfigClass implements CreateComponentType {

View File

@@ -16,12 +16,16 @@
<setting-item name="高度">
<n-input-number v-model:value="optionData.flipperHeight" size="small" :min="1"></n-input-number>
</setting-item>
<setting-item name="间隔">
<n-input-number v-model:value="optionData.flipperGap" size="small" :min="0"></n-input-number>
<setting-item name="边框">
<n-input-number v-model:value="optionData.flipperBorderWidth" size="small" :min="0" :max="10"></n-input-number>
</setting-item>
<setting-item name="圆角">
<n-input-number v-model:value="optionData.flipperRadius" size="small" :min="0"></n-input-number>
</setting-item>
<setting-item name="翻牌间隔">
<n-input-number v-model:value="optionData.flipperGap" size="small" :min="0"></n-input-number>
</setting-item>
<setting-item />
<setting-item name="背景色">
<n-color-picker
size="small"

View File

@@ -9,6 +9,7 @@
:radius="flipperRadius"
:flip-type="flipperType"
:duration="flipperSpeed"
:border-width="flipperBorderWidth"
v-for="(item, index) in flipperData"
:key="index"
class="go-d-block"
@@ -42,7 +43,8 @@ const {
flipperRadius,
flipperGap,
flipperType,
flipperSpeed
flipperSpeed,
flipperBorderWidth
} = toRefs(props.chartConfig.option as OptionType)
const flipperData = ref<string[] | number[]>([])
@@ -61,7 +63,7 @@ watch(
() => props.chartConfig.option,
newVal => {
try {
updateDatasetHandler((newVal as OptionType).dataset)
updateDatasetHandler((newVal as any as OptionType).dataset)
} catch (error) {
console.log(error)
}

View File

@@ -0,0 +1,19 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig } from '@/settings/designSetting'
import { PipelineHConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
export const option = {
color_type: 1,
o_color: '#0a7ae2',
i_color: '#119bfa',
line_class: 'svg_ani_flow'
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = PipelineHConfig.key
public attr = { ...chartInitConfig, w: 500, h: 15, zIndex: -1 }
public chartConfig = cloneDeep(PipelineHConfig)
public option = cloneDeep(option)
}

View File

@@ -0,0 +1,77 @@
<template>
<CollapseItem name="管道" :expanded="true">
<SettingItemBox name="默认颜色">
<SettingItem>
<n-select v-model:value="optionData.color_type" :options="colorOptions" @update:value="handleColorChange" />
</SettingItem>
</SettingItemBox>
<SettingItemBox name="管道颜色">
<SettingItem>
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.o_color"></n-color-picker>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="水流颜色">
<SettingItem>
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.i_color"></n-color-picker>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="流向">
<SettingItem>
<n-select v-model:value="optionData.line_class" :options="options" />
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, ref } from 'vue'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { option } from './config'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true
}
})
const options = ref([
{
value: 'svg_ani_flow',
label: '正向'
},
{
value: 'svg_ani_flow_back',
label: '反向'
},
{
value: 'svg_ani_flow_stop',
label: '停止'
}
])
const colorOptions = ref([
{
value: 1,
label: '蓝'
},
{
value: 2,
label: '黄'
}
])
// 默认颜色
const handleColorChange = (e: number) => {
switch (e) {
case 1:
props.optionData.o_color = '#0a7ae2'
props.optionData.i_color = '#119bfa'
break
case 2:
props.optionData.o_color = '#ff9d00'
props.optionData.i_color = '#f7ea37'
break
}
}
</script>

View File

@@ -0,0 +1,13 @@
import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const PipelineHConfig: ConfigType = {
key: 'PipelineH',
chartKey: 'VPipelineH',
conKey: 'VCPipelineH',
title: '管道-横向',
category: ChatCategoryEnum.MORE,
categoryName: ChatCategoryEnumName.MORE,
package: PackagesCategoryEnum.DECORATES,
image: 'Pipeline_H.png'
}

View File

@@ -0,0 +1,141 @@
<template>
<div class="go-decorates-line">
<svg :width="w" :height="h">
<line :x1="0" :y1="h / 2" :x2="w" :y2="h / 2" :stroke="o_color" :stroke-width="h"></line>
<line :x1="0" :y1="h / 2" :x2="w" :y2="h / 2" :stroke="i_color" :stroke-width="h / 2" :class="line_class"></line>
</svg>
</div>
</template>
<script setup lang="ts">
import { PropType, toRefs } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
const { w, h } = toRefs(props.chartConfig.attr)
const { o_color, i_color, line_class } = toRefs(props.chartConfig.option)
</script>
<style lang="scss" scoped>
.go-decorates-line {
font-size: 0;
}
/* 正向流动效果 */
.svg_ani_flow {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 13, 5;
}
}
@-webkit-keyframes ani_flow {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 13, 5;
}
}
/* 停止流动效果 */
.svg_ani_flow_stop {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow_stop 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow_stop 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
@-webkit-keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
/* 反向流动效果 */
.svg_ani_flow_back {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow_back 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow_back 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow_back {
from {
stroke-dasharray: 13, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
@-webkit-keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
/* 以最大40高度填充 */
.svg_ani_fill_h40 {
animation: ani_fill_h40 5s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_fill_h40 5s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_fill_h40 {
from {
height: 0px;
}
to {
height: 40px;
}
}
@-webkit-keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
</style>

View File

@@ -0,0 +1,19 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig } from '@/settings/designSetting'
import { PipelineVConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
export const option = {
color_type: 1,
o_color: '#0a7ae2',
i_color: '#119bfa',
line_class: 'svg_ani_flow'
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = PipelineVConfig.key
public attr = { ...chartInitConfig, w: 15, h: 500, zIndex: -1 }
public chartConfig = cloneDeep(PipelineVConfig)
public option = cloneDeep(option)
}

View File

@@ -0,0 +1,77 @@
<template>
<CollapseItem name="管道" :expanded="true">
<SettingItemBox name="默认颜色">
<SettingItem>
<n-select v-model:value="optionData.color_type" :options="colorOptions" @update:value="handleColorChange" />
</SettingItem>
</SettingItemBox>
<SettingItemBox name="管道颜色">
<SettingItem>
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.o_color"></n-color-picker>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="水流颜色">
<SettingItem>
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.i_color"></n-color-picker>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="流向">
<SettingItem>
<n-select v-model:value="optionData.line_class" :options="options" />
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, ref } from 'vue'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { option } from './config'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true
}
})
const options = ref([
{
value: 'svg_ani_flow',
label: '正向'
},
{
value: 'svg_ani_flow_back',
label: '反向'
},
{
value: 'svg_ani_flow_stop',
label: '停止'
}
])
const colorOptions = ref([
{
value: 1,
label: '蓝'
},
{
value: 2,
label: '黄'
}
])
// 默认颜色
const handleColorChange = (e: number) => {
switch (e) {
case 1:
props.optionData.o_color = '#0a7ae2'
props.optionData.i_color = '#119bfa'
break
case 2:
props.optionData.o_color = '#ff9d00'
props.optionData.i_color = '#f7ea37'
break
}
}
</script>

View File

@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const PipelineVConfig: ConfigType = {
key: 'PipelineV',
chartKey: 'VPipelineV',
conKey: 'VCPipelineV',
title: '管道-纵向',
category: ChatCategoryEnum.MORE,
categoryName: ChatCategoryEnumName.MORE,
package: PackagesCategoryEnum.DECORATES,
image: 'Pipeline_V.png'
}

View File

@@ -0,0 +1,115 @@
<template>
<div class="go-decorates-line">
<svg :width="w" :height="h">
<line :x1="w / 2" :y1="0" :x2="w / 2" :y2="h" :stroke="o_color" :stroke-width="w"></line>
<line :x1="w / 2" :y1="0" :x2="w / 2" :y2="h" :stroke="i_color" :stroke-width="w / 2" :class="line_class"></line>
</svg>
</div>
</template>
<script setup lang="ts">
import { PropType, toRefs } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
const { w, h } = toRefs(props.chartConfig.attr)
const { o_color, i_color, line_class } = toRefs(props.chartConfig.option)
</script>
<style lang="scss" scoped>
.go-decorates-line {
font-size: 0;
}
/* 正向流动效果 */
.svg_ani_flow {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 13, 5;
}
}
@-webkit-keyframes ani_flow {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 13, 5;
}
}
/* 停止流动效果 */
.svg_ani_flow_stop {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow_stop 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow_stop 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
@-webkit-keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
/* 反向流动效果 */
.svg_ani_flow_back {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow_back 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow_back 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow_back {
from {
stroke-dasharray: 13, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
@-webkit-keyframes ani_flow_back {
from {
stroke-dasharray: 13, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
</style>

View File

@@ -3,5 +3,7 @@ import { TimeCommonConfig } from './TimeCommon/index'
import { ClockConfig } from './Clock/index'
import { CountDownConfig } from './CountDown/index'
import { FlipperNumberConfig } from './FlipperNumber'
import { PipelineHConfig } from './PipelineH/index'
import { PipelineVConfig } from './PipelineV/index'
export default [NumberConfig, FlipperNumberConfig, TimeCommonConfig, CountDownConfig, ClockConfig]
export default [NumberConfig, FlipperNumberConfig, TimeCommonConfig, CountDownConfig, ClockConfig, PipelineHConfig, PipelineVConfig]

View File

@@ -1,4 +1,3 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import {
ArcCurve,

View File

@@ -1,34 +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 }
/**
* 资源文件
* 把模型和图片分开进行加载
*/
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: Record<string, { default: string }> = import.meta.glob("../../images/earth/*", { eager: true })
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 }

View File

@@ -0,0 +1,84 @@
import { IconConfig } from '../Default/Icon/index'
import { PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
const iconNames = [
'uim:adobe',
'uim:airplay',
'uim:align',
'uim:angle-double-down',
'uim:angle-double-left',
'uim:angle-double-right',
'uim:angle-double-up',
'uim:angle-down',
'uim:angle-left',
'uim:angle-right',
'uim:angle-up',
'uim:android-alt',
'uim:apple',
'uim:apps',
'uim:bag',
'uim:battery-bolt',
'uim:bing',
'uim:box',
'uim:briefcase',
'uim:calender',
'uim:chart',
'uim:chart-pie',
'uim:circle-layer',
'uim:clinic-medical',
'uim:clock',
'uim:comment-alt',
'uim:comment-alt-dots',
'uim:docker',
'uim:download-alt',
'uim:dribbble',
'uim:dropbox',
'uim:entry',
'uim:exclamation-circle',
'uim:exclamation-triangle',
'uim:exit',
'uim:facebook',
'uim:facebook-messenger',
'uim:facebook-messenger-alt',
'uim:google-drive',
'uim:google-play',
'uim:graph-bar',
'uim:head-side-mask',
'uim:horizontal-align-left',
'uim:hospital',
'uim:house-user',
'uim:image-v',
'uim:key-skeleton',
'uim:layer-group',
'uim:layers-alt',
'uim:link-h',
'uim:microscope',
'uim:microsoft',
'uim:object-group',
'uim:object-ungroup',
'uim:paypal',
'uim:refresh',
'uim:repeat',
'uim:right-indent-alt',
'uim:rocket',
'uim:shield-plus',
'uim:social-distancing',
'uim:telegram-alt',
'uim:user-md',
'uim:toilet-paper',
'uim:youtube'
]
const iconList = iconNames.map(name => ({
...IconConfig,
category: ChatCategoryEnum.COMMON,
categoryName: ChatCategoryEnumName.COMMON,
package: PackagesCategoryEnum.ICONS,
image: name,
icon: name,
dataset: name,
title: name.replace('uim:', ''),
redirectComponent: `${IconConfig.package}/${IconConfig.category}/${IconConfig.key}` // 跳转组件路径规则packageName/categoryName/componentKey
}))
export default iconList

View File

@@ -0,0 +1,20 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig } from '@/settings/designSetting'
import { IconConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
export const option = {
// 图标名称
dataset: 'uim:apple',
color: '#03A9F4',
size: 64,
rotate: 0 // 旋转角度
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = IconConfig.key
public attr = { ...chartInitConfig, w: 64, h: 64, zIndex: 1 }
public chartConfig = cloneDeep(IconConfig)
public option = cloneDeep(option)
}

View File

@@ -0,0 +1,50 @@
<template>
<collapse-item name="属性" :expanded="true">
<setting-item-box name="样式">
<setting-item name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.color"></n-color-picker>
</setting-item>
<setting-item name="尺寸">
<n-input-number v-model:value="optionData.size" size="small" :min="0" placeholder="尺寸"></n-input-number>
</setting-item>
</setting-item-box>
<setting-item-box name="旋转">
<setting-item name="旋转">
<n-select v-model:value="optionData.rotate" size="small" :options="rotateMode"></n-select>
</setting-item>
</setting-item-box>
</collapse-item>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { option } from './config'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true
}
})
// 旋转方式
const rotateMode = [
{
value: 0,
label: '0°'
},
{
value: 1,
label: '90°'
},
{
value: 2,
label: '180°'
},
{
value: 3,
label: '270°'
}
]
</script>

View File

@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const IconConfig: ConfigType = {
key: 'Icon',
chartKey: 'VIcon',
conKey: 'VCIcon',
title: '图标',
category: ChatCategoryEnum.DEFAULT,
categoryName: ChatCategoryEnumName.DEFAULT,
package: PackagesCategoryEnum.ICONS,
chartFrame: ChartFrameEnum.STATIC,
image: 'icon.png'
}

View File

@@ -0,0 +1,31 @@
<template>
<div class="go-icon-box">
<Icon :icon="((dataset || '') as string)" :color="color" :width="size" :rotate="rotate" />
</div>
</template>
<script setup lang="ts">
import { PropType, toRefs } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { Icon } from '@iconify/vue'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
const { w, h } = toRefs(props.chartConfig.attr)
const { dataset, color, size, rotate } = toRefs(props.chartConfig.option)
</script>
<style lang="scss" scoped>
@include go('icon-box') {
display: flex;
align-items: center;
justify-content: center;
width: v-bind('`${w}px`');
height: v-bind('`${h}px`');
}
</style>

View File

@@ -0,0 +1,45 @@
import { IconConfig } from '../Default/Icon/index'
import { PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
const iconNames = [
'line-md:beer-alt-filled-loop',
'line-md:beer-alt-twotone-loop',
'line-md:cloud-braces-loop',
'line-md:cloud-download-loop',
'line-md:cloud-download-outline-loop',
'line-md:cloud-upload-loop',
'line-md:cloud-upload-outline-loop',
'line-md:coffee-half-empty-twotone-loop',
'line-md:coffee-loop',
'line-md:coffee-twotone-loop',
'line-md:downloading-loop',
'line-md:github-loop',
'line-md:light-dark-loop',
'line-md:loading-alt-loop',
'line-md:loading-loop',
'line-md:loading-twotone-loop',
'line-md:moon-alt-loop',
'line-md:moon-alt-to-sunny-outline-loop-transition',
'line-md:moon-filled-loop',
'line-md:moon-filled-to-sunny-filled-loop-transition',
'line-md:star-pulsating-filled-loop',
'line-md:star-pulsating-loop',
'line-md:star-pulsating-twotone-loop',
'line-md:upload-loop',
'line-md:upload-outline-loop',
'line-md:uploading-loop'
]
const iconList = iconNames.map(name => ({
...IconConfig,
category: ChatCategoryEnum.ML,
categoryName: ChatCategoryEnumName.ML,
package: PackagesCategoryEnum.ICONS,
image: name,
icon: name,
dataset: name,
title: name.replace('line-md:', ''),
redirectComponent: `${IconConfig.package}/${IconConfig.category}/${IconConfig.key}` // 跳转组件路径规则packageName/categoryName/componentKey
}))
export default iconList

View File

@@ -0,0 +1,53 @@
import { IconConfig } from '../Default/Icon/index'
import { PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
const iconNames = [
'wi:cloud',
'wi:cloudy',
'wi:cloudy-gusts',
'wi:cloudy-windy',
'wi:day-cloudy',
'wi:day-cloudy-high',
'wi:day-hail',
'wi:day-haze',
'wi:day-light-wind',
'wi:day-lightning',
'wi:day-rain',
'wi:day-rain-mix',
'wi:day-rain-wind',
'wi:day-showers',
'wi:day-sleet',
'wi:day-sleet-storm',
'wi:day-snow',
'wi:day-snow-thunderstorm',
'wi:day-snow-wind',
'wi:day-sprinkle',
'wi:day-storm-showers',
'wi:day-sunny',
'wi:day-sunny-overcast',
'wi:hail',
'wi:horizon',
'wi:horizon-alt',
'wi:hot',
'wi:lightning',
'wi:night-alt-cloudy',
'wi:night-alt-cloudy-gusts',
'wi:night-alt-cloudy-high',
'wi:night-alt-hail',
'wi:night-alt-lightning',
'wi:umbrella'
]
const iconList = iconNames.map(name => ({
...IconConfig,
category: ChatCategoryEnum.WEATHER,
categoryName: ChatCategoryEnumName.WEATHER,
package: PackagesCategoryEnum.ICONS,
image: name,
icon: name,
dataset: name,
title: name.replace('wi:', ''),
redirectComponent: `${IconConfig.package}/${IconConfig.category}/${IconConfig.key}` // 跳转组件路径规则packageName/categoryName/componentKey
}))
export default iconList

View File

@@ -0,0 +1,13 @@
export enum ChatCategoryEnum {
ML = 'MaterialLine',
COMMON = 'Common',
WEATHER = 'Weather',
DEFAULT = 'Default' // 这个仅用来表示组件分类目录,不要在 index.ts 中导入
}
export enum ChatCategoryEnumName {
ML = '动画',
COMMON = '通用',
WEATHER = '天气',
DEFAULT = '默认'
}

View File

@@ -0,0 +1,5 @@
import MaterialLine from './MaterialLine'
import Common from './Common'
import Weather from './Weather'
export const IconList = [...MaterialLine, ...Weather, ...Common]

View File

@@ -4,7 +4,7 @@ import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig } from '@/settings/designSetting'
import { COMPONENT_INTERACT_EVENT_KET } from '@/enums/eventEnum'
import { interactActions, ComponentInteractEventEnum } from './interact'
import { interactActions, ComponentInteractEventEnum, DefaultTypeEnum, DifferUnitEnum } from './interact'
import { InputsDateConfig } from './index'
export const option = {
@@ -12,7 +12,14 @@ export const option = {
[COMPONENT_INTERACT_EVENT_KET]: ComponentInteractEventEnum.DATE,
// 下拉展示
isPanel: 0,
dataset: dayjs().valueOf()
// 默认值
dataset: dayjs().valueOf() as number | number[] | null,
// 默认值类型
defaultType: DefaultTypeEnum.STATIC,
// 动态默认值偏移单位
differUnit: [DifferUnitEnum.DAY, DifferUnitEnum.DAY],
// 动态默认值偏移值
differValue: [0, 0]
}
export default class Config extends PublicConfigClass implements CreateComponentType {

View File

@@ -1,41 +1,74 @@
<template>
<collapse-item name="展示方式" :expanded="true">
<setting-item-box name="选择方式">
<n-select
v-model:value="optionData.isPanel"
size="small"
:options="panelOptions"
/>
<n-select v-model:value="optionData.isPanel" size="small" :options="panelOptions" />
</setting-item-box>
</collapse-item>
<collapse-item name="时间配置" :expanded="true">
<setting-item-box name="基础">
<setting-item name="类型">
<n-select
v-model:value="optionData.componentInteractEventKey"
size="small"
:options="datePickerTypeOptions"
/>
<n-select v-model:value="optionData.componentInteractEventKey" size="small" :options="datePickerTypeOptions"
@update:value="datePickerTypeUpdate"/>
</setting-item>
</setting-item-box>
<setting-item-box name="默认值" :alone="true">
<n-date-picker
size="small"
v-model:value="optionData.dataset"
:type="optionData.componentInteractEventKey"
/>
<setting-item-box name="默认值">
<setting-item name="类型">
<n-select v-model:value="optionData.defaultType" size="small" :options="defaultTypeOptions"
@update:value="defaultTypeUpdate" />
</setting-item>
</setting-item-box>
<setting-item-box v-if="optionData.defaultType === DefaultTypeEnum.STATIC" :alone="true">
<setting-item name="静态默认值">
<n-date-picker size="small" clearable v-model:value="optionData.dataset" :type="optionData.componentInteractEventKey" />
</setting-item>
</setting-item-box>
<setting-item-box v-if="optionData.defaultType === DefaultTypeEnum.DYNAMIC" >
<template #name>
<n-text></n-text>
<n-tooltip trigger="hover">
<template #trigger>
<n-icon size="21" :depth="3">
<help-outline-icon></help-outline-icon>
</n-icon>
</template>
<span>打开页面时浏览器操作系统的系统时间+偏移量(单位)</span>
</n-tooltip>
</template>
<setting-item :name="differValueName">
<n-input-number v-model:value="optionData.differValue[0]" class="input-num-width" size="small">
<template #suffix>
{{DifferUnitObject[optionData.differUnit[0]]}}
</template>
</n-input-number>
</setting-item>
<setting-item :name="differUnitName">
<n-select v-model:value="optionData.differUnit[0]" size="small" :options="differUnitOptions" />
</setting-item>
<setting-item v-if="isRange" name="结束值动态偏移量">
<n-input-number v-model:value="optionData.differValue[1]" class="input-num-width" size="small">
<template #suffix>
{{DifferUnitObject[optionData.differUnit[1]]}}
</template>
</n-input-number>
</setting-item>
<setting-item v-if="isRange" name="结束值偏移单位">
<n-select v-model:value="optionData.differUnit[1]" size="small" :options="differUnitOptions" />
</setting-item>
</setting-item-box>
</collapse-item>
</template>
<script lang="ts" setup>
import { PropType } from 'vue'
import { PropType, computed } from 'vue'
import { icon } from '@/plugins'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { option } from './config'
import { ComponentInteractEventEnum } from './interact'
import { ComponentInteractEventEnum, DefaultTypeEnum, DifferUnitEnum, DifferUnitObject } from './interact'
import dayjs from "dayjs";
const { HelpOutlineIcon } = icon.ionicons5
@@ -95,4 +128,87 @@ const datePickerTypeOptions = [
value: ComponentInteractEventEnum.QUARTER_RANGE
}
]
const defaultTypeOptions = [
{
label: '静态',
value: DefaultTypeEnum.STATIC
},
{
label: '动态',
value: DefaultTypeEnum.DYNAMIC
},
{
label: '无',
value: DefaultTypeEnum.NONE
}
]
const differUnitOptions = [
// ManipulateType
{
value: DifferUnitEnum.DAY,
label: DifferUnitObject[DifferUnitEnum.DAY]
},
{
value: DifferUnitEnum.WEEK,
label: DifferUnitObject[DifferUnitEnum.WEEK]
},
{
value: DifferUnitEnum.MONTH,
label: DifferUnitObject[DifferUnitEnum.MONTH]
},
{
value: DifferUnitEnum.QUARTER,
label: DifferUnitObject[DifferUnitEnum.QUARTER]
},
{
value: DifferUnitEnum.YEAR,
label: DifferUnitObject[DifferUnitEnum.YEAR]
},
{
value: DifferUnitEnum.HOUR,
label: DifferUnitObject[DifferUnitEnum.HOUR]
},
{
value: DifferUnitEnum.MINUTE,
label: DifferUnitObject[DifferUnitEnum.MINUTE]
},
{
value: DifferUnitEnum.SECOND,
label: DifferUnitObject[DifferUnitEnum.SECOND]
},
{
value: DifferUnitEnum.MILLISECOND,
label: DifferUnitObject[DifferUnitEnum.MILLISECOND]
}
]
const isRange = computed(() => {
return props.optionData.componentInteractEventKey.endsWith('range')
})
const differValueName = computed(() => {
return isRange.value ? '开始值动态偏移量' : '动态偏移量'
})
const differUnitName = computed(() => {
return isRange.value ? '开始值偏移单位' : '偏移单位'
})
const datePickerTypeUpdate = () => {
props.optionData.dataset = isRange.value ? [dayjs().valueOf(), dayjs().valueOf()] : dayjs().valueOf()
}
const defaultTypeUpdate = (v: string) => {
if (v === DefaultTypeEnum.STATIC) {
datePickerTypeUpdate()
} else {
// DefaultTypeEnum.
props.optionData.dataset = null
}
}
</script>

View File

@@ -1,6 +1,7 @@
<template>
<n-date-picker
v-model:value="option.dataset"
clearable
:panel="!!chartConfig.option.isPanel"
:type="chartConfig.option.componentInteractEventKey"
:style="`width:${w}px;`"
@@ -9,13 +10,15 @@
</template>
<script setup lang="ts">
import { PropType, toRefs, ref, shallowReactive, watch } from 'vue'
import dayjs from 'dayjs'
import { computed, PropType, ref, shallowReactive, toRefs, watch } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { useChartInteract } from '@/hooks'
import { InteractEventOn } from '@/enums/eventEnum'
import { ComponentInteractParamsEnum } from './interact'
import {ComponentInteractEventEnum, ComponentInteractParamsEnum, DefaultTypeEnum} from './interact'
import dayjs, {ManipulateType} from 'dayjs'
import quarterOfYear from 'dayjs/plugin/quarterOfYear';
const props = defineProps({
chartConfig: {
@@ -31,43 +34,107 @@ const option = shallowReactive({
dataset: props.chartConfig.option.dataset
})
const isRange = computed(() => {
return props.chartConfig.option.componentInteractEventKey.endsWith('range')
})
// 监听事件改变
const onChange = (v: number | number[]) => {
if (v instanceof Array) {
const onChange = (v: number | number[] | null) => {
if (isRange.value) {
let dateStart = null
let dateEnd = null
let daterange = null
if(v instanceof Array){
dateStart = v[0]
dateEnd = v[1]
daterange = `${v[0]}-${v[1]}`
}
// 存储到联动数据
useChartInteract(
props.chartConfig,
useChartEditStore,
{
[ComponentInteractParamsEnum.DATE_START]: v[0] | dayjs().valueOf(),
[ComponentInteractParamsEnum.DATE_END]: v[1] | dayjs().valueOf(),
[ComponentInteractParamsEnum.DATE_RANGE]: `${v[0]}-${v[1]}`
},
InteractEventOn.CHANGE
props.chartConfig,
useChartEditStore,
{
[ComponentInteractParamsEnum.DATE_START]: dateStart,
[ComponentInteractParamsEnum.DATE_END]: dateEnd,
[ComponentInteractParamsEnum.DATE_RANGE]: daterange
},
InteractEventOn.CHANGE
)
} else {
// 存储到联动数据
useChartInteract(
props.chartConfig,
useChartEditStore,
{ [ComponentInteractParamsEnum.DATE]: v },
InteractEventOn.CHANGE
props.chartConfig,
useChartEditStore,
{ [ComponentInteractParamsEnum.DATE]: v },
InteractEventOn.CHANGE
)
}
}
// 手动更新
watch(
() => props.chartConfig.option.dataset,
(newData: number | number[]) => {
option.dataset = newData
// 关联目标组件首次请求带上默认内容
onChange(newData)
},
{
immediate: true
const getDiffDate = (type: ComponentInteractEventEnum, date: dayjs.Dayjs) => {
// 注册 quarterOfYear 插件
dayjs.extend(quarterOfYear)
switch (type) {
case ComponentInteractEventEnum.DATE:
case ComponentInteractEventEnum.DATE_RANGE:
date = date.startOf('day')
break
case ComponentInteractEventEnum.MONTH:
case ComponentInteractEventEnum.MONTH_RANGE:
date = date.startOf('month')
break
case ComponentInteractEventEnum.YEAR:
case ComponentInteractEventEnum.YEAR_RANGE:
date = date.startOf('year')
break
case ComponentInteractEventEnum.QUARTER:
case ComponentInteractEventEnum.QUARTER_RANGE:
date = date.startOf('quarter')
break
default:
break
}
)
return date
}
watch(
() => {
return {
type: props.chartConfig.option.componentInteractEventKey as ComponentInteractEventEnum,
defaultType: props.chartConfig.option.defaultType as string,
differValue: props.chartConfig.option.differValue as number[],
differUnit: props.chartConfig.option.differUnit as ManipulateType[],
dataset: props.chartConfig.option.dataset as number | number[] | null,
};
},
(newData, oldData) => {
const hasTypeChanged = newData.type !== oldData?.type;
const hasDefaultTypeChanged = newData.defaultType !== oldData?.defaultType;
const hasDifferValueChanged = newData.differValue !== oldData?.differValue;
const hasDifferUnitChanged = newData.differUnit !== oldData?.differUnit;
if (hasTypeChanged || hasDefaultTypeChanged || hasDifferValueChanged || hasDifferUnitChanged) {
if (newData.defaultType === DefaultTypeEnum.NONE) {
props.chartConfig.option.dataset = null;
} else if (newData.defaultType === DefaultTypeEnum.DYNAMIC) {
let date = dayjs();
if (isRange.value) {
props.chartConfig.option.dataset = [
getDiffDate(newData.type,date.add(newData.differValue[0], newData.differUnit[0])).valueOf(),
getDiffDate(newData.type,date.add(newData.differValue[1], newData.differUnit[1])).valueOf(),
];
} else {
props.chartConfig.option.dataset = getDiffDate(newData.type,date.add(newData.differValue[0], newData.differUnit[0])).valueOf()
}
}
}
option.dataset = props.chartConfig.option.dataset;
onChange(option.dataset);
},
{
immediate: true,
}
);
</script>
<style lang="scss" scoped>

View File

@@ -22,6 +22,37 @@ export enum ComponentInteractParamsEnum {
DATE_RANGE = 'daterange'
}
export enum DefaultTypeEnum {
NONE = "none",
STATIC = "static",
DYNAMIC = "dynamic"
}
export enum DifferUnitEnum {
DAY = 'd',
WEEK = 'w',
MONTH = 'M',
QUARTER = 'Q',
YEAR = 'y',
HOUR = 'h',
MINUTE = 'm',
SECOND = 's',
MILLISECOND = 'ms',
}
export const DifferUnitObject = {
// https://day.js.org/docs/en/manipulate/add
[DifferUnitEnum.DAY]: '天',
[DifferUnitEnum.WEEK]: '周',
[DifferUnitEnum.MONTH]: '月',
[DifferUnitEnum.QUARTER]: '季度',
[DifferUnitEnum.YEAR]: '年',
[DifferUnitEnum.HOUR]: '小时',
[DifferUnitEnum.MINUTE]: '分钟',
[DifferUnitEnum.SECOND]: '秒',
[DifferUnitEnum.MILLISECOND]: '毫秒',
}
const time = [
{
value: ComponentInteractParamsEnum.DATE,

View File

@@ -1,8 +1,11 @@
<template>
<collapse-item name="标签页配置" :expanded="true">
<setting-item-box name="默认值" :alone="true">
<setting-item-box name="标签类型" :alone="true">
<n-select size="small" v-model:value="optionData.tabType" :options="tabTypeOptions" />
</setting-item-box>
<setting-item-box name="默认值" :alone="true">
<n-select size="small" v-model:value="optionData.tabLabel" value-field="label" :options="optionData.dataset" />
</setting-item-box>
</collapse-item>
</template>

View File

@@ -1,5 +1,5 @@
<template>
<n-tabs :type="option.value.tabType" @update:value="onChange">
<n-tabs :type="option.value.tabType" @update:value="onChange" :default-value="option.value.tabLabel">
<n-tab v-for="(item, index) in option.value.dataset" :name="item.label" :key="index"> {{ item.label }} </n-tab>
</n-tabs>
</template>

View File

@@ -1,47 +0,0 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { CarouselConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
import logo from '@/assets/logo.png'
// 示例图片资源
const modules = import.meta.globEager("./images/*");
const dataset = [logo]
for (var item in modules) {
dataset.push(modules[item].default)
}
export const option = {
// 图片资源列表
dataset: dataset,
// 自动播放
autoplay: true,
// 自动播放的间隔ms
interval: 5000,
// 每页显示的图片数量
slidesPerview: 1,
// 轮播方向
direction: "horizontal",
// 拖曳切换
draggable: true,
// 居中显示
centeredSlides: false,
// 过渡效果
effect: "slide",
// 是否显示指示点
showDots: true,
// 指示器样式
dotType: "dot",
// 指示器位置
dotPlacement: "bottom",
// 显示箭头
showArrow: false,
// 图片样式
fit: "contain",
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = CarouselConfig.key
public chartConfig = cloneDeep(CarouselConfig)
public option = cloneDeep(option)
}

View File

@@ -1,176 +0,0 @@
<template>
<collapse-item name="属性" :expanded="true">
<setting-item-box name="路径" :alone="true">
<setting-item v-for="item, index in optionData.dataset" :key="index">
<n-input-group>
<n-input v-model:value="optionData.dataset[index]" size="small" placeholder="请输入图片地址"></n-input>
<n-button ghost @click="optionData.dataset.splice(index, 1)">
-
</n-button>
</n-input-group>
</setting-item>
<setting-item>
<n-button size="small" @click="optionData.dataset.push('')">
+
</n-button>
</setting-item>
</setting-item-box>
<setting-item-box name="播放器">
<setting-item>
<n-space>
<n-switch v-model:value="optionData.autoplay" size="small" />
<n-text>自动播放</n-text>
</n-space>
</setting-item>
<!-- 开启自动播放时设置间隔时间 -->
<setting-item name="间隔时间">
<n-input-number v-model:value="optionData.interval" size="small" placeholder=""></n-input-number>
</setting-item>
<setting-item name="轮播方向">
<n-select v-model:value="optionData.direction" :options="directions" placeholder="选择方向" />
</setting-item>
<setting-item name="过渡效果">
<n-select v-model:value="optionData.effect" :options="effects" placeholder="效果" />
</setting-item>
<setting-item name="每页数量">
<n-input-number v-model:value="optionData.slidesPerview" size="small" placeholder=""></n-input-number>
</setting-item>
<setting-item>
<n-space>
<n-switch v-model:value="optionData.centeredSlides" size="small" />
<n-text>居中显示</n-text>
</n-space>
</setting-item>
<setting-item name="图片样式">
<n-select v-model:value="optionData.fit" :options="fitList" placeholder="样式" />
</setting-item>
</setting-item-box>
<setting-item-box name="指示器">
<setting-item name="样式">
<n-select v-model:value="optionData.dotType" :options="dotTypes" placeholder="选择样式" />
</setting-item>
<setting-item name="位置">
<n-select v-model:value="optionData.dotPlacement" :options="dotPlacements" placeholder="选择位置" />
</setting-item>
<setting-item>
<n-space>
<n-switch v-model:value="optionData.showDots" size="small" />
<n-text>显示</n-text>
</n-space>
</setting-item>
<setting-item>
<n-space>
<n-switch v-model:value="optionData.showArrow" size="small" />
<n-text>箭头</n-text>
</n-space>
</setting-item>
<setting-item>
<n-space>
<n-switch v-model:value="optionData.draggable" size="small" />
<n-text>拖曳切换</n-text>
</n-space>
</setting-item>
</setting-item-box>
</collapse-item>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { option } from './config'
import {
CollapseItem,
SettingItemBox,
SettingItem
} from '@/components/Pages/ChartItemSetting'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true
}
})
// 字典
const dotTypes = [
{
label: "点",
value: "dot"
},
{
label: "线",
value: "line"
}
]
const directions = [
{
label: "水平方向",
value: "horizontal"
},
{
label: "垂直方向",
value: "vertical"
}
]
const effects = [
{
label: "slide",
value: "slide"
},
{
label: "fade",
value: "fade"
},
{
label: "card",
value: "card"
},
{
label: "custom",
value: "custom"
}
]
const dotPlacements = [
{
label: "上边",
value: "top"
},
{
label: "下边",
value: "bottom"
},
{
label: "左边",
value: "left"
},
{
label: "右边",
value: "right"
}
]
// 适应类型
const fitList = [
{
value: 'fill',
label: 'fill'
},
{
value: 'contain',
label: 'contain'
},
{
value: 'cover',
label: 'cover'
},
{
value: 'scale-down',
label: 'scale-down'
},
{
value: 'none',
label: 'none'
},
]
</script>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,14 +0,0 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum,ChatCategoryEnumName } from '../../index.d'
export const CarouselConfig: ConfigType = {
key: 'Carousel',
chartKey: 'VCarousel',
conKey: 'VCCarousel',
title: '轮播图',
category: ChatCategoryEnum.MORE,
categoryName: ChatCategoryEnumName.MORE,
package: PackagesCategoryEnum.INFORMATIONS,
chartFrame: ChartFrameEnum.NAIVE_UI,
image: 'photo.png'
}

View File

@@ -1,47 +0,0 @@
<template>
<div>
<n-carousel :autoplay="autoplay" :interval="interval" :centered-slides="centeredSlides" :direction="direction"
:dot-placement="dotPlacement" :dot-type="dotType" :draggable="draggable" :effect="effect"
:slides-per-view="slidesPerview" :show-arrow="showArrow" :show-dots="showDots">
<n-image v-for="url in option.dataset" :object-fit="fit" preview-disabled :src="url"
:fallback-src="requireErrorImg()" :width="w" :height="h"></n-image>
</n-carousel>
</div>
</template>
<script setup lang="ts">
import { PropType, toRefs, shallowReactive, watch } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { requireErrorImg } from '@/utils'
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { option as configOption } from './config'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
const option = shallowReactive({
dataset: configOption.dataset
})
const { w, h } = toRefs(props.chartConfig.attr)
const { autoplay, interval, slidesPerview, direction, draggable, centeredSlides, effect, dotType, dotPlacement, showArrow, showDots, fit } = toRefs(props.chartConfig.option)
watch(
() => props.chartConfig.option.dataset,
(newData: any) => {
option.dataset = newData
},
{
immediate: true,
deep: false
}
)
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
option.dataset = newData
})
</script>

View File

@@ -7,7 +7,8 @@
:fallback-src="requireErrorImg()"
:width="w"
:height="h"
></n-image>
lazy
/>
</div>
</template>

View File

@@ -3,6 +3,5 @@ import { ImageCarouselConfig } from './ImageCarousel/index'
import { IframeConfig } from './Iframe/index'
import { VideoConfig } from './Video/index'
import { WordCloudConfig } from './WordCloud/index'
import { CarouselConfig } from './Carousel/index'
export default [ImageConfig, ImageCarouselConfig, VideoConfig, IframeConfig, WordCloudConfig]

View File

@@ -2,7 +2,7 @@
<collapse-item name="信息" :expanded="true">
<setting-item-box name="文字" :alone="true">
<setting-item>
<n-input v-model:value="optionData.dataset" size="small"></n-input>
<n-input v-model:value="optionData.dataset" type="textarea" size="small"></n-input>
</setting-item>
</setting-item-box>
<setting-item-box name="链接" :alone="true">
@@ -30,11 +30,7 @@
<n-input-number v-model:value="optionData.fontSize" size="small" placeholder="字体大小"></n-input-number>
</setting-item>
<setting-item name="字体粗细">
<n-select
v-model:value="optionData.fontWeight"
size="small"
:options="fontWeightOptions"
/>
<n-select v-model:value="optionData.fontWeight" size="small" :options="fontWeightOptions" />
</setting-item>
<setting-item name="X轴内边距">
<n-input-number v-model:value="optionData.paddingX" size="small" placeholder="输入内边距"></n-input-number>
@@ -87,9 +83,7 @@
<script setup lang="ts">
import { PropType } from 'vue'
import { option, WritingModeEnum, WritingModeObject,
FontWeightEnum,
FontWeightObject, } from './config'
import { option, WritingModeEnum, WritingModeObject, FontWeightEnum, FontWeightObject } from './config'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
const props = defineProps({
optionData: {
@@ -117,13 +111,13 @@ const verticalOptions = [
const fontWeightOptions = [
{
label: FontWeightEnum.NORMAL,
value: FontWeightObject[FontWeightEnum.NORMAL],
value: FontWeightObject[FontWeightEnum.NORMAL]
},
{
label: FontWeightEnum.BOLD,
value: FontWeightObject[FontWeightEnum.BOLD],
},
];
value: FontWeightObject[FontWeightEnum.BOLD]
}
]
const handleLinkClick = () => {
window.open(props.optionData.linkHead + props.optionData.link)
}

View File

@@ -1,8 +1,8 @@
<template>
<div class="go-text-box">
<div class="content">
<span style="cursor: pointer" v-if="link" @click="click">{{ option.dataset }}</span>
<span v-else>{{ option.dataset }}</span>
<span style="cursor: pointer; white-space: pre-wrap" v-if="link" @click="click"></span>
<span style="white-space: pre-wrap" v-else>{{ option.dataset }}</span>
</div>
</div>
</template>
@@ -82,7 +82,7 @@ const click = () => {
border-width: v-bind('borderWidth + "px"');
border-radius: v-bind('borderRadius + "px"');
border-color: v-bind('borderColor');
background-color: v-bind('backgroundColor');
}
}

View File

@@ -0,0 +1,91 @@
import { ChartFrameEnum, ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
import { ImageConfig } from '@/packages/components/Informations/Mores/Image/index'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
import { setLocalStorage, getLocalStorage, goDialog } from '@/utils'
import { StorageEnum } from '@/enums/storageEnum'
import { FileTypeEnum } from '@/enums/fileTypeEnum'
import { backgroundImageSize } from '@/settings/designSetting'
import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
const StoreKey = StorageEnum.GO_USER_MEDIA_PHOTOS
/**
* 上传完成事件类型
*/
type UploadCompletedEventType = {
fileName: string
url: string
}
const userPhotosList: ConfigType[] = getLocalStorage(StoreKey) || []
const uploadFile = (callback: Function | null = null) => {
const input = document.createElement('input')
input.type = 'file'
input.accept = '.png,.jpg,.jpeg,.gif' // 这里只允许部分图片类型
input.onchange = async () => {
if (!input.files || !input.files.length) return
const file = input.files[0]
const { name, size, type } = file
if (size > 1024 * 1024 * backgroundImageSize) {
window['$message'].warning(`图片超出 ${backgroundImageSize}M 限制,请重新上传!`)
return false
}
if (type !== FileTypeEnum.PNG && type !== FileTypeEnum.JPEG && type !== FileTypeEnum.GIF) {
window['$message'].warning('文件格式不符合,请重新上传!')
return false
}
const reader = new FileReader()
reader.onload = () => {
const eventObj: UploadCompletedEventType = { fileName: name, url: reader.result as string }
callback && callback(eventObj)
}
reader.readAsDataURL(file)
}
input.click()
}
const addConfig = {
...ImageConfig,
category: ChatCategoryEnum.PRIVATE,
categoryName: ChatCategoryEnumName.PRIVATE,
package: PackagesCategoryEnum.PHOTOS,
chartFrame: ChartFrameEnum.STATIC,
title: '点击上传图片',
image: 'upload.png',
redirectComponent: `${ImageConfig.package}/${ImageConfig.category}/${ImageConfig.key}`, // 跳转组件路径规则packageName/categoryName/componentKey
disabled: true,
configEvents: {
// 点击上传事件
addHandle: (photoConfig: ConfigType) => {
goDialog({
message: `图片需小于 ${backgroundImageSize}M 且只暂存在浏览器中。当前图片暂存上限5M超过不再缓存新图片请自行对接后端接口现编译成 base64 进行渲染对接后端后请使用【URL地址】进行交互`,
transformOrigin: 'center',
onPositiveCallback: () => {
uploadFile((e: UploadCompletedEventType) => {
// 和上传组件一样配置,更换标题,图片,预设数据
const packagesStore = usePackagesStore()
const newPhoto = {
...ImageConfig,
category: ChatCategoryEnum.PRIVATE,
categoryName: ChatCategoryEnumName.PRIVATE,
package: PackagesCategoryEnum.PHOTOS,
chartFrame: ChartFrameEnum.STATIC,
title: e.fileName,
image: e.url,
dataset: e.url,
redirectComponent: `${ImageConfig.package}/${ImageConfig.category}/${ImageConfig.key}` // 跳转组件路径规则packageName/categoryName/componentKey
}
userPhotosList.unshift(newPhoto)
// 存储在本地数据中
setLocalStorage(StoreKey, userPhotosList)
// 插入到上传按钮前的位置
packagesStore.addPhotos(newPhoto, 1)
})
}
})
}
}
}
export default [addConfig, ...userPhotosList]

View File

@@ -0,0 +1,23 @@
import { ChartFrameEnum, PackagesCategoryEnum } from '@/packages/index.d'
import { ImageConfig } from '@/packages/components/Informations/Mores/Image/index'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
// 远程共享库(调接口获取图像列表)
const imageList = [
{ imageName: 'carousel1', imageUrl: 'https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel1.jpeg' },
{ imageName: 'carousel2', imageUrl: 'https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel2.jpeg' }
]
const photoConfigList = imageList.map(i => ({
...ImageConfig,
category: ChatCategoryEnum.SHARE,
categoryName: ChatCategoryEnumName.SHARE,
package: PackagesCategoryEnum.PHOTOS,
chartFrame: ChartFrameEnum.STATIC,
image: i.imageUrl,
dataset: i.imageUrl,
title: i.imageName,
redirectComponent: `${ImageConfig.package}/${ImageConfig.category}/${ImageConfig.key}` // 跳转组件路径规则packageName/categoryName/componentKey
}))
export default photoConfigList

View File

@@ -0,0 +1,9 @@
export enum ChatCategoryEnum {
PRIVATE = 'Private',
SHARE = 'Share'
}
export enum ChatCategoryEnumName {
PRIVATE = '私有图',
SHARE = '共享图'
}

View File

@@ -0,0 +1,4 @@
import Private from './Private'
import Share from './Share'
export const PhotoList = [...Private, ...Share]

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