Compare commits

..

407 Commits
v2.0 ... v2.7.1

Author SHA1 Message Date
Orange
607eec6c44 perf: 允许用户修改Source 2021-02-02 04:41:54 -06:00
fit2bot
28daf41fb5 fix: 临时调整因为Chrome的兼容性问题导致的日期计算错误 (#607)
* fix: 临时调整因为Chrome的兼容性问题导致的日期计算错误

* fix: 修复工单字段展示问题

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-28 19:28:55 +08:00
fit2bot
e45720af1b fix: 修复工单列表字段展示错误 (#604)
Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-26 18:08:31 +08:00
Jiangjie.Bai
2c69b36291 Merge pull request #598 from jumpserver/dev
Dev
2021-01-21 15:35:46 +08:00
Orange
d267cd1f5e fix: 移除提交应用申请工单时多余的字段 2021-01-21 15:33:02 +08:00
Orange
807e3a407a fix: 移除重复的权限提醒 2021-01-21 15:33:02 +08:00
Orange
1ba790e680 fix: 调整工单主机名字段变成主机名组 2021-01-21 15:33:02 +08:00
Orange
44b701edbc fix: 修复命令过滤规则创建提示有详情的问题 2021-01-21 15:33:02 +08:00
fit2bot
8619ab8bca fix: 修改测试产生的bugs (#596)
* fix: 修改创建录像存储的条件为必填

* fix: 修改最后一次执行的log类型为Ansible

* fix: 修复批量测试资产可连接性的权限问题

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-21 12:43:36 +08:00
Jiangjie.Bai
79e92fa46b Merge pull request #595 from jumpserver/dev
Dev
2021-01-20 19:27:59 +08:00
fit2bot
f19c863440 feat: 添加管理用户列表 (#588)
* feat: 添加管理用户列表

* fix: 整理管理用户详情页面

* fix: 整理管理用户详情页面

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-20 19:07:00 +08:00
fit2bot
fff8b79a45 fix: 修正测试产生的Bugs (#593)
* fix: 调整登录复核工单字段

* fix: 移除创建网关的详情链接展示

* fix: 调整我发起的工单的API字段

* fix: 移除工单申请表单的非必选字段

* fix: 修正创建密码匣子时表单错误

* fix: 调整申请应用时推荐应用对应字段

* fix: 移除旧版本请求

* fix: 优化内部变量写法

* fix: 干掉旧刷新方法

* fix: 优化写法

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-20 17:32:52 +08:00
fit2bot
cf810b3d3e revert: 回滚表格自定义列功能 (#594)
* revert: 回滚表格自定义列功能

* fix: 优化写法

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-20 17:12:04 +08:00
fit2bot
f58e37a76a feat: 添加批量测试资产可连接性功能 (#590)
* feat: 添加批量测试资产可连接性功能

* feat: 添加批量测试资产可连接性功能

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-20 14:56:03 +08:00
ibuler
5889e20aae fix(perms): 修复应用详情中移除系统用户的错误 2021-01-20 14:39:00 +08:00
fit2bot
7caa2c8264 fix: 禁止用户更新用户名与登录名相同选项 (#591)
* fix: 禁止用户更新用户名与登录名相同选项

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-20 11:27:29 +08:00
Jiangjie.Bai
865388dedc Merge pull request #589 from jumpserver/dev
Dev
2021-01-19 19:52:52 +08:00
Orange
35c1077eed fix: 修复新版工单的Bugs 2021-01-19 19:50:35 +08:00
Orange
487e199995 fix: 修复资产列表无克隆创建的bug 2021-01-19 03:31:23 -06:00
fit2bot
f584e96675 fix: 暂时移除应用的克隆创建 (#587)
Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-19 17:29:17 +08:00
fit2bot
1f4f1d3712 fix: 调整Session过期检查 10mins -> 30s (#586)
* fix: 调整Session过期检查 10mins -> 30s

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-19 17:23:02 +08:00
fit2bot
08facb1eda fix: 添加WS链接类型 (#585)
* fix: 添加WS链接类型


Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-19 17:12:52 +08:00
Jiangjie.Bai
34cb9424d4 Merge pull request #582 from jumpserver/dev
Dev
2021-01-17 17:59:42 +08:00
fit2bot
36767cd265 feat: 添加组织详情统计 (#580)
* feat: 添加组织详情统计

* feat: 添加组织详情统计

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-13 20:48:25 +08:00
Orange
86b9fc8f5a fix: 修复工单详情问题 2021-01-13 19:05:50 +08:00
Orange
49054e5dc0 fix: 修复工单详情问题 2021-01-13 19:05:50 +08:00
Orange
bac7cef23c fix: 修复工单详情问题 2021-01-13 19:05:50 +08:00
Orange
4013ea6212 fix: 修复表单生成问题 2021-01-13 18:51:13 +08:00
Orange
37153ebe1d fix: 修复用户界面加载问题 2021-01-13 18:41:51 +08:00
Orange
da35d9be25 修改版权时间 2021-01-12 17:16:33 +08:00
Orange
c3c24b0ad1 feat: 创建资产授权时按协议过滤资产 2021-01-12 17:16:17 +08:00
Orange
98da517724 fix: 移除命令过滤规则的克隆功能 2021-01-12 17:16:04 +08:00
Orange
f002c7f917 feat: 优化重构工单 2021-01-12 15:07:42 +08:00
Orange
0d4e4324ce feat: 重构申请工单系统 2021-01-12 15:07:42 +08:00
Orange
fdeab46970 feat: 重构申请工单系统 2021-01-12 15:07:42 +08:00
Orange
5acbdd5679 feat: 修改录像存储和会话存储接口 2021-01-12 15:07:25 +08:00
Orange
3a64120241 fix: 移除多余的Console.log 2021-01-12 15:04:31 +08:00
Orange
81d1cbf3a1 perf: 修复表格生成Error并记录页码数据 2021-01-12 15:04:31 +08:00
Orange
cec17bbef8 Merge pull request #537 from jumpserver/pr@dev@feat_custom_col_list
feat: 允许用户自定义表格列显示功能
2021-01-12 02:10:29 +08:00
Orange
fbc3373e1b Merge branch 'dev' into pr@dev@feat_custom_col_list 2021-01-12 02:10:02 +08:00
老广
b4a935ab15 Merge pull request #563 from jumpserver/pr@dev@fix_test_storage_notify
fix: 统一测试存储时的命令提醒
2021-01-06 14:33:14 +08:00
老广
7fbff42067 Merge pull request #560 from jumpserver/pr@dev@fix_remove_unuse_func
fix: 移除未使用的函数
2021-01-06 14:08:36 +08:00
老广
5077fec5a8 Merge pull request #564 from jumpserver/pr@dev@fix_create_ticeket
fix: 修复创建工单提示信息
2021-01-06 11:20:27 +08:00
老广
c4619af96f Merge pull request #566 from jumpserver/pr@dev@fix_tree_rightclick
fix: 修复资产树超过屏幕,右键菜单显示不全问题
2021-01-06 11:03:30 +08:00
Orange
025d0abeae fix: 修复资产树超过屏幕,右键菜单显示不全问题 2021-01-05 16:45:22 +08:00
Orange
5735a591ba feat: 允许用户自定义表格列显示功能 2020-12-30 20:08:58 +08:00
Orange
3b664ee1dc feat: 允许用户自定义表格列显示功能 2020-12-30 19:59:23 +08:00
Orange
a3f6de330e fix: 修复创建工单提示信息 2020-12-25 11:15:14 +08:00
Orange
cbc67a5a4c fix: 统一测试存储时的命令提醒 2020-12-23 15:53:44 +08:00
Orange
dec0593907 fix: 移除未使用的函数 2020-12-21 18:23:23 +08:00
Orange
1ed432b1e2 feat: 添加资产编号字段 2020-12-21 14:32:24 +08:00
Jiangjie.Bai
b65664f9c4 Merge pull request #557 from jumpserver/dev
Merge Dev
2020-12-17 18:13:49 +08:00
Orange
f64def0bec fix: 修改箭头颜色 2020-12-17 18:13:09 +08:00
Orange
06f6202bc4 fix: Filter使用后端搜索 2020-12-17 18:13:09 +08:00
Jiangjie.Bai
6fa7800d6b Merge pull request #554 from jumpserver/dev
Chore: Merge Dev
2020-12-17 15:14:11 +08:00
ibuler
54aa252c20 fix(some): 修复没有clone的列表 2020-12-17 15:12:53 +08:00
ibuler
c083f6c4a4 fix(clone): 修复一下clone丢失的问题 2020-12-17 15:12:53 +08:00
Orange
73bb854ebb fix: 去除批量移除, 去除简单写法 2020-12-17 15:03:28 +08:00
Orange
d6f9df277e fix: 修复样式异常和Bug 2020-12-17 15:03:28 +08:00
Jiangjie.Bai
ba78e33f89 Merge pull request #551 from jumpserver/dev
chore: Merge master from dev
2020-12-16 18:42:40 +08:00
Orange
09ef15cff0 fix: 修复工单提示报错 2020-12-16 17:54:38 +08:00
Orange
62d520e625 fix: 修复工单提示报错 2020-12-16 17:54:38 +08:00
Orange
f07a857813 fix: 修复命令过滤器添加系统用户问题 2020-12-16 17:54:38 +08:00
Orange
2699d5e8eb fix: 禁用命令存储文档类型修改 2020-12-16 17:54:38 +08:00
Orange
fb398ca3e4 调整页面errorMessage 2020-12-16 17:54:38 +08:00
Orange
3230c37318 fix: 调整页面保存设置, 优化页面Bug 2020-12-16 17:54:38 +08:00
Jiangjie.Bai
bc258a7ff8 Merge pull request #549 from jumpserver/dev
chore: Merge master from dev
2020-12-15 20:33:14 +08:00
Orange
64d610e282 fix: 修改系统监控页面 2020-12-15 20:32:04 +08:00
Orange
4d95461b5c fix: 添加组件负载状态 2020-12-15 20:32:04 +08:00
Orange
f364c8fdf9 fix: 批量修复2.6版本测试产生的Bug 2020-12-15 20:32:04 +08:00
Orange
88dc2d9271 Merge pull request #547 from jumpserver/dev
fix: 批量修复2.6版本测试产生的Bug
2020-12-14 19:05:38 +08:00
Orange
3aced25da4 fix: 批量修复2.6版本测试产生的Bug 2020-12-14 19:00:16 +08:00
老广
fcf142b696 Merge pull request #544 from jumpserver/dev
Dev
2020-12-11 19:33:54 +08:00
fit2bot
2123037897 fix: 添加组件总量显示 (#543)
Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-11 19:30:58 +08:00
Orange
f5bc2842ec fix: 修复日期显示问题及用户应用授权更新路由问题 2020-12-11 19:28:26 +08:00
Orange
2c95e5f10b fix: 移除中断心跳间隔设置并添加认证方式字段 2020-12-11 19:28:08 +08:00
Jiangjie.Bai
b3ff9c5bcb Merge pull request #539 from jumpserver/dev
chore(merge): dev 合并到 master
2020-12-10 23:47:30 +08:00
Orange
905e5e00b1 fix: 去掉多余JS引用 2020-12-10 23:43:43 +08:00
Orange
81db3d86fa feat: 添加系统监控页面 2020-12-10 23:43:43 +08:00
Orange
ea15515264 feat: 系统用户详情页面增加资产分页面,增加指定资产推送功能 2020-12-10 20:54:14 +08:00
fit2bot
4048a000c7 feat: 添加表格过滤字段选项 (#527)
* feat: 添加表格过滤字段选项

* fix: 如果已有Filter的情况下去掉过滤

Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-10 17:52:12 +08:00
xinwen
a60693c41c perf(asset): 资产树,右击增加计算节点数量的菜单,可以让后台去计算 #527 2020-12-10 17:51:10 +08:00
Orange
06d6c54db8 feat: 允许用户自定义表格列显示功能 2020-12-10 14:26:17 +08:00
fit2bot
57d5c893d3 fix: 修复自动生成表格字段冲突问题 (#536)
Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-09 16:32:52 +08:00
fit2bot
435ce24c75 fix: 修复删除source字段引起的无法更新密码的问题 (#534)
Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-09 16:21:59 +08:00
Orange
4a757bb6bc fix: 不允许用户修改source字段 2020-12-08 20:56:11 +08:00
fit2bot
32fa4f0b11 feat: 资产允许编辑更多信息 (#530)
* feat: 资产允许编辑更多信息

* feat: 资产允许编辑更多资产信息

* feat: 资产允许编辑更多资产信息

* fix: 修改组件名称

Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-08 20:25:54 +08:00
Orange
9f12e1aa18 perf: 禁用应用的导入导出 2020-12-08 11:12:00 +08:00
jym503558564
6165709747 perf(assetBatchOperation): 资产模块添加批量删除操作 2020-12-08 11:10:15 +08:00
fit2bot
0ad1eef196 feat: 添加xlsx格式支持 (#526)
* feat: 添加xlsx格式支持

* feat: 添加xlsx格式支持

* feat: 添加xlsx格式支持

* feat: 添加xlsx格式支持

Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-07 15:22:56 +08:00
Orange
d2d07555b5 fix: 修复批量更新终端存储时附带的保存并继续添加 2020-12-07 14:06:48 +08:00
Orange
7f60224c6d fix: 修复会话时间显示问题 2020-12-07 14:04:26 +08:00
Orange
bbf502c85d fix: 调整select2组件默认长度 2020-12-07 14:03:30 +08:00
Orange
e6aaa52506 perf: 恢复Web终端入口 2020-11-26 12:38:28 +08:00
ibuler
70affacfde fix(list): 修复列表克隆的bug 2020-11-26 12:37:28 +08:00
Orange
40a8da5e58 fix: 修复批量更新组件的问题 2020-11-23 14:00:34 +08:00
Orange
24266bb929 fix: 修复批量批量更新组件的问题 2020-11-22 16:53:01 +08:00
Jiangjie.Bai
14286b961e Merge pull request #514 from jumpserver/dev
Merge dev
2020-11-18 11:49:17 +08:00
Orange
0498db9a8f fix: 修复应用授权过滤系统用户的问题及系统用户创建的问题 2020-11-18 11:47:19 +08:00
ibuler
266d107ffd fix(clone): 修复会话页面产生的clone的问题 2020-11-18 10:55:13 +08:00
ibuler
4f2a9c0c6c fix(clone): 修复会话页面产生的clone的问题 2020-11-18 10:35:40 +08:00
Jiangjie.Bai
affb0ec2bb Merge pull request #509 from jumpserver/dev
Merge Dev
2020-11-17 19:45:03 +08:00
Orange
3075d50357 fix: 修复任务列表字段显示问题 2020-11-17 19:30:36 +08:00
Orange
e49da02c4d fix: 修复RemoteApp详情点击更新失败的问题 2020-11-17 19:07:20 +08:00
Orange
7df5736354 fix: 修改默认版本号以及添加修改应用详情页添加系统用户bug 2020-11-17 18:33:14 +08:00
ibuler
98886149f9 perf(tickets): 优化工单备注 2020-11-17 18:13:40 +08:00
ibuler
abb98d55b9 fix(tickets): 修复工单备注的说明 2020-11-17 18:13:40 +08:00
ibuler
f9c979af88 fix(table): 修复克隆出现在不应该出现的表单上 2020-11-17 18:13:15 +08:00
ibuler
89018a2258 fix(assets|users): 修复批量更新上保存并提交 close #469 2020-11-17 18:13:15 +08:00
Orange
a0c29563ca fix: 显示License过期提示 2020-11-17 16:57:29 +08:00
Orange
21223fddea perf: 优化用户详情中授权的应用列表 2020-11-17 16:54:58 +08:00
Orange
254a2b58cc fix: 用户来源不是本地时禁用更新密码和更新SSH 2020-11-17 16:44:42 +08:00
fit2bot
777c371070 fix: 修复工单提交时必须输入系统用户的提醒 (#503)
* fix: 修复工单提交时必须输入系统用户的提醒

* fix: 去掉多余的console.log

Co-authored-by: Orange <orangemtony@gmail.com>
2020-11-17 16:33:40 +08:00
Orange
0cba2b3116 fix: 修复删除应用的提示框 2020-11-17 16:31:57 +08:00
Orange
c27dd0baef fix: 修复翻译缺失和更新系统用户的表单问题 2020-11-17 16:18:15 +08:00
Orange
990aebefdd Merge pull request #495 from jumpserver/dev
Dev
2020-11-12 16:14:36 +08:00
Orange
6f84312dbe fix: 修改改密计划密码默认长度 2020-11-12 14:26:23 +08:00
Orange
d4c12fb38f fix: 修复CSS和监控禁用逻辑问题 2020-11-12 12:16:49 +08:00
Orange
1bb94824df perf: 添加可中断API字段 2020-11-11 19:21:30 +08:00
Orange
5772430761 fix: 修复翻译和样式Bug 2020-11-11 19:20:53 +08:00
ibuler
790941f361 fix(common): 修复通用form的值的错误 2020-11-11 14:28:43 +08:00
fit2bot
a9ce01ac0e feat: 云管同步增加Azure模块 (#488)
* feat: 云管同步增加Azure模块


Co-authored-by: Orange <orangemtony@gmail.com>
2020-11-10 17:02:20 +08:00
fit2bot
1cdc406e70 fix: 修复生成的表格,不该拥有克隆的,有了克隆 (#490)
* fix: 修复生成的表格,不该拥有克隆的,有了克隆

* fix: 修复生成的表格,不该拥有克隆的,有了克隆

Co-authored-by: ibuler <ibuler@qq.com>
2020-11-10 15:32:11 +08:00
Orange
cb60660272 fix: 修复应用授权翻译 2020-11-10 10:28:30 +08:00
fit2bot
8625e21077 feat(createupdate): 修改和创建的msg增加detail连接 (#487)
* feat(form): 增加可以连续增加的功能

* feat(createupdate): 修改和创建的msg增加detail连接

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Orange <orangemtony@gmail.com>
2020-11-09 15:37:26 +08:00
fit2bot
2251a1653e feat(all): 增加clone创建 (#485)
* perf(lang): 优化语言切换

* perf: 优化命令获取

* feat(all): 增加clone创建

* fix: 修改排序

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Orange <orangemtony@gmail.com>
2020-11-09 15:35:43 +08:00
ibuler
ff1debcbce feat(form): 增加可以连续增加的功能 2020-11-09 12:41:03 +08:00
Orange
17e5564cd7 fix: 修复用户界面字段显示问题 2020-11-08 20:40:18 -06:00
fit2bot
615576b3fd perf(passwordExpireTip): 添加密码过期提醒 (#483)
* perf(passwordExpireTip): 初步实现密码过期提醒

* perf(passwordExpireTip): 添加密码过期提醒

Co-authored-by: jym503558564 <503558564@qq.com>
2020-11-09 10:39:27 +08:00
fit2bot
c0d3fbb47a fix: 修复权限认证问题 (#482)
* fix: 修复权限认证问题

* fix: 修复权限认证问题

Co-authored-by: Orange <orangemtony@gmail.com>
2020-11-05 18:59:08 +08:00
ibuler
09075b13b7 perf: 优化命令获取 2020-11-05 18:57:27 +08:00
ibuler
4e92c1a77c perf(lang): 优化语言切换 2020-11-05 18:57:27 +08:00
fit2bot
7cc49bc907 fix: 添加Tree加载重试组件 (#477)
* fix: 添加409错误信息

* fix(perms): 用户授权树重建冲突时弹出提示信息,并保留之前的树

* fix: 添加Tree加载重试组件

Co-authored-by: Orange <orangemtony@gmail.com>
2020-11-05 10:50:38 +08:00
jym503558564
2e2b5bf873 perf(opsTask): 支持批量删除任务信息 2020-11-05 09:53:21 +08:00
Orange
66b1d17dd2 fix: 用户界面隐藏左侧侧边栏web终端入口 2020-11-05 09:52:32 +08:00
Orange
ee26e47c4d fix: 修复CSS样式错误 2020-11-05 09:51:36 +08:00
Orange
aedf6d2158 fix: 修复组织url拼写错误 2020-11-03 18:15:06 +08:00
Orange
6e848e65b4 feat: RDP系统用户增加AD域名字段 2020-11-03 16:13:21 +08:00
Orange
5c0108906c feat: RDP系统用户增加AD域名字段 2020-11-03 16:13:21 +08:00
fit2bot
6c1f8ec8f7 feat: 整理重构应用模块及应用授权模块 (#465)
* feat: 整理重构应用模块及应用授权模块

Co-authored-by: Orange <orangemtony@gmail.com>
2020-11-03 14:28:36 +08:00
Orange
dda36d2b40 fix: 关闭查询保存参数 2020-10-30 17:48:13 +08:00
peijianbo
1abf30c347 feat:危险命令告警 2020-10-28 21:50:24 -05:00
fit2bot
697b5a3d13 perf(permTranslate): 修改翻译 (#462)
* perf(permTranslate): 修改授权一些翻译

* perf(permTranslate): 修改翻译

Co-authored-by: jym503558564 <503558564@qq.com>
2020-10-29 10:49:29 +08:00
jym503558564
74e4c3397e perf(tableWidth): 修改数据库应用列表的宽度 2020-10-28 21:48:34 -05:00
Orange
c227bf59a6 fix: 修复删除节点下资产授权报错的问题 2020-10-28 21:47:56 -05:00
jym503558564
0092f6d6d7 perf(sessionDetail): 修改session详情中的字段翻译 2020-10-28 21:47:31 -05:00
Orange
747477b27c perf: 隐藏左侧边栏Web终端入口 2020-10-28 21:47:00 -05:00
Orange
4943dab50c perf: 去除多余的console.log() 2020-10-28 21:46:28 -05:00
jym503558564
5cac3ee1f7 perf(changeAuthPlan): 改密计划详情中的资产选择,禁选已存在的资产 2020-10-21 12:51:38 +08:00
jym503558564
fa5a227aff perf(renameNode): 修复重命名节点,同名时有两条错误提示信息的问题 2020-10-21 12:51:22 +08:00
jym503558564
792e8595b8 perf(formatterPassword): 统一密码组件 2020-10-21 12:50:55 +08:00
jym503558564
9d62614ff4 perf(formatterPassword): 统一管理用户、系统用户密码组建 2020-10-21 12:50:55 +08:00
jym503558564
48c0f6e8c6 perf(licenseTip): 修复license过期提醒,开源版本出现split提示问题 2020-10-21 12:50:29 +08:00
jym503558564
31b17b384d perf(safari): 修复表格兼容safari,不错位 2020-10-21 12:49:42 +08:00
jym503558564
858d7a9d6f perf(genericCreateUpdate): 修改漏传attr 2020-10-21 12:49:21 +08:00
jym503558564
48b6c48581 perf(userDetail): 统一用户授权详情列表的宽度 2020-10-21 12:48:54 +08:00
八千流
38be9dd367 Merge pull request #443 from jumpserver/pr@dev@perf_userpage_k8s_detail
feat: 用户详情页面添加K8S应用
2020-10-16 09:07:30 +08:00
Orange
7e5570ad72 feat: 用户详情页面添加K8S应用 2020-10-15 18:54:23 +08:00
Orange
16476caa1e Merge pull request #441 from jumpserver/dev
chore: dev to master
2020-10-15 13:05:35 +08:00
八千流
6a5c28ac26 Merge pull request #442 from jumpserver/pr@dev@fix_command_execution
fix: 修复命令执行选择资产报错问题
2020-10-15 13:02:57 +08:00
Orange
5335faa789 fix: 修复命令执行选择资产报错问题 2020-10-15 13:00:27 +08:00
老广
fd1ee6ef7d Merge pull request #439 from jumpserver/pr@dev@fix_badge_hidden
fix: 如果数量为0 隐藏工单Badge
2020-10-14 22:57:40 -05:00
老广
52d8c34bbf Merge pull request #440 from jumpserver/pr@dev@asset_disabled_style
fix: 调整未激活资产展示样式
2020-10-14 22:57:10 -05:00
Orange
9eac41c0c3 fix: 如果数量为0 隐藏工单Badge 2020-10-15 11:45:10 +08:00
Orange
6881316203 fix: 调整未激活资产展示样式 2020-10-15 11:40:11 +08:00
Orange
d4ee8379e8 fix: 如果数量为0 隐藏工单Badge 2020-10-15 10:37:40 +08:00
老广
f64e877491 Merge pull request #435 from jumpserver/dev
chore: Merge Dev to Master
2020-10-14 07:51:05 -05:00
Orange
f46a63cfcf feat: 禁止未激活资产链接 2020-10-14 07:50:34 -05:00
Orange
65a71df10e fix: 在Default组织下隐藏隐藏邀请用户按钮 2020-10-14 19:05:28 +08:00
jym503558564
fd6b0532ba perf(OrgMember): 修复添加组织成员有更多按钮出现的问题 2020-10-14 18:49:11 +08:00
八千流
e02d05a327 Merge pull request #434 from jumpserver/pr@dev@hidden_userpage_tickets
fix: 隐藏用户界面工单
2020-10-14 17:59:24 +08:00
Orange
23740cdce0 fix: 隐藏用户界面工单 2020-10-14 17:57:59 +08:00
Orange
a50f224227 fix: 修复工单数量为0不显示工单按钮的问题 2020-10-14 17:45:47 +08:00
Orange
f8ec327f11 Merge pull request #432 from jumpserver/dev
chore: merge dev to master
2020-10-14 16:04:36 +08:00
ibuler
1d76e037a4 fix: 修复用户邀请 2020-10-14 15:28:58 +08:00
jym503558564
98f5f38694 perf(licenseTip): 管理员有license过期提醒,其它用户无license过期提醒 2020-10-14 15:10:32 +08:00
Orange
b78b95e67a Merge pull request #419 from jumpserver/dev
chore: merge dev to master
2020-10-13 18:53:29 +08:00
Orange
5f60130952 fix: 修复改密计划创建时定期任务创建冲突的问题 2020-10-13 05:49:40 -05:00
Orange
5a82931fc2 fix: 修复时间选择器显示宽度问题 2020-10-13 05:49:00 -05:00
jym503558564
ad49e3250b perf(licenseExpiredTip): 去掉系统设置里面license过期提醒 2020-10-13 17:57:49 +08:00
jym503558564
7ef95f4567 perf(licenseExporeTip): 初步实现license过期提醒 2020-10-13 17:57:49 +08:00
Orange
25a9d21fd7 fix: 修改Tree的刷新Url 2020-10-13 03:06:00 -05:00
Orange
b849df1dc1 fix: 修复表格的时间过滤问题 2020-10-13 03:00:31 -05:00
Orange
1519ccb8e2 fix: 修改添加用户API 2020-10-12 05:40:14 -05:00
fit2bot
47e88e7bb4 perf(org): 添加组织成员合并成一个card (#422)
* perf(org): 添加组织成员合并成一个card

* perf(org): 添加成员后,object需重新刷新才能重新获取新值

Co-authored-by: jym503558564 <503558564@qq.com>
2020-10-12 10:55:56 +08:00
Orange
c86aef999c fix: 调整badge位置 2020-10-11 21:45:41 -05:00
jym503558564
2f9d7ab826 perf(protocol): 批量更新协议组给默认值 2020-10-11 21:41:42 -05:00
fit2bot
b4311b8a59 perf(permSelectAssetCard): 优化授权详情页中的添加资产,将已添加的资产disable掉 (#405)
* perf(permSelectAssetCard): 优化授权详情页中的添加资产,将已添加的资产disable掉

* perf(permSelectAssetCard): 修改全选时,禁用也被选择的bug

* perf(assetSelect): 修改名称

Co-authored-by: jym503558564 <503558564@qq.com>
2020-10-09 11:05:42 +08:00
Jiangjie.Bai
04a97a9923 Merge pull request #418 from jumpserver/dev
Merge Dev
2020-10-09 10:56:15 +08:00
jym503558564
c7624f9092 perf(relationCard): 优化relationCard以及添加组织成员翻译 2020-10-08 21:08:02 -05:00
fit2bot
fc29fc6c6d feat: 工单入口移动至Header栏 (#417)
* feat: 工单入口移动至Header栏

* feat: 工单入口移动至Header栏

Co-authored-by: Orange <orangemtony@gmail.com>
2020-10-09 10:00:23 +08:00
ibuler
7afc501db5 perf: 修改长度 2020-09-30 03:45:31 -05:00
Orange
8ed5672e95 fix: 修复工单审批时系统用户多选参数配置问题 2020-09-29 19:02:13 +08:00
Orange
951c9f56c5 fix: 修复工单审批时系统用户多选参数配置问题 2020-09-29 19:02:13 +08:00
Orange
2c0e079aa2 fix: 修复工单审批时系统用户多选参数配置问题 2020-09-29 16:35:11 +08:00
jym503558564
a8e7ea9c80 perf(ticket): 优化授权工单,支持填写系统用户,审批时系统用户支持选多个 2020-09-29 15:13:10 +08:00
Orange
30143f833a feat: 添加邀请用户进入组织功能 (#411)
* feat: 添加邀请用户进入组织功能
2020-09-29 15:10:48 +08:00
ibuler
ad88daef9a chore: merge xpack dev pr 2020-09-29 14:26:05 +08:00
ibuler
9eb80eb6ca chore: add package 2020-09-29 14:26:05 +08:00
Orange
112de6e81c feat: 添加403请求拦截器
Closes https://github.com/jumpserver/trello/issues/238
2020-09-28 16:51:57 +08:00
Orange
d01f903a9e fix: 临时禁用树节点移动
Closes https://github.com/jumpserver/lina/issues/383
2020-09-28 16:50:39 +08:00
Orange
af6d0aff7c fix: 使用http-equiv功能禁止index.html缓存 2020-09-28 16:49:02 +08:00
Orange
edf8621e8f fix: 修复版本更新Index.html不刷新的问题 2020-09-28 16:49:02 +08:00
Orange
eeb15c624a fix: 修复组织名称更新列表未刷新的问题 2020-09-28 16:48:15 +08:00
Orange
79e2d49a3d fix: 修复页面title设置不生效问题
Closes https://github.com/jumpserver/trello/issues/302
2020-09-28 16:47:22 +08:00
Orange
628395e447 fix: 修复命令存储更新问题 2020-09-24 15:54:23 +08:00
八千流
6cb6e6444b Merge pull request #394 from jumpserver/pr@dev@fix_command_filter
feat: 修复命令过滤详情添加系统用户的问题
2020-09-22 15:09:19 +08:00
八千流
1b9aa23761 Merge pull request #397 from jumpserver/pe@dev@fix_change_orgs_url
fix: 修复更改组织时路径跳转的问题
2020-09-22 15:08:56 +08:00
jym503558564
537c385ecf perf(licenseExpired): 优化license过期提醒 2020-09-22 15:06:39 +08:00
jym503558564
c0f7d6e7ff perf(translate): 修改界面设置的一些字段翻译 2020-09-22 15:05:41 +08:00
jym503558564
78df96f888 feat(batchUpdateProtocols): 新增批量更新协议组 2020-09-22 15:05:08 +08:00
jym503558564
829957090d fix(gateway): 修复测试网关端口数据类型:String改为Int类型 2020-09-22 15:04:42 +08:00
jym503558564
026c8f37ea perf(navHeader): 在页面右上角添加web终端 2020-09-22 15:04:02 +08:00
Orange
97340c6aac feat: 修复更改组织时路径跳转的问题 2020-09-18 15:18:55 +08:00
Orange
bbe54eae48 fix: 修复Build失败的问题 2020-09-18 11:43:19 +08:00
Orange
9137207055 feat: 修复命令过滤详情添加系统用户的问题 2020-09-17 15:10:30 +08:00
Jiangjie.Bai
0ef30ef651 Merge pull request #392 from jumpserver/dev
Merge Dev
2020-09-17 12:11:23 +08:00
Orange
5ae8a6c9e4 fix: 禁用命令记录列表导出选择项 2020-09-17 12:10:46 +08:00
Orange
ace1dcd0b8 fix: 修复切换组织时权限展示问题
Closes https://github.com/jumpserver/trello/issues/332
2020-09-17 11:06:40 +08:00
Jiangjie.Bai
5df487d6bd Merge pull request #390 from jumpserver/dev
chore: 去掉submodule
2020-09-15 17:21:32 +08:00
ibuler
6e548749e1 Removed submodule 2020-09-15 16:26:11 +08:00
ibuler
99885d9f28 chore: 去掉submodule 2020-09-15 16:16:11 +08:00
Orange
83163e11e3 Merge pull request #387 from jumpserver/dev
Merge dev
2020-09-15 16:02:49 +08:00
Orange
8c191fee67 Merge pull request #386 from jumpserver/pr@dev@feat_hidden_tickets
perf: 优化license的路由判断
2020-09-15 15:17:32 +08:00
ibuler
ff9862fa06 perf: 优化license的路由判断 2020-09-15 15:14:07 +08:00
ibuler
db0cea7051 feat: 可以设置隐藏工单 2020-09-15 14:55:38 +08:00
Orange
305c713a57 Merge pull request #382 from jumpserver/dev
chore: 更新Xpack版本
2020-09-08 20:35:35 +08:00
Orange
8a5f93e268 chore: 更新Xpack版本 2020-09-08 20:34:19 +08:00
Jiangjie.Bai
c4d262150b Merge pull request #374 from jumpserver/dev
Merge dev to master
2020-09-08 20:28:47 +08:00
Jiangjie.Bai
94f161f7e6 Merge pull request #380 from jumpserver/pr@dev@merge_Master
修复Merge冲突
2020-09-08 20:26:52 +08:00
Orange
2c5bfb3f4c 修复Merge冲突 2020-09-08 20:24:35 +08:00
Orange
8e12837a77 fix: 修复请求计时器时间问题 2020-09-08 20:22:29 +08:00
ibuler
4385d84f01 feat: 请求时刷新session age 2020-09-08 20:15:42 +08:00
Orange
214bb28c4c chore: 更新Submodule指向 2020-09-08 20:14:56 +08:00
Orange
14c2285ac8 Merge pull request #375 from jumpserver/pr@master@update_xpack_brench
chore: 更新Submodule指向
2020-09-08 20:11:41 +08:00
Orange
18cbe578f0 chore: 更新Submodule指向 2020-09-08 20:09:09 +08:00
fit2bot
e19ded8365 bug(userDetail): 修复用户详情删除其某条授权规则失败的问题 (#371)
Co-authored-by: jym503558564 <503558564@qq.com>
2020-09-08 19:20:24 +08:00
fit2bot
0150008075 fix: 修复备注过长的显示问题 (#373)
Closes https://github.com/jumpserver/lina/issues/341

Co-authored-by: Orange <orangemtony@gmail.com>
2020-09-08 19:19:20 +08:00
Orange
b0bca65cab fix: 修复工单列表提交的问题
Closes https://github.com/jumpserver/trello/issues/312
2020-09-08 12:17:49 +08:00
jym503558564
cb95b9ba4f perf(assetCreateUpdate): 优化资产创建或更新页面的ip字段翻译 2020-09-07 20:05:25 +08:00
jym503558564
4a840288c5 pref(assetSelect): 优化资产选择表单字段翻译 2020-09-07 20:05:25 +08:00
ibuler
11b1d6638d perf(xpack): 优化license导入 2020-09-07 20:04:07 +08:00
Bai
2225807a36 feat(tickets): 工单添加comment字段 2020-09-07 20:01:22 +08:00
Bai
a8baca81d5 feat(i18n): 添加云同步实例任务的hostname_strategy翻译 2020-09-07 17:52:05 +08:00
jym503558564
30161c7178 perf(pref_ticket_badge): 优化badge的背景色 2020-09-07 17:41:33 +08:00
jym503558564
e222d147f6 pref(tickerBadge): 在ticket页面添加badge 2020-09-07 17:41:33 +08:00
jym503558564
3ddc41707c pref(storage): 修改录像存储的配置 2020-09-03 12:51:47 +08:00
jym503558564
a245055150 pref(storageHelpText): 添加录像存储endpoint提示 2020-09-03 12:51:47 +08:00
Orange
ba5fdf2027 fix: 修复LDAP部分问题 2020-09-03 12:50:50 +08:00
jym503558564
e1999e5ce8 pref(textarea): 优化textarea的minRows为3 2020-09-02 14:23:22 +08:00
jym503558564
be4e0b5e35 pref(formRequired): 统一form required 2020-09-02 14:22:50 +08:00
八千流
82c381b80d Merge pull request #359 from jumpserver/revert-356-pr@dev@pref_ticket_comment
Revert "pref(ticket): 优化工单评论显示,我的回复显示在右边,其它回复均显示在左边"
2020-09-02 10:16:53 +08:00
老广
7d71aa96b9 Revert "pref(ticket): 优化工单评论显示,我的回复显示在右边,其它回复均显示在左边 (#356)"
This reverts commit cea03df4eb.
2020-09-02 10:15:02 +08:00
Orange
93408e52c1 feat: 导入组件时按钮显示加载状态
Closes https://github.com/jumpserver/trello/issues/87
2020-09-01 11:45:59 +08:00
fit2bot
cea03df4eb pref(ticket): 优化工单评论显示,我的回复显示在右边,其它回复均显示在左边 (#356)
* pref(ticket): 优化工单评论显示,我的回复显示在右边,其它回复均显示在左边

* pref(ticket): 优化评论显示

* pref(ticket): 修改方法名

Co-authored-by: jym503558564 <503558564@qq.com>
2020-09-01 11:34:43 +08:00
fit2bot
72ee5f60b9 pref(permission): 资产授权详情页添加动作字段 (#353)
* pref(permission): 资产授权详情页添加动作字段

* pref(permission): 修改翻译

Co-authored-by: jym503558564 <503558564@qq.com>
2020-09-01 11:14:58 +08:00
jym503558564
f6a8e5634b pref(userProfile): 优化ssh公钥textarea字段为自适应高度 2020-09-01 11:00:00 +08:00
Orange
868e77c983 feat: 添加测试网关弹窗
Closes https://github.com/jumpserver/trello/issues/191
2020-09-01 10:57:57 +08:00
jym503558564
2a3fd42ca1 pref(storage): 优化存储表单页面 2020-08-31 13:45:11 +08:00
jym503558564
596a26bfb6 pref(session): 优化会话列表字段翻译 2020-08-31 13:40:19 +08:00
Orange
2fc8cea9ef fix: 修复K8S系统用户更新时令牌是必填项的问题
Closes https://github.com/jumpserver/trello/issues/276
2020-08-28 18:03:20 +08:00
Orange
6f8a5c2bfc fix: 授权Actions组件默认折叠
Closes https://github.com/jumpserver/trello/issues/287
2020-08-28 18:00:46 +08:00
Orange
260901351f 修复Core设置SECURITY_VIEW_AUTH_NEED_MFA失效的问题 2020-08-28 17:59:45 +08:00
Orange
66845e58db 修复Core设置SECURITY_VIEW_AUTH_NEED_MFA失效的问题 2020-08-28 17:59:45 +08:00
OrangeM21
0e9e549bea fix: 修改样例中的端口号 2020-08-20 16:51:46 +08:00
OrangeM21
785611414e fix: 修改样例中的端口号 2020-08-20 16:50:38 +08:00
OrangeM21
19267ee001 fix: 更新Xpack代码 2020-08-20 01:42:47 -05:00
老广
65326916ca chore: merge dev to master (#338)
* fix(systemUser): 修复创建k8s系统用户的问题 (#315)

Co-authored-by: jym503558564 <503558564@qq.com>

* fix(csrfToken): 更新CsrfToken的获取方式,改为从Cookie中获取

* pref(i18n): 修改工单详情页的字段翻译

* fix(systemUserCreate): 优化系统用户创建页面

* fix: 通用导出组件提供默认选项

Closes https://github.com/jumpserver/trello/issues/253

* fix: 修复工单列表的状态展示

Closes https://github.com/jumpserver/trello/issues/198

* fix: 修复工单关闭请求问题

* update

* update

* update

* update

* pref(K8s): 统一Kubernetes翻译

* pref(settings): 优化系统设置提示英文的问题

* fix: 优化创建系统用户时表单联动

Closes https://github.com/jumpserver/trello/issues/180

* fix: 修改翻译

其他 => 其它

Closes https://github.com/jumpserver/trello/issues/181

* fix: 创建用户字段显示隐藏问题

* update

* update

* update

* fix: 修改添加组织表单

* fix: 更新密码后刷新表单

* fix: 修改Xpack指向最新代码

* fix: 修改工单备注提交结构

* fix: 调整工单翻译以及角色顺序

* fix: 更新翻译

* fix: 更新翻译

* fix: 修复Safari时间显示问题

Closes https://github.com/jumpserver/trello/issues/237

* Update common.js

Co-authored-by: fit2bot <68588906+fit2bot@users.noreply.github.com>
Co-authored-by: jym503558564 <503558564@qq.com>
Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-08-20 14:42:11 +08:00
fit2bot
0b925ccf33 pref(xpack): 修改lina master xpack 指向 (#339)
Co-authored-by: jym503558564 <503558564@qq.com>
2020-08-20 14:12:37 +08:00
Orange
58505d2b50 Update common.js 2020-08-19 23:42:17 -05:00
OrangeM21
ecae504a80 fix: 修复Safari时间显示问题
Closes https://github.com/jumpserver/trello/issues/237
2020-08-19 23:42:17 -05:00
OrangeM21
844bc5b44f fix: 更新翻译 2020-08-19 22:22:39 -05:00
OrangeM21
c1dcf82fbd fix: 更新翻译 2020-08-19 22:22:39 -05:00
OrangeM21
336406ddff fix: 调整工单翻译以及角色顺序 2020-08-19 21:51:41 -05:00
OrangeM21
81e8e650bf fix: 修改工单备注提交结构 2020-08-19 09:12:39 -05:00
OrangeM21
af6308e1b3 fix: 修改Xpack指向最新代码 2020-08-19 09:11:34 -05:00
OrangeM21
aae552f374 fix: 更新密码后刷新表单 2020-08-19 08:48:51 -05:00
OrangeM21
b4c1ee786a fix: 修改添加组织表单 2020-08-19 08:48:26 -05:00
OrangeM21
61da88114d update 2020-08-19 07:38:14 -05:00
OrangeM21
279859ce81 update 2020-08-19 07:38:14 -05:00
OrangeM21
820bb075a3 update 2020-08-19 07:38:14 -05:00
OrangeM21
d96bd76ca9 fix: 创建用户字段显示隐藏问题 2020-08-19 07:38:14 -05:00
OrangeM21
7c56c889f2 fix: 修改翻译
其他 => 其它

Closes https://github.com/jumpserver/trello/issues/181
2020-08-19 07:37:25 -05:00
OrangeM21
3547fb26ad fix: 优化创建系统用户时表单联动
Closes https://github.com/jumpserver/trello/issues/180
2020-08-19 07:36:57 -05:00
jym503558564
ac1363b377 pref(settings): 优化系统设置提示英文的问题 2020-08-19 07:36:11 -05:00
jym503558564
6b5c90ee86 pref(K8s): 统一Kubernetes翻译 2020-08-19 07:35:39 -05:00
OrangeM21
8164fa57ef update 2020-08-19 07:34:31 -05:00
OrangeM21
96c9f229e2 update 2020-08-19 07:34:31 -05:00
OrangeM21
58313f5fe0 update 2020-08-19 07:34:31 -05:00
OrangeM21
738a9c3da1 update 2020-08-19 07:34:31 -05:00
OrangeM21
0f10ed9ffc fix: 修复工单关闭请求问题 2020-08-19 07:34:31 -05:00
OrangeM21
294e05cb06 fix: 修复工单列表的状态展示
Closes https://github.com/jumpserver/trello/issues/198
2020-08-19 07:32:19 -05:00
OrangeM21
1598dcbfbc fix: 通用导出组件提供默认选项
Closes https://github.com/jumpserver/trello/issues/253
2020-08-19 07:31:00 -05:00
jym503558564
cf2d6a47c2 fix(systemUserCreate): 优化系统用户创建页面 2020-08-19 07:30:31 -05:00
jym503558564
c8fb334dae pref(i18n): 修改工单详情页的字段翻译 2020-08-19 07:27:55 -05:00
OrangeM21
93dba0bbee fix(csrfToken): 更新CsrfToken的获取方式,改为从Cookie中获取 2020-08-19 07:27:30 -05:00
fit2bot
2832d876fd fix(systemUser): 修复创建k8s系统用户的问题 (#315)
Co-authored-by: jym503558564 <503558564@qq.com>
2020-08-17 10:33:19 +08:00
jym503558564
5cd89cee6a fix(xpack): 更新master xpack 指向 2020-08-14 14:38:29 +08:00
老广
2f69861361 chore: merge to master
Merge Dev to master
2020-08-14 12:26:03 +08:00
fit2bot
8f51d9b0ea fix(remoteApp): 创建mysql_workbench表单添加端口字段 (#307)
Co-authored-by: jym503558564 <503558564@qq.com>
2020-08-13 17:06:22 +08:00
OrangeM21
3a2b6d79fb fix: 去掉API请求的加载条 2020-08-13 15:36:23 +08:00
fit2bot
67ede69685 fix(xpack): 修改Xpack指向 (#305)
Co-authored-by: jym503558564 <503558564@qq.com>
2020-08-13 12:43:50 +08:00
fit2bot
17c4c9b2ef feat(sytemUser): 优化系统用户页面,新增home和groups字段 (#293)
* feat(sytemUser): 优化系统用户页面,新增home和groups字段

* feat(systemUser): 优化创建系统用户页面

* feat(systemUsers): 修改用户组字段

* feat(systemUser): 修改用户组翻译

* fix(systemUser): 去掉必填

* fix(systemUser): 系统用户表单页面,当用户名与用户相同,则username不是必填

Co-authored-by: jym503558564 <503558564@qq.com>
2020-08-12 18:19:32 +08:00
fit2bot
0319d43942 perf(kubernetes): 优化kubernetes更新页面翻译 (#304)
Co-authored-by: jym503558564 <503558564@qq.com>
2020-08-12 17:57:19 +08:00
jym503558564
dcd088fd58 perf(kubernetes): 优化kubernetes页面 2020-08-12 14:31:23 +08:00
jym503558564
9875ded710 perf(listTable): 优化ip字段宽度,以及修改kubernetes apps翻译 2020-08-12 14:31:23 +08:00
jym503558564
e26cd95ef9 perf(detailCard): 优化详情页bool值字段的显示 2020-08-12 12:22:53 +08:00
fit2bot
1bb8e8c709 fix: 创建K8S系统用户时允许填写username (#301)
* fix: 创建K8S系统用户时允许填写username


Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-08-11 19:51:44 +08:00
OrangeM21
09617fa606 feat: 添加用户界面K8S应用 2020-08-11 16:45:39 +08:00
fit2bot
62a6d11332 perf: 优化用户列表和用户创建的角色 (#291)
* perf: 优化用户角色

Closes https://github.com/orgs/jumpserver/projects/1

* perf: 优化用户角色

Closes https://github.com/orgs/jumpserver/projects/1

Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-08-11 11:06:22 +08:00
fit2bot
8f00dbf23e feat(kubenetes): 添加kubenetes应用 (#292)
* feat(kubenetes): 添加kubenetes应用

* feat(Kubernetes): 添加K8S应用支持

* feat(Kubernetes): 添加K8S应用支持

Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-08-11 11:01:14 +08:00
jym503558564
8fd624e0b7 fix(opsTask): 去掉任务详情中多余的ID字段 2020-08-11 10:32:25 +08:00
jym503558564
cef6521a2b fix(userCreateUpdate): 修复创建用户时生成密码的问题 2020-08-10 15:10:07 +08:00
OrangeM21
da1217972a fix: 修正创建用户时生成密码的问题 2020-08-07 12:11:43 +08:00
OrangeM21
9efacb68b6 fix: 修复工单提交时候方法冲突问题及删掉多余函数 2020-08-07 12:10:52 +08:00
fit2bot
b3c22f96d8 perf(tagSearch): 优化搜索组件增加编辑选项功能 (#286)
* perf(tagSearch): 优化搜索组件增加编辑选项功能

* 优化Tag细节

* 优化Tag细节

Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-08-07 12:10:13 +08:00
OrangeM21
6bf15655b7 perf(ticket): 优化工单细节
Closes https://github.com/orgs/jumpserver/projects/1
2020-08-07 12:02:03 +08:00
jym503558564
ee3dc30985 feat(xpackOrg): 添加组织成员列表页面的翻译 2020-08-06 17:41:11 +08:00
OrangeM21
21da017f8e fix: 修复上传组件模板下载失败问题 2020-08-06 17:40:21 +08:00
OrangeM21
38b4810d9e perf(sass): 替换node-sass为sass
BREAKING CHANGE: 替换node-sass为sass
2020-08-04 13:42:03 +08:00
fit2bot
d49aae69ab fix(Tickets): 调整工单详情样式 (#281)
* fix(Tickets): 调整工单详情样式

Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-08-04 13:40:08 +08:00
OrangeM21
012fefa3ea fix(zTree): 修复zTree销毁冲突问题
Closes https://github.com/jumpserver/lina/issues/280
2020-08-04 11:41:48 +08:00
fit2bot
1f91b9a72f perf(Router): 优化外链路由 (#268)
* perf(Router): 优化外链路由

* perf(Router): 优化外链路由

* 替换BASEURL为BASE_URL

Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-07-31 11:57:04 +08:00
八千流
e061d9eb75 Merge pull request #277 from jumpserver/pr@dev@hotfix_build_error
fix(ticket): 修复Merge冲突造成的编译问题
2020-07-31 11:15:48 +08:00
OrangeM21
00cd04e103 fix(ticket): 修复Merge冲突造成的编译问题 2020-07-31 11:12:31 +08:00
Orange
4b3b1a723f feat: 添加申请资源工单功能 (#185)
* [Feature] 添加申请资产工单

* feat: 添加资产申请工单功能

* update

* feat: 添加申请资源工单功能

* feat: 添加申请资源工单功能

* feat: 添加申请资源工单功能

* feat: 添加申请资源工单功能

* fix(终端列表): 还原终端列表的代码

* fix: 修改申请资源工单功能

* fix: 修改申请资源工单功能

* fix: 修改申请资源工单功能

* feat: 添加请求资产权限工单

* Update cn.json

* Update en.json

Co-authored-by: xinwen <coderWen@126.com>
2020-07-31 10:51:17 +08:00
OrangeM21
2d3a43c202 fix(dialog): 修复当有搜索条件时禁用导出所有选项
Closes https://github.com/jumpserver/trello/issues/74
2020-07-31 10:49:22 +08:00
fit2bot
e8e751668d feat(terminalStorage): 添加批量更新终端存储功能 (#247)
* feat(terminalStorage): 添加批量更新终端存储功能

Closes https://github.com/jumpserver/jumpserver/issues/4392
Closes https://github.com/jumpserver/jumpserver/issues/4172

* update

* update

Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-07-31 10:46:57 +08:00
OrangeM21
c6e0a17aaa fix: 修复创建远程应用密码框显示明文的问题
Closes https://github.com/jumpserver/trello/issues/133
2020-07-31 10:44:30 +08:00
OrangeM21
066d81446c fix(preload): 开启Preload
开启preload,提高首屏加载速度
2020-07-31 10:37:13 +08:00
fit2bot
a63b07cf2e feat: 个人界面添加更新公钥快捷按钮 (#271)
* feat: 个人界面添加更新公钥快捷按钮

Closes https://github.com/jumpserver/jumpserver/issues/4361

* feat: feat: 个人界面添加更新公钥快捷按钮

Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-07-31 10:32:26 +08:00
jym503558564
0ab96fa413 fix(detailCard): 修复详情页数据如果没有ID字段,则不显示ID 2020-07-31 10:30:27 +08:00
ibuler
e064c1cfc4 ci(docker): 使用docker缓存,使用npm mirror 2020-07-29 16:07:15 +08:00
jym503558564
2913699597 fix(tableList): 优化列表组件一些字段的宽度 2020-07-28 16:04:33 +08:00
jym503558564
c413623a22 fix(sessions_cmd_storage): 优化创建命令存储的提示文案 2020-07-28 16:03:15 +08:00
jym503558564
92f08605df fix(sessions_cmd_storage): 优化创建命令存储的提示文案 2020-07-28 16:03:15 +08:00
jym503558564
3fb32ad81c fix(sessions_cmd_storage): 优化创建命令存储的提示文案 2020-07-28 16:03:15 +08:00
jym503558564
760dbad5ac fix(ticket): 优化工单详情 2020-07-28 16:00:58 +08:00
jym503558564
878933bc07 fix(ticket): 优化工单详情 2020-07-28 16:00:58 +08:00
Orange
603ebff771 Merge pull request #217 from jumpserver/pr_dev_i18n_xpack_cloud
feat(AddTranslate): 添加云管账户详情翻译
2020-07-28 15:57:28 +08:00
jym503558564
c23ed70df4 fix(xpack):修改xpack指向 2020-07-28 15:56:18 +08:00
jym503558564
d21598cf1c fix(xpack): 更新xpack指向 2020-07-27 17:41:14 +08:00
OrangeM21
34f1b5d662 fix(CommandExecution): 修复命令过滤执行的系统用户选择 2020-07-24 11:32:37 +08:00
jym503558564
c493aca11b fix(assets): 优化资产详情中网域字段名的显示 2020-07-24 11:01:55 +08:00
jym503558564
cc937d600b fix(permissions): 优化授权列表各列宽度 2020-07-24 10:58:48 +08:00
OrangeM21
fcbe61cb92 fix(el-data-table): 修复翻页搜索结果不正确的问题
Closes https://github.com/jumpserver/trello/issues/69
Closes https://github.com/jumpserver/trello/issues/68
2020-07-24 10:53:26 +08:00
jym503558564
b39f01a023 fix(detailCard): detailCard 增加ID字段 2020-07-24 10:48:43 +08:00
jym503558564
794ad35f84 fix(sessionDetailTimeFormat): 优化会话详情中时间字段的显示格式 2020-07-24 10:46:32 +08:00
fit2bot
729b07798e feat(Clipboard): 添加剪切板权限控制 (#238)
Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-07-24 10:44:47 +08:00
fit2bot
f2514f68b8 fix(select2): 默认添加clearable属性 (#240)
* fix(select2): 默认添加clearable属性


Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-07-24 10:35:41 +08:00
fit2bot
30d4044d20 fix(perms): 优化授权列表添加刷新按钮 (#245)
* fix(perms): 优化授权列表添加刷新按钮

* fix(perms): 优化授权列表添加导入导出按钮

Co-authored-by: jym503558564 <503558564@qq.com>
2020-07-24 10:20:37 +08:00
jym503558564
e7cd6e49e8 fix(Xpack): 更新xpack指向 2020-07-21 15:45:03 +08:00
OrangeM21
75276d37e4 fix: 添加form组件的refs 2020-07-21 15:04:24 +08:00
Orange
5a92c4f3ee Merge pull request #228 from jumpserver/pr@dev@ci_add_generic_handler
ci(github): 添加通用action
2020-07-21 13:08:36 +08:00
Orange
ae6fb22fae Merge pull request #229 from jumpserver/pr@master@ci_add_generic_handler
ci(github): 添加通用action
2020-07-21 13:07:55 +08:00
github-actions
ccd98606a1 ci(github): 添加通用action 2020-07-21 05:06:02 +00:00
github-actions
7f2ed5d038 ci(github): 添加通用action 2020-07-21 05:05:59 +00:00
jym503558564
d24741ab4b feat(AddTranslate): 添加云管账户详情翻译 2020-07-20 11:38:19 +08:00
Orange
9ac5eabff2 Merge pull request #212 from jumpserver/pr_dev_ci-change
ci(pull-request): 添加自动pr打标签功能
2020-07-17 16:14:52 +08:00
ibuler
18b5fafa41 ci(pull-request): 添加自动pr打标签功能 2020-07-17 16:10:02 +08:00
Orange
8a76bb05ac Merge pull request #208 from jumpserver/jym_bug_fix
fix(settings): 修复安全设置页面更新不了的问题
2020-07-16 15:49:18 +08:00
Orange
ad5b5c7c20 Merge pull request #207 from jumpserver/jym_bug_fix
fix(settings): 修复安全设置页面更新不了的问题
2020-07-16 15:48:40 +08:00
jym503558564
fe7d43f669 fix(settings): 修复安全设置页面更新不了的问题 2020-07-16 15:35:42 +08:00
八千流
aad23f3de5 Merge pull request #202 from jumpserver/dev
merge(master): Merge from dev to master
2020-07-16 11:12:33 +08:00
八千流
9c33093b01 Merge pull request #205 from jumpserver/update_xpack
update:Xpack
2020-07-16 11:11:00 +08:00
OrangeM21
c94a5dfa1f update:Xpack 2020-07-16 11:08:24 +08:00
八千流
143973531c Merge pull request #204 from jumpserver/update_xpack_master
fix: 更新Master Xpack代码
2020-07-16 11:05:29 +08:00
OrangeM21
7db590950c fix: 更新Master Xpack代码 2020-07-16 11:04:13 +08:00
八千流
7e65a52062 Merge pull request #203 from jumpserver/update_xpack
fix: 更新Xpack代码
2020-07-16 11:01:05 +08:00
OrangeM21
226d118d28 fix: 更新Xpack代码 2020-07-16 10:58:49 +08:00
老广
0e3bd186fe Merge pull request #201 from jumpserver/v2.0
Merge v2.0
2020-07-16 10:44:27 +08:00
八千流
50e8dfc86d fix(timeFormat): 统一时间格式 (#197)
* fix(timeFormat): 统一时间格式

* fix: 更新Xpack代码

Co-authored-by: OrangeM21 <orangemtony@gmail.com>
2020-07-15 20:46:01 +08:00
OrangeM21
a023e03074 fix: 更新Xpack代码&&用户界面资产添加备注选项
Closes https://github.com/jumpserver/trello/issues/75
2020-07-15 20:43:39 +08:00
OrangeM21
a0a592f064 fix(AssetUpdate): 创建资产网域选项可清空
Closes https://github.com/jumpserver/lina/issues/141
2020-07-15 20:19:15 +08:00
八千流
4d86edd65e fix(ldapUserListFix): 优化ldap用户列表勾选后关闭页面,再次打开时,上一次选中的状态还存在的问题 (#192)
* fix(ldapUserListFix): 优化ldap用户列表勾选后关闭页面,再次打开时,上一次选中的状态还存在的问题
2020-07-15 18:34:17 +08:00
Orange
c597bc1dca fix(SessionList): 修复命令详情参数及在线会话快捷操作组件 (#198)
* fix(SessionList): 修复命令详情参数及在线会话快捷操作组件

Closes https://github.com/jumpserver/trello/issues/72

* fix: 更新API参数
2020-07-15 18:31:13 +08:00
OrangeM21
a3d45fd4b9 fix(UserProfile): 修复登录密码后没有立即提示退出登录等问题
Closes https://github.com/jumpserver/trello/issues/23
2020-07-15 18:23:00 +08:00
jym503558564
e16d3c2e3f fix(changePasswd): 添加changePassword创建页面中的翻译 2020-07-15 18:21:17 +08:00
OrangeM21
e251127f6e fix(AssetUserTable): 去掉删除资产的前端提示,改为由后端提示
Closes https://github.com/jumpserver/trello/issues/30
2020-07-15 18:19:20 +08:00
jym503558564
58b8917739 fix(ldap): 修复LDAP一键导入不选择用户,导入了全部用户的问题 2020-07-15 18:18:26 +08:00
八千流
ad423e921a fix(highlight): 修复创建资源后跳转到列表页世,左侧菜单没有高亮问题 (#191) 2020-07-15 17:54:11 +08:00
OrangeM21
b82231f3ea fix(Userlist): 添加禁止更新和禁止删除的权限判断
Closes https://github.com/jumpserver/trello/issues/29
2020-07-15 17:49:48 +08:00
Orange
f976800cde fix: 修改弹窗按钮高亮提示 (#187) 2020-07-15 17:47:22 +08:00
OrangeM21
4973c62618 fix: 用户来源不为Local的时候禁止更新密码
Closes https://github.com/jumpserver/trello/issues/44
2020-07-15 17:46:04 +08:00
Orange
786528f9b2 Merge pull request #184 from jumpserver/remote_permission_fix
fix(remote_app_permission): 修复创建远程应用页面系统用户的获取
2020-07-09 18:29:19 +08:00
Orange
661409b99d Merge pull request #183 from jumpserver/remote_permission_fix
fix(remote_app_permission): 修复创建远程应用页面系统用户的获取
2020-07-09 18:29:04 +08:00
ibuler
702bb3acfa ci(build): 修改构建,使用严格模式 2020-07-09 17:50:05 +08:00
ibuler
8c6d2a1150 ci(build): 修改构建,使用严格模式 2020-07-09 17:29:56 +08:00
ibuler
1295fb7fd2 ci(docker): 修改docker构建 2020-07-09 16:35:55 +08:00
BaiJiangJie
d3fbb9a391 Merge pull request #177 from jumpserver/ci
ci(docker): 修改docker构建
2020-07-09 16:31:23 +08:00
ibuler
2b14cf0225 ci(docker): 修改docker构建 2020-07-09 16:25:42 +08:00
BaiJiangJie
a78f8a3633 Merge pull request #176 from jumpserver/dev
merge: Merge to master from branch dev
2020-07-09 16:01:19 +08:00
何去何从
c4868dabac fix: 优化资产树 mini-button 添加 cursor:pointer 2020-07-09 15:40:41 +08:00
BaiJiangJie
faf848dca5 Merge pull request #175 from jumpserver/ci
fix(Docker): 修改Dockerfile,统一使用build.sh构建
2020-07-09 15:40:19 +08:00
ibuler
05edffe173 fix(Docker): 修改Dockerfile,统一使用build.sh构建 2020-07-09 15:35:44 +08:00
Orange
4b3862443a Merge pull request #173 from jumpserver/master_merge
fix: lina dev中xpack的commit号指向xpack master
2020-07-09 14:20:31 +08:00
Orange
536ebd7513 Merge pull request #172 from jumpserver/master_merge
fix: lina master中xpack的commit号指向xpack master
2020-07-09 14:16:59 +08:00
jym503558564
7dce39d79c fix: lina master中xpack的commit号指向xpack master 2020-07-09 14:14:06 +08:00
八千流
a8389304d6 Merge pull request #171 from jumpserver/master
Merge Master 代码
2020-07-09 13:53:18 +08:00
Orange
649d4ac848 Merge pull request #170 from jumpserver/v2.0
feat: 更新Master代码
2020-07-09 13:44:11 +08:00
八千流
86b5cb81fc Merge pull request #168 from jumpserver/v2.0
feat: 更新Master代码
2020-07-08 17:30:03 +08:00
八千流
1ffcf9e7b4 Merge pull request #163 from jumpserver/v2.0
revert: 撤销网域显示ID的修复
2020-07-08 14:02:27 +08:00
Orange
a665d6ed20 Merge pull request #161 from jumpserver/v2.0
ci(fix): 修改ci使用的action为node10
2020-07-08 12:03:17 +08:00
Orange
47c05922ae Merge pull request #159 from jumpserver/v2.0
fix(many): v2.0中修改的bug Merge到master
2020-07-08 11:51:06 +08:00
八千流
7db080b418 Merge pull request #114 from jumpserver/fix_bugs_orange
fix:修复资产和前端bugs
2020-07-01 15:24:01 +08:00
老广
57e4b65059 Merge pull request #100 from jumpserver/dev
[hotfix]更改路由权限验证模块
2020-06-22 18:42:42 +08:00
Orange
5aa28903dd Merge pull request #99 from jumpserver/dev
[feature] v2.0 Release
2020-06-18 11:04:32 +08:00
259 changed files with 10309 additions and 2516 deletions

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "src/views/xpack"]
path = src/views/xpack
url = git@github.com:jumpserver/lina-xpack.git

View File

@@ -46,7 +46,7 @@ server {
## License & Copyright
Copyright (c) 2014-2020 飞致云 FIT2CLOUD, All rights reserved.
Copyright (c) 2014-2021 飞致云 FIT2CLOUD, All rights reserved.
Licensed under The GNU General Public License version 2 (GPLv2) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

View File

@@ -2,8 +2,8 @@ server {
listen 80;
location /ui/ {
try_files $uri / /ui/index.html;
alias /opt/lina/;
try_files $uri / /ui/index.html;
alias /opt/lina/;
}
location / {

View File

@@ -20,6 +20,7 @@
"dependencies": {
"@ztree/ztree_v3": "3.5.44",
"axios": "0.18.1",
"axios-retry": "^3.1.9",
"deepmerge": "^4.2.2",
"echarts": "^4.7.0",
"element-ui": "2.13.2",
@@ -82,8 +83,8 @@
"less-loader": "^5.0.0",
"lint-staged": "^10.1.2",
"mockjs": "1.0.1-beta3",
"node-sass": "^4.9.0",
"runjs": "^4.3.2",
"sass": "^1.26.10",
"sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "0.7.2",

View File

@@ -3,6 +3,10 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="Expires" content="0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache" content="no-cache">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= webpackConfig.name %></title>

8
src/api/ticket.js Normal file
View File

@@ -0,0 +1,8 @@
import request from '@/utils/request'
export function getTicketOpenCount(assign) {
return request({
url: `/api/v1/tickets/tickets/?assignees__id=${assign}&status=open&offset=0&limit=15&display=1&draw=1/`,
method: 'get'
})
}

View File

@@ -65,3 +65,7 @@ export function logout() {
method: 'post'
})
}
export function refreshSessionIdAge() {
return getProfile()
}

View File

@@ -1,14 +1,14 @@
<template>
<div :class="grouped ? 'el-button-group' : ''">
<el-button v-for="item in iActions" :key="item.name" :size="size" v-bind="item" @click="handleClick(item.name)">
<el-tooltip v-if="['actionExport', 'actionImport', 'actionRefresh'].indexOf(item.name) !== -1" effect="dark" :content="item.tip" placement="top">
<el-tooltip v-if="item.tip" effect="dark" :content="item.tip" placement="top">
<i v-if="item.fa" :class="'fa ' + item.fa" />{{ item.title }}
</el-tooltip>
<span v-else>
<i v-if="item.fa" :class="'fa ' + item.fa" />{{ item.title }}
</span>
</el-button>
<el-dropdown v-if="iMoreActions.length > 0" trigger="click" @command="handleClick">
<el-dropdown v-if="iMoreActions.length > 0" trigger="click" :placement="moreActionsPlacement" @command="handleClick">
<el-button :size="size" :type="moreActionsType" class="btn-more-actions">
{{ iMoreActionsTitle }}<i class="el-icon-arrow-down el-icon--right" />
</el-button>
@@ -52,6 +52,11 @@ export default {
moreActionsType: {
type: String,
default: 'default'
},
moreActionsPlacement: {
type: String,
default: 'bottom'
// 居中对齐
}
},
computed: {
@@ -114,6 +119,11 @@ export default {
if (!has) {
continue
}
// 是否有分割线
const divided = this.checkItem(action, 'divided', false)
delete action['divided']
action.divided = divided
// 是否是disabled
const can = this.checkItem(action, 'can')
delete action['can']

View File

@@ -3,7 +3,7 @@
<table style="width: 100%">
<tr>
<td colspan="2">
<AssetSelect ref="assetSelect" />
<AssetSelect ref="assetSelect" :can-select="canSelect" />
</td>
</tr>
<tr>
@@ -49,6 +49,12 @@ export default {
onAddSuccess: {
type: Function,
default: (objects, that) => {}
},
canSelect: {
type: Function,
default(row, index) {
return true
}
}
},
data() {

View File

@@ -34,6 +34,12 @@ export default {
value: {
type: Array,
default: () => []
},
canSelect: {
type: Function,
default(row, index) {
return true
}
}
},
data() {
@@ -68,6 +74,7 @@ export default {
tableConfig: {
url: '/api/v1/assets/assets/',
hasTree: true,
canSelect: this.canSelect,
columns: [
{
prop: 'hostname',
@@ -81,7 +88,7 @@ export default {
},
{
prop: 'ip',
label: this.$t('assets.ip'),
label: this.$t('assets.ipDomain'),
sortable: 'custom'
}
],
@@ -156,20 +163,20 @@ export default {
.el-select{
width: 100%;
}
.page /deep/ .page-heading{
.page ::v-deep .page-heading{
display: none;
}
.el-dialog__wrapper /deep/.el-dialog__body{
.el-dialog__wrapper ::v-deep .el-dialog__body{
padding: 5px 10px;
}
.page /deep/ .treebox{
.page ::v-deep .treebox{
height: inherit !important;
}
.asset-select-dialog >>> .transition-box:first-child {
background-color: #f3f3f3 ;
}
.el-dialog__wrapper /deep/.el-dialog__body .wrapper-content {
.el-dialog__wrapper ::v-deep .el-dialog__body .wrapper-content {
padding: 10px;
}

View File

@@ -120,7 +120,7 @@ export default {
{
prop: 'ip',
label: this.$t('assets.ip'),
width: 140
width: '120px'
},
{
prop: 'username',
@@ -130,7 +130,7 @@ export default {
{
prop: 'version',
label: this.$t('assets.Version'),
width: '50px'
width: '70px'
},
{
prop: 'date_created',
@@ -154,7 +154,7 @@ export default {
type: 'primary',
callback: function(val) {
this.MFAInfo.asset = val.cellValue
if (this.MFAVerifyAt + this.MFA_TTl * 1000 > (new Date()).valueOf()) {
if (!this.needMFAVerify) {
this.showMFADialog = true
this.MFAConfirmed = true
this.$axios.get(`/api/v1/assets/asset-user-auth-infos/${this.MFAInfo.asset}/`).then(res => {
@@ -176,7 +176,7 @@ export default {
this.$axios.delete(`/api/v1/assets/asset-users/${val.cellValue}/`).then(() => {
this.$message.success(this.$t('common.deleteSuccessMsg'))
this.$refs.ListTable.reloadTable()
}).catch(() => this.$message.error(this.$t('common.deleteFailedMsg')))
})
}
},
{
@@ -239,7 +239,8 @@ export default {
computed: {
...mapGetters([
'MFA_TTl',
'MFAVerifyAt'
'MFAVerifyAt',
'publicSettings'
]),
needMFAVerify() {
if (!this.publicSettings.SECURITY_VIEW_AUTH_NEED_MFA) {
@@ -312,6 +313,7 @@ export default {
key: ''
}
this.showDialog = false
this.$refs.ListTable.reloadTable()
},
Onchange(e) {
const vm = this
@@ -351,6 +353,7 @@ export default {
key: ''
}
this.showDialog = false
this.$refs.ListTable.reloadTable()
}
}
}

View File

@@ -44,7 +44,6 @@ export default {
groups: []
}
},
mounted() {
this.optionUrlMeta()
},
@@ -54,7 +53,7 @@ export default {
this.meta = data.actions[this.method.toUpperCase()] || {}
this.generateColumns()
}).catch(err => {
console.error(err)
this.$log.error(err)
}).finally(() => {
this.loading = false
})
@@ -63,9 +62,11 @@ export default {
switch (type) {
case 'choice':
type = 'radio-group'
field.options = fieldMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
if (!fieldMeta.read_only) {
field.options = fieldMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
}
break
case 'datetime':
type = 'date-picker'
@@ -76,6 +77,9 @@ export default {
case 'field':
type = ''
field.component = Select2
if (fieldMeta.required) {
field.el.clearable = false
}
break
case 'string':
type = 'input'
@@ -89,12 +93,14 @@ export default {
break
}
if (type === 'radio-group') {
const options = fieldMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
if (options.length > 4) {
type = 'select'
field.el.filterable = true
if (!fieldMeta.read_only) {
const options = fieldMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
if (options.length > 4) {
type = 'select'
field.el.filterable = true
}
}
}
field.type = type
@@ -128,7 +134,8 @@ export default {
},
generateField(name) {
let field = { id: name, prop: name, el: {}, attrs: {}}
const fieldMeta = this.meta[name] || {}
// const fieldMeta = this.meta[name] || this.meta['attrs']['children'][name] || {}
const fieldMeta = this.meta[name] || ((this.meta['attrs']) ? (this.meta['attrs']['children'][name]) : {})
field.label = fieldMeta.label
field = this.generateFieldByType(fieldMeta.type, field, fieldMeta)
field = this.generateFieldByName(name, field)
@@ -146,12 +153,25 @@ export default {
})
return this.generateFields(fields)
},
generateFieldAttrs(name) {
const fields = []
Object.keys(this.meta[name]['children']).forEach((key, i) => {
const filed = this.generateField(key)
fields.push(filed)
})
return fields
},
generateFields(data) {
let fields = []
for (let field of data) {
if (field instanceof Array) {
const items = this.generateFieldGroup(field)
fields = [...fields, ...items]
} else if (field === 'attrs') {
const items = this.generateFieldAttrs(field)
fields = [...fields, ...items]
// 修改title插入ID
this.groups[this.groups.length - 1].name = items[0].id
} else if (typeof field === 'string') {
field = this.generateField(field)
fields.push(field)
@@ -165,15 +185,23 @@ export default {
},
generateColumns() {
this.totalFields = this.generateFields(this.fields)
this.$log.debug('Total fields: ', this.totalFields)
},
setFieldError(name, error) {
const field = this.totalFields.find((v) => v.prop === name)
if (!field) {
return
}
if (field.attrs.error === error) {
error += '.'
if (typeof error === 'object') {
const str = error
error = ''
Object.keys(str).forEach(key => {
error += `${parseInt(key) + 1}.${str[key][0]} `
})
}
// if (field.attrs.error === error) {
// error += '.'
// }
field.attrs.error = error
}
}

View File

@@ -47,7 +47,9 @@ export default {
}
const option = {
label: field.label,
type: field.type,
value: name
}
if (field.type === 'choice' && field.choices) {
option.children = field.choices.map(item => {

View File

@@ -1,5 +1,5 @@
<template>
<DataTable v-if="!loading" ref="dataTable" v-loading="loading" :config="iConfig" v-bind="$attrs" v-on="$listeners" />
<DataTable v-if="!loading" ref="dataTable" v-loading="loading" :config="iConfig" v-bind="$attrs" v-on="$listeners" @filter-change="filterChange" />
</template>
<script type="text/jsx">
@@ -15,6 +15,10 @@ export default {
config: {
type: Object,
default: () => ({})
},
filterTable: {
type: Function,
default: () => ({})
}
},
data() {
@@ -118,6 +122,37 @@ export default {
}
return col
},
addFilterIfNeed(col) {
if (col.prop) {
const column = this.meta[col.prop] || {}
if (!column.filter) {
return col
}
if (column.type === 'boolean') {
col.filters = [
{ text: this.$t('common.Yes'), value: true },
{ text: this.$t('common.No'), value: false }
]
col.sortable = false
col['column-key'] = col.prop
}
if (column.type === 'choice' && column.choices) {
col.filters = column.choices.map(item => {
if (typeof (item.value) === 'boolean') {
if (item.value) {
return { text: item.display_name, value: 'True' }
} else {
return { text: item.display_name, value: 'False' }
}
}
return { text: item.display_name, value: item.value }
})
col.sortable = false
col['column-key'] = col.prop
}
}
return col
},
generateColumn(name) {
const colMeta = this.meta[name] || {}
const customMeta = this.config.columnsMeta ? this.config.columnsMeta[name] : {}
@@ -127,6 +162,7 @@ export default {
col = this.generateColumnByType(colMeta.type, col)
col = Object.assign(col, customMeta)
col = this.addHelpTipsIfNeed(col)
col = this.addFilterIfNeed(col)
return col
},
generateColumns() {
@@ -142,6 +178,12 @@ export default {
}
config.columns = columns
this.iConfig = config
},
filterChange(filters) {
const key = Object.keys(filters)[0]
const attr = {}
attr[key] = filters[key][0]
this.filterTable(attr)
}
}
}

View File

@@ -139,8 +139,6 @@ export default {
treeNode.name = treeNode.name + ' (' + assetsAmount + ')'
this.zTree.updateNode(treeNode)
this.$message.success(this.$t('common.updateSuccessMsg'))
}).catch(error => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
})
},
onBodyMouseDown: function(event) {
@@ -159,6 +157,11 @@ export default {
y -= (offset.top + scrollTop) / 3 - 10
x += document.body.scrollLeft
y += document.body.scrollTop + document.documentElement.scrollTop
if (y + $(`#${rMenuID} ul`).height() >= window.innerHeight) {
y -= $(`#${rMenuID} ul`).height()
}
this.rMenu.css({ 'top': y + 'px', 'left': x + 'px', 'visibility': 'visible' })
$(`#${rMenuID} ul`).show()
$('body').bind('mousedown', this.onBodyMouseDown)
@@ -237,10 +240,7 @@ export default {
})
},
refresh: function() {
this.$axios.post(
'/api/v1/assets/nodes/00000000-0000-0000-0000-000000000000/tasks/',
{ action: 'refresh_cache' }
)
},
getSelectedNodes: function() {
return this.zTree.getSelectedNodes()

View File

@@ -15,6 +15,7 @@
<el-form-item>
<el-button v-for="button in moreButtons" :key="button.title" size="small" v-bind="button" @click="handleClick(button)">{{ button.title }}</el-button>
<el-button v-if="defaultButton && hasReset" size="small" @click="resetForm('form')">{{ $t('common.Reset') }}</el-button>
<el-button v-if="defaultButton && hasSaveContinue" size="small" @click="submitForm('form', true)">{{ $t('common.SaveAndAddAnother') }}</el-button>
<el-button v-if="defaultButton" size="small" :loading="isSubmitting" type="primary" @click="submitForm('form')">{{ $t('common.Submit') }}</el-button>
</el-form-item>
</ElFormRender>
@@ -35,6 +36,10 @@ export default {
type: Boolean,
default: true
},
hasSaveContinue: {
type: Boolean,
default: true
},
fields: {
type: Array,
default: () => []
@@ -60,11 +65,11 @@ export default {
},
methods: {
// 获取表单数据
submitForm(formName) {
submitForm(formName, addContinue) {
const form = this.$refs[formName]
form.validate((valid) => {
if (valid) {
this.$emit('submit', form.getFormValue(), form)
this.$emit('submit', form.getFormValue(), form, addContinue)
} else {
this.$emit('invalid', valid)
return false
@@ -77,7 +82,7 @@ export default {
},
handleClick(button) {
const callback = button.callback || function(values, form) {
console.log('Click ', button.title, ': ', values)
// console.log('Click ', button.title, ': ', values)
}
const form = this.$refs['form']
const values = form.getFormValue()
@@ -88,27 +93,27 @@ export default {
</script>
<style lang="less" scoped>
.el-form /deep/ .el-form-item {
.el-form ::v-deep .el-form-item {
margin-bottom: 12px;
}
.el-form /deep/ .el-form-item__content {
.el-form ::v-deep .el-form-item__content {
width: 75%;
}
.el-form /deep/ .el-form-item__label {
.el-form ::v-deep .el-form-item__label {
padding: 0 30px 0 0;
}
.el-form /deep/ .el-form-item__error {
.el-form ::v-deep .el-form-item__error {
position: inherit;
}
.el-form /deep/ .form-group-header {
.el-form ::v-deep .form-group-header {
margin-left: 50px;
}
.el-form /deep/ .help-block {
.el-form ::v-deep .help-block {
display: block;
margin-top: 5px;
margin-bottom: 10px;
@@ -116,7 +121,7 @@ export default {
font-size: 12px;
line-height: 18px;
}
.el-form /deep/ .help-block a {
.el-form ::v-deep .help-block a {
color: #1c84c6;
}
</style>

View File

@@ -11,9 +11,10 @@
v-bind="tableAttrs"
:data="data"
:row-class-name="rowClassName"
v-on="$listeners"
@selection-change="selectStrategy.onSelectionChange"
@select="selectStrategy.onSelect"
@select-all="selectStrategy.onSelectAll($event, selectable)"
@select-all="selectStrategy.onSelectAll($event, canSelect)"
@sort-change="onSortChange"
>
<!--TODO 不用jsx写, 感觉template逻辑有点不清晰了-->
@@ -90,11 +91,14 @@
<!--非树-->
<template v-else>
<el-data-table-column v-if="hasSelection" type="selection" :align="selectionAlign" />
<el-data-table-column v-if="hasSelection" type="selection" :align="selectionAlign" :selectable="canSelect" />
<el-data-table-column
v-for="col in columns"
:key="col.prop"
:formatter="typeof col.formatter === 'function' ? col.formatter : null"
:filters="col.filters || null"
:filter-multiple="false"
:filter-method="typeof col.filterMethod === 'function' ? col.filterMethod : null"
v-bind="{align: columnsAlign, ...col}"
>
<template v-if="col.formatter && typeof col.formatter !== 'function'" v-slot:default="{row, column, index}">
@@ -422,7 +426,7 @@ export default {
onEdit: {
type: Function,
default(row) {
console.log('On delete row')
// console.log('On delete row')
}
},
/**
@@ -713,6 +717,12 @@ export default {
hasDetail: {
type: Boolean,
default: true
},
canSelect: {
type: Function,
default(row, index) {
return true
}
}
},
data() {
@@ -953,6 +963,8 @@ export default {
})
},
search(attrs, reset) {
// 重置搜索结果到第一页
this.page = defaultFirstPage
// Orange 重置查询对象
if (reset) {
this.innerQuery = merge({}, attrs)
@@ -962,6 +974,8 @@ export default {
return this.getList()
},
searchDate(attrs) {
// 重置搜索结果到第一页
this.page = defaultFirstPage
this.innerQuery = merge(this.innerQuery, attrs)
return this.getList()
},
@@ -996,7 +1010,7 @@ export default {
},
handleSizeChange(val) {
if (this.size === val) return
this.$emit('sizeChange', val)
this.page = defaultFirstPage
this.size = val
this.getList()

View File

@@ -1,16 +1,16 @@
.el-data-table /deep/ .el-pagination{
.el-data-table ::v-deep .el-pagination{
text-align: center !important;
}
.el-data-table /deep/ .el-table td{
.el-data-table ::v-deep .el-table td{
padding: 4px 0;
}
.el-data-table /deep/ .el-table th{
.el-data-table ::v-deep .el-table th{
padding: 4px 0;
}
.el-data-table/deep/ .el-form-item{
.el-data-table ::v-deep .el-form-item{
margin-bottom:10px !important ;
margin-top:10px;
}
.el-data-table/deep/ .el-pagination{
.el-data-table ::v-deep .el-pagination{
padding:15px 0 !important ;
}
}

View File

@@ -1,9 +1,17 @@
<template>
<ElDatableTable ref="table" class="el-table" v-bind="tableConfig" @update="onUpdate" v-on="iListeners" />
<ElDatableTable
ref="table"
class="el-table"
v-bind="tableConfig"
@update="onUpdate"
v-on="iListeners"
@sizeChange="handleSizeChange"
/>
</template>
<script>
import { default as ElDatableTable } from './compenents/el-data-table'
import { mapGetters } from 'vuex'
export default {
name: 'DataTable',
@@ -58,7 +66,6 @@ export default {
pageCount: 5,
paginationLayout: 'total, sizes, prev, pager, next',
paginationSizes: [15, 30, 50, 100],
paginationSize: 15,
paginationBackground: true,
transformQuery: query => {
if (query.page && query.size) {
@@ -85,12 +92,17 @@ export default {
},
computed: {
tableConfig() {
const config = Object.assign(this.defaultConfig, this.config)
const tableDefaultConfig = this.defaultConfig
tableDefaultConfig.paginationSize = _.get(this.globalTableConfig, 'paginationSize', 15)
const config = Object.assign(tableDefaultConfig, this.config)
return config
},
iListeners() {
return Object.assign({}, this.$listeners, this.tableConfig.listeners)
}
},
...mapGetters({
'globalTableConfig': 'tableConfig'
})
},
watch: {
config: {
@@ -131,6 +143,14 @@ export default {
this.toggleRowSelection(row, true)
}
}
},
handleSizeChange(val) {
this.$store.commit('table/SET_TABLE_CONFIG',
{
key: 'paginationSize',
value: val
}
)
}
}
}
@@ -138,16 +158,16 @@ export default {
<style lang="less" scoped>
.el-table /deep/ .el-table__row > td {
.el-table ::v-deep .el-table__row > td {
line-height: 1.5;
padding: 8px 0;
}
.el-table /deep/ .el-table__row > td> div > span {
.el-table ::v-deep .el-table__row > td> div > span {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.el-table /deep/ .el-table__header > thead > tr >th {
.el-table ::v-deep .el-table__header > thead > tr >th {
padding: 8px 0;
background-color: #F5F5F6;
font-size: 13px;
@@ -158,11 +178,11 @@ export default {
}
//分页
.el-pagination /deep/ .el-pagination__total{
.el-pagination ::v-deep .el-pagination__total{
float: left;
}
.el-pagination /deep/ .el-pagination__sizes{
.el-pagination ::v-deep .el-pagination__sizes{
float: left;
}
//修改颜色

View File

@@ -1,6 +1,9 @@
<template>
<div>
<div class="treebox">
<ul v-show="loading" class="ztree">
{{ this.$t('common.tree.Loading') }}...
</ul>
<div v-show="!loading" class="treebox">
<ul :id="iZTreeID" class="ztree">
{{ this.$t('common.tree.Loading') }}...
</ul>
@@ -22,6 +25,7 @@
import $ from '@/utils/jquery-vendor.js'
import '@ztree/ztree_v3/js/jquery.ztree.all.min.js'
import '@/styles/ztree.css'
import axiosRetry from 'axios-retry'
const defaultObject = {
type: Object,
@@ -39,7 +43,9 @@ export default {
iZTreeID: `zTree_${this._uid}`,
iRMenuID: `rMenu_${this._uid}`,
zTree: '',
rMenu: ''
rMenu: '',
init: false,
loading: false
}
},
computed: {
@@ -52,11 +58,30 @@ export default {
// $('.treebox').css('height', window.innerHeight - 60)
},
beforeDestroy() {
$.fn.zTree.destroy()
$.fn.zTree.destroy(this.iZTreeID)
},
methods: {
initTree: function() {
this.$axios.get(this.treeSetting.treeUrl).then(res => {
const vm = this
let treeUrl
if (this.init) {
this.loading = true
}
if (this.init && this.treeSetting.treeUrl.indexOf('/perms/') !== -1 && this.treeSetting.treeUrl.indexOf('rebuild_tree') === -1) {
treeUrl = (this.treeSetting.treeUrl.indexOf('?') === -1) ? `${this.treeSetting.treeUrl}?rebuild_tree=1` : `${this.treeSetting.treeUrl}&rebuild_tree=1`
} else {
treeUrl = this.treeSetting.treeUrl
}
this.$axios.get(treeUrl, {
'axios-retry': {
retries: 20,
retryCondition: e => {
return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 409
},
shouldResetTimeout: true,
retryDelay: () => { return 5000 }
}
}).then(res => {
if (!res) {
res = []
}
@@ -65,6 +90,10 @@ export default {
name: this.$t('common.tree.Empty')
})
}
this.treeSetting.treeUrl = treeUrl
if (this.init) {
vm.zTree.destroy()
}
this.zTree = $.fn.zTree.init($(`#${this.iZTreeID}`), this.treeSetting, res)
if (this.treeSetting.showRefresh) {
this.rootNodeAddDom(
@@ -79,6 +108,9 @@ export default {
if (this.treeSetting.otherMenu) {
$('.menu-actions').append(this.otherMenu)
}
}).finally(_ => {
vm.loading = false
vm.init = true
})
},
rootNodeAddDom: function(ztree, callback) {
@@ -95,7 +127,6 @@ export default {
}
const refreshIconRef = $('#tree-refresh')
refreshIconRef.bind('click', function() {
ztree.destroy()
const result = callback()
if (result && result.then) {
result.finally(() => {
@@ -158,7 +189,7 @@ export default {
top: 100%;
z-index: 1000;
}
.ztree /deep/ .fa-refresh {
.ztree ::v-deep .fa-refresh {
font: normal normal normal 14px/1 FontAwesome !important;
}
.dropdown a:hover {

View File

@@ -37,8 +37,8 @@ export default {
showRemoveBtn: false,
showRenameBtn: false,
drag: {
isCopy: true,
isMove: true
isCopy: false,
isMove: false
}
},
callback: {
@@ -72,7 +72,7 @@ export default {
},
methods: {
defaultCallback: function(action) {
console.log(action)
// console.log(action)
}
}
}

View File

@@ -93,17 +93,17 @@ export default {
<style lang='less' scoped>
.datepicker{
width: 240px;
width: 233px;
}
.el-input__inner{
border: 1px solid #dcdee2;
border-radius: 3px;
height: 36px;
}
/*.el-date-editor /deep/ .el-input__icon{*/
/*.el-date-editor ::v-deep .el-input__icon{*/
/* line-height: 28px;*/
/*}*/
.el-date-editor /deep/ .el-range-separator{
.el-date-editor ::v-deep .el-range-separator{
line-height: 28px;
}
</style>

View File

@@ -3,7 +3,7 @@ export default {
name: 'ItemValue',
props: {
value: {
type: [String, Number, Function, Array, Object],
type: [String, Number, Function, Array, Object, Boolean],
default: ''
},
item: {
@@ -15,10 +15,21 @@ export default {
default: null
}
},
methods: {
toChoicesDisplay(value) {
if (!value) {
return this.$t('common.No')
}
return this.$t('common.Yes')
}
},
render(h) {
if (typeof this.formatter === 'function') {
return this.formatter(this.item, this.value)
}
if (typeof this.value === 'boolean') {
return <span>{this.toChoicesDisplay(this.value)}</span>
}
return <span>{this.value}</span>
}
}

View File

@@ -1,11 +1,19 @@
<template>
<IBox :title="title" fa="fa-info-circle">
<div class="content">
<el-row v-if="this.$route.params.id" :gutter="10" class="item">
<el-col :span="6"><div :style="{ 'text-align': align }" class="item-label"><label>ID: </label></div></el-col>
<el-col :span="18"><div class="item-text">{{ this.$route.params.id }}</div></el-col>
</el-row>
<el-row v-for="item in items" :key="'card-' + item.key" :gutter="10" class="item">
<el-col :span="6"><div :style="{ 'text-align': align }" class="item-label"><label>{{ item.key }}: </label></div></el-col>
<el-col :span="18"><div class="item-text">
<ItemValue :value="item.value" v-bind="item" />
</div></el-col>
<el-col :span="6">
<div :style="{ 'text-align': align }" class="item-label"><label>{{ item.key }}: </label></div>
</el-col>
<el-col :span="18">
<div class="item-text">
<ItemValue :value="item.value" v-bind="item" />
</div>
</el-col>
</el-row>
<slot />
</div>

View File

@@ -10,7 +10,7 @@
<div slot="footer" class="dialog-footer">
<slot name="footer">
<el-button v-if="showCancel" size="small" @click="onCancel">{{ cancelTitle }}</el-button>
<el-button v-if="showConfirm" type="primary" size="small" @click="onConfirm">{{ confirmTitle }}</el-button>
<el-button v-if="showConfirm" type="primary" size="small" :loading="loadingStatus" @click="onConfirm">{{ confirmTitle }}</el-button>
</slot>
</div>
</el-dialog>
@@ -42,6 +42,10 @@ export default {
type: Boolean,
default: true
},
loadingStatus: {
type: Boolean,
default: false
},
confirmTitle: {
type: String,
default() {

View File

@@ -65,22 +65,24 @@ export default {
sortable: true,
formatterArgs: {
route: 'AssetDetail'
}
},
showOverflowTooltip: true
},
{
prop: 'ip',
label: this.$t('assets.IP'),
width: '140px',
sortable: 'custom'
},
{
prop: 'systemUsers',
label: this.$t('assets.SystemUsers'),
align: 'center',
width: '200px',
formatter: SystemUserFormatter,
formatterArgs: {
getUrl: this.getShowUrl.bind(this)
}
},
showOverflowTooltip: true
}
]
},

View File

@@ -1,6 +1,11 @@
<template>
<Dialog v-if="showExportDialog" :title="$t('common.Export')" :visible.sync="showExportDialog" :destroy-on-close="true" @confirm="handleExportConfirm()" @cancel="handleExportCancel()">
<el-form label-position="left" style="padding-left: 50px">
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
<el-radio-group v-model="exportTypeOption">
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item class="export-form" :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
<el-radio-group v-model="exportOption">
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
@@ -51,7 +56,8 @@ export default {
data() {
return {
showExportDialog: false,
exportOption: '',
exportOption: 'all',
exportTypeOption: 'csv',
meta: {}
}
},
@@ -67,6 +73,8 @@ export default {
const query = listTableRef.dataTable.getQuery()
delete query['limit']
delete query['offset']
delete query['date_from']
delete query['date_to']
return query
},
tableHasQuery() {
@@ -77,7 +85,7 @@ export default {
{
label: this.$t('common.imExport.ExportAll'),
value: 'all',
can: this.canExportAll
can: this.canExportAll && !this.tableHasQuery
},
{
label: this.$t('common.imExport.ExportOnlySelectedItems'),
@@ -90,6 +98,20 @@ export default {
can: this.tableHasQuery && this.canExportFiltered
}
]
},
exportTypeOptions() {
return [
{
label: 'CSV',
value: 'csv',
can: true
},
{
label: 'Excel',
value: 'xlsx',
can: true
}
]
}
},
mounted() {
@@ -121,7 +143,7 @@ export default {
// delete query['limit']
// delete query['offset']
}
query['format'] = 'csv'
query['format'] = this.exportTypeOption
const queryStr =
(url.indexOf('?') > -1 ? '&' : '?') +
queryUtil.stringify(query, '=', '&')

View File

@@ -1,6 +1,18 @@
<template>
<Dialog :title="$t('common.Import')" :visible.sync="showImportDialog" :destroy-on-close="true" @confirm="handleImportConfirm" @cancel="handleImportCancel()">
<Dialog
:title="$t('common.Import')"
:visible.sync="showImportDialog"
:destroy-on-close="true"
:loading-status="loadStatus"
@confirm="handleImportConfirm"
@cancel="handleImportCancel()"
>
<el-form label-position="left" style="padding-left: 50px">
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
<el-radio-group v-model="importTypeOption">
<el-radio v-for="option of importTypeOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('common.Import' )" :label-width="'100px'">
<el-radio v-model="importOption" class="export-item" label="1">{{ this.$t('common.Create') }}</el-radio>
<el-radio v-model="importOption" class="export-item" label="2">{{ this.$t('common.Update') }}</el-radio>
@@ -26,7 +38,7 @@
:before-upload="beforeUpload"
>
<el-button size="mini" type="default">{{ this.$t('common.SelectFile') }}</el-button>
<div slot="tip" :class="uploadHelpTextClass" style="line-height: 1.5">{{ this.$t('common.imExport.onlyCSVFilesTips') }}</div>
<!-- <div slot="tip" :class="uploadHelpTextClass" style="line-height: 1.5">{{ this.$t('common.imExport.onlyCSVFilesTips') }}</div>-->
</el-upload>
</el-form-item>
</el-form>
@@ -63,19 +75,36 @@ export default {
showImportDialog: false,
importOption: '1',
isCsv: true,
errorMsg: ''
errorMsg: '',
loadStatus: false,
importTypeOption: 'csv'
}
},
computed: {
hasSelected() {
return this.selectedRows.length > 0
},
importTypeOptions() {
return [
{
label: 'CSV',
value: 'csv',
can: true
},
{
label: 'Excel',
value: 'xlsx',
can: true
}
]
},
upLoadUrl() {
return this.url
},
downloadImportTempUrl() {
const baseUrl = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)
return baseUrl + '?format=csv&template=import&limit=1'
const format = this.importTypeOption === 'csv' ? 'format=csv&template=import&limit=1' : 'format=xlsx&template=import&limit=1'
const url = (this.url.indexOf('?') === -1) ? `${this.url}?${format}` : `${this.url}&${format}`
return url
},
uploadHelpTextClass() {
const cls = ['el-upload__tip']
@@ -95,24 +124,28 @@ export default {
this.$axios.put(
this.upLoadUrl,
item.file,
{ headers: { 'Content-Type': 'text/csv' }, disableFlashErrorMsg: true }
{ headers: { 'Content-Type': this.importTypeOption === 'csv' ? 'text/csv' : 'text/xlsx' }, disableFlashErrorMsg: true }
).then((data) => {
const msg = this.$t('common.imExport.updateSuccessMsg', { count: data.length })
this.onSuccess(msg)
}).catch(error => {
this.catchError(error)
}).finally(() => {
this.loadStatus = false
})
},
performCreate(item) {
this.$axios.post(
this.upLoadUrl,
item.file,
{ headers: { 'Content-Type': 'text/csv' }, disableFlashErrorMsg: true }
{ headers: { 'Content-Type': this.importTypeOption === 'csv' ? 'text/csv' : 'text/xlsx' }, disableFlashErrorMsg: true }
).then((data) => {
const msg = this.$t('common.imExport.createSuccessMsg', { count: data.length })
this.onSuccess(msg)
}).catch(error => {
this.catchError(error)
}).finally(() => {
this.loadStatus = false
})
},
catchError(error) {
@@ -149,6 +182,7 @@ export default {
window.URL.revokeObjectURL(url)
},
handleImport(item) {
this.loadStatus = true
if (this.importOption === '1') {
this.performCreate(item)
} else {
@@ -163,7 +197,8 @@ export default {
}
const spm = await createSourceIdCache(resources)
const baseUrl = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)
const url = `${baseUrl}?format=csv&template=update&spm=` + spm.spm
const format = this.importTypeOption === 'csv' ? '?format=csv&template=update&spm=' : '?format=xlsx&template=update&spm='
const url = `${baseUrl}${format}` + spm.spm
return this.downloadCsv(url)
},
async handleImportConfirm() {
@@ -173,7 +208,12 @@ export default {
this.showImportDialog = false
},
beforeUpload(file) {
this.isCsv = _.endsWith(file.name, 'csv')
this.isCsv = this.importTypeOption === 'csv' ? _.endsWith(file.name, 'csv') : _.endsWith(file.name, 'xlsx')
if (!this.isCsv) {
this.$message.error(
this.$t('common.NeedSpecifiedFile')
)
}
return this.isCsv
}
}

View File

@@ -1,7 +1,7 @@
<template>
<div>
<ActionsGroup :is-fa="true" :actions="rightSideActions" class="right-side-actions right-side-item" />
<ImExportDialog :selected-rows="selectedRows" :url="tableUrl" />
<ImExportDialog :selected-rows="selectedRows" :url="tableUrl" v-bind="$attrs" />
</div>
</template>
@@ -119,7 +119,7 @@ export default {
}
.right-side-actions >>> .el-button:hover {
background-color: rgb(0, 0, 0, 0.05);
background-color: rgba(0, 0, 0, 0.05);
}
.action-search >>> .el-input__suffix i {

View File

@@ -24,6 +24,19 @@ const defaultUpdateCallback = function({ row, col }) {
this.$router.push(route)
}
const defaultCloneCallback = function({ row, col }) {
const id = row.id
let route = { query: { clone_from: id }}
const cloneRoute = this.colActions.cloneRoute
if (typeof cloneRoute === 'object') {
route = Object.assign(route, cloneRoute)
} else {
route.name = cloneRoute
}
this.$router.push(route)
}
const defaultDeleteCallback = function({ row, col, cellValue, reload }) {
let msg = this.$t('common.deleteWarningMsg')
const name = row.name || row.hostname
@@ -71,10 +84,14 @@ export default {
canUpdate: true, // can set function(row, value)
hasDelete: true, // can set function(row, value)
canDelete: true,
hasClone: false,
canClone: true,
updateRoute: this.$route.name.replace('List', 'Update'),
cloneRoute: this.$route.name.replace('List', 'Create'),
performDelete: defaultPerformDelete,
onUpdate: defaultUpdateCallback,
onDelete: defaultDeleteCallback,
onClone: defaultCloneCallback,
extraActions: [] // format see defaultActions
}
}
@@ -89,7 +106,8 @@ export default {
type: 'primary',
has: colActions.hasUpdate,
can: colActions.canUpdate,
callback: colActions.onUpdate
callback: colActions.onUpdate,
order: 10
},
{
name: 'delete',
@@ -97,7 +115,17 @@ export default {
type: 'danger',
has: colActions.hasDelete,
can: colActions.canDelete,
callback: colActions.onDelete
callback: colActions.onDelete,
order: 20
},
{
name: 'clone',
title: this.$t('common.Clone'),
type: 'info',
has: colActions.hasClone,
can: colActions.canClone,
callback: colActions.onClone,
order: 30
}
]
return {
@@ -115,9 +143,11 @@ export default {
v.has = this.cleanBoolean(v, 'has')
v.can = this.cleanBoolean(v, 'can')
v.callback = this.cleanCallback(v)
v.order = v.order || 100
return v
})
actions = actions.filter((v) => v.has)
actions.sort((a, b) => a.order - b.order)
return actions
},
actions() {

View File

@@ -10,6 +10,7 @@
<script>
import BaseFormatter from './base'
import { toSafeLocalDateStr } from '@/utils/common'
export default {
name: 'ChoicesFormatter',
extends: BaseFormatter,
@@ -54,7 +55,7 @@ export default {
return this.formatterArgs.tipStatus(this.cellValue, vm)
},
tipTime() {
return this.cellValue.datetime
return toSafeLocalDateStr(this.cellValue.datetime)
}
}
}

View File

@@ -43,7 +43,7 @@ export default {
}
},
mounted() {
console.log(this.col)
// console.log(this.col)
},
methods: {
checkBool(item, attr, defaults) {

View File

@@ -33,6 +33,9 @@ export default {
}
},
iCanDelete() {
if (this.col.objects === 'all') {
return false
}
return this.col.objects.indexOf(this.cellValue) === -1
}
}

View File

@@ -2,7 +2,7 @@
<div>
<TableAction :table-url="iTableConfig.url" :search-table="search" :date-pick="handleDateChange" v-bind="headerActions" :selected-rows="selectedRows" :reload-table="reloadTable" />
<IBox class="table-content">
<AutoDataTable ref="dataTable" :config="iTableConfig" @selection-change="handleSelectionChange" v-on="$listeners" />
<AutoDataTable ref="dataTable" :filter-table="filter" :config="iTableConfig" @selection-change="handleSelectionChange" v-on="$listeners" />
</IBox>
</div>
</template>
@@ -13,6 +13,7 @@ import IBox from '../IBox'
import TableAction from './TableAction'
import Emitter from '@/mixins/emitter'
import deepmerge from 'deepmerge'
export default {
name: 'ListTable',
components: {
@@ -41,11 +42,24 @@ export default {
}
},
computed: {
dataTable() {
return this.$refs.dataTable.$refs.dataTable
},
hasCreateAction() {
const hasLeftAction = this.headerActions.hasLeftActions
if (hasLeftAction === false) {
return false
}
const hasCreate = this.headerActions.hasCreate
if (hasCreate === false) {
return false
}
return true
},
iTableConfig() {
const config = deepmerge(this.tableConfig, { extraQuery: this.extraQuery })
this.$log.debug('Header actions', this.headerActions)
this.$log.debug('ListTable: iTableConfig change', config)
return config
}
@@ -56,10 +70,14 @@ export default {
this.$log.debug('ListTable: found extraQuery change')
},
deep: true
},
tableColConfig: {
handler() {
this.$log.debug('ListTable: found colConfig change')
},
deep: true
}
},
mounted() {
},
methods: {
handleSelectionChange(val) {
this.selectedRows = val
@@ -70,6 +88,9 @@ export default {
search(attrs) {
return this.dataTable.search(attrs, true)
},
filter(attrs) {
this.$refs.dataTable.$refs.dataTable.search(attrs, true)
},
handleDateChange(attrs) {
this.$set(this.extraQuery, 'date_from', attrs[0].toISOString())
this.$set(this.extraQuery, 'date_to', attrs[1].toISOString())
@@ -92,28 +113,28 @@ export default {
<style lang="scss" scoped>
.table-content {
margin-top: 10px;
.table-content {
margin-top: 10px;
& >>> .el-card__body {
padding: 0;
}
& >>> .el-table__header thead > tr > th {
background-color: white;
}
/*& >>> .el-table--striped .el-table__body tr.el-table__row--striped td {*/
/*background: white;*/
/*}*/
/*& >>> .el-table th, .el-table tr {*/
/*background-color: red;*/
/*!*background-color: #FAFAFA;*!*/
/*}*/
& >>> .el-card__body {
padding: 0;
}
& >>> .el-table__header thead > tr > th {
background-color: white;
}
//修改颜色
// .el-button--text{
// color: #409EFF;
// }
/*& >>> .el-table--striped .el-table__body tr.el-table__row--striped td {*/
/*background: white;*/
/*}*/
/*& >>> .el-table th, .el-table tr {*/
/*background-color: red;*/
/*!*background-color: #FAFAFA;*!*/
/*}*/
}
//修改颜色
// .el-button--text{
// color: #409EFF;
// }
</style>

View File

@@ -1,8 +1,8 @@
<template>
<IBox fa="fa-edit" :title="title" v-bind="$attrs">
<div class="quick-actions">
<div v-for="action of actions" :key="action.title" class="quick-actions">
<table>
<ActionItem v-for="action of actions" :key="action.title" :action="action" />
<ActionItem v-if="action.has === undefined || action.has" :action="action" />
</table>
</div>
</IBox>

View File

@@ -6,6 +6,7 @@
<Select2 ref="select2" v-model="select2.value" v-bind="select2" />
</td>
</tr>
<slot />
<tr>
<td colspan="2">
<el-button :type="type" size="small" :loading="submitLoading" @click="addObjects">{{ $t('common.Add') }}</el-button>
@@ -21,7 +22,7 @@
</td>
</tr>
</template>
<tr v-if="params.hasMore" class="item">
<tr v-if="params.hasMore && showHasMore" class="item">
<td colspan="2">
<el-button :type="type" size="small" style="width: 100%" @click="loadMore">
<i class="fa fa-arrow-down" />
@@ -37,7 +38,6 @@
import Select2 from '../Select2'
import IBox from '../IBox'
import { createSourceIdCache } from '@/api/common'
export default {
name: 'RelationCard',
components: {
@@ -83,6 +83,10 @@ export default {
type: [Array, Number, String],
default: () => []
},
showHasMore: {
type: Boolean,
default: true
},
performDelete: {
type: Function,
default: (obj, that) => {}

View File

@@ -8,6 +8,7 @@
:remote-method="filterOptions"
:multiple="multiple"
filterable
:clearable="clearable"
popper-append-to-body
class="select2"
v-bind="$attrs"
@@ -69,6 +70,10 @@ export default {
type: Boolean,
default: true
},
clearable: {
type: Boolean,
default: true
},
// 初始化值,也就是选中的值
value: {
type: [Array, String, Number, Boolean],
@@ -172,6 +177,9 @@ export default {
},
methods: {
async loadMore(load) {
if (!this.iAjax.url) {
return
}
if (!this.params.hasMore) {
return
}

View File

@@ -1,14 +1,35 @@
<template>
<div class="filter-field">
<el-cascader ref="Cascade" :options="options" :props="config" @change="handleMenuItemChange" />
<el-tag v-for="(v, k) in filterTags" :key="k" :name="k" closable size="small" class="filter-tag" type="info" @close="handleTagClose(k)">
<el-tag
v-for="(v, k) in filterTags"
:key="k"
:name="k"
closable
size="small"
class="filter-tag"
type="info"
:disable-transitions="true"
@close="handleTagClose(k)"
@click="handleTagClick(v,k)"
>
<strong v-if="v.label">{{ v.label + ':' }}</strong>
<span v-if="v.valueLabel">{{ v.valueLabel }}</span>
<span v-else>{{ v.value }}</span>
</el-tag>
<span v-if="keyLabel" slot="prefix" class="filterTitle">{{ keyLabel + ':' }}</span>
<el-input ref="SearchInput" v-model="filterValue" :placeholder="placeholder" class="search-input" @blur="focus = false" @focus="focus = true" @change="handleConfirm" />
<el-input
ref="SearchInput"
v-model="filterValue"
:placeholder="placeholder"
class="search-input"
@blur="focus = false"
@focus="focus = true"
@change="handleConfirm"
/>
</div>
</template>
<script>
@@ -65,13 +86,13 @@ export default {
}
},
watch: {
filterTags: {
handler(val) {
this.$nextTick(() => this.$emit('tagSearch', this.filterMaps))
// this.$emit('tagSearch', this.filterMaps)
},
deep: true
}
// filterTags: {
// handler(val) {
// this.$nextTick(() => this.$emit('tagSearch', this.filterMaps))
// // this.$emit('tagSearch', this.filterMaps)
// },
// deep: true
// }
},
mounted() {
setTimeout(() => {
@@ -116,6 +137,7 @@ export default {
},
handleTagClose(evt) {
this.$delete(this.filterTags, evt)
this.$emit('tagSearch', this.filterMaps)
return true
},
handleConfirm() {
@@ -124,9 +146,33 @@ export default {
}
const tag = { key: this.filterKey, label: this.keyLabel, value: this.filterValue, valueLabel: this.valueLabel }
this.$set(this.filterTags, this.filterKey, tag)
this.$emit('tagSearch', this.filterMaps)
this.filterKey = ''
this.filterValue = ''
this.valueLabel = ''
},
handleTagClick(v, k) {
let unableChange = false
for (const field of this.options) {
if (field.value === v.key) {
if (field.type === 'choice') {
unableChange = true
}
if (field.type === 'boolean') {
unableChange = true
}
}
}
if (unableChange) {
return
}
if (this.filterValue.length !== 0) {
this.handleConfirm()
}
this.$delete(this.filterTags, k)
this.filterKey = v.key
this.filterValue = v.value
this.$refs.SearchInput.focus()
}
}
}

View File

@@ -98,6 +98,7 @@ export default {
color: #FFFFFF;
border-radius: 3px;
line-height: 1.428;
cursor:pointer;
}
.el-tree{
background-color: inherit !important;

View File

@@ -2,8 +2,25 @@
"": "",
"applications": {
"": "",
"applicationsType": {
"chrome": "Chrome",
"mysql_workbench": "MySQL Workbench",
"vmware_client":"Vmware Client",
"custom":"Custom",
"mysql": "MySQL",
"oracle": "Oracle",
"postgresql": "PostgreSQL",
"mariadb": "MariaDB",
"k8s": "Kubernetes"
},
"applicationsCategory": {
"remote_app": "远程应用",
"db": "数据库应用",
"cloud": "云应用"
},
"appPath": "应用路径",
"appType": "应用类型",
"appName": "应用名称",
"asset": "资产",
"database": "数据库",
"host": "主机",
@@ -16,6 +33,7 @@
"chrome_password": "登录密码",
"mysql_workbench": "MySQL Workbench",
"mysql_workbench_ip": "数据库IP",
"mysql_workbench_port": "数据库端口",
"mysql_workbench_name": "数据库名",
"mysql_workbench_username": "数据库账号",
"mysql_workbench_password": "数据库密码",
@@ -28,7 +46,11 @@
"custom_target": "目标地址",
"custom_username": "登录账号",
"custom_password": "登录密码",
"Custom": "自定义"
"Custom": "自定义",
"cluster": "集群",
"kubernetes":"Kubernetes",
"clusterHelpTextMessage": "例如https://172.16.8.8:8443",
"DBInfo": "数据库信息"
},
"assets": {
"Action": "动作",
@@ -37,13 +59,19 @@
"AdminUserDetail": "管理用户详情",
"AdminUserListHelpMessage": "管理用户是资产(被控服务器)上的 root或拥有 NOPASSWD: ALL sudo 权限的用户, JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等。\n",
"Asset": "资产",
"HardwareInfo": "硬件信息",
"AssetDetail": "资产详情",
"AssetList": "资产列表",
"ReplaceNodeAssetsAdminUser":"替换节点资产的管理员",
"AssetListHelpMessage": "左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的,右侧是属于该节点下的资产\n",
"TestGatewayTestConnection":"测试连接网关",
"TestGatewayHelpMessage": "如果使用了nat端口映射请设置为ssh真实监听的端口",
"SshPort": "SSH 端口",
"AssetNumber": "资产编号",
"AssetUserList": "资产用户列表",
"Assets": "资产",
"Auth": "认证",
"AccountList": "账号列表",
"AutoGenerateKey": "自动生成密钥",
"AutoPush": "自动推送",
"BasePlatform": "基础平台",
@@ -63,6 +91,8 @@
"DateUpdated": "更新日期",
"DeactiveSelected": "禁用所选",
"Disk": "硬盘",
"AdDomain": "AD域名",
"AdDomainHelpText": "提供给域用户登录的AD域名",
"Domain": "网域",
"DomainDetail": "网域详情",
"DomainHelpMessage": "网域功能是为了解决部分环境混合云无法直接连接而新增的功能原理是通过网关服务器进行跳转登录。JMS => 网域网关 => 目标资产",
@@ -87,6 +117,7 @@
"OnlyLatestVersion": "仅最新版本",
"Os": "操作系统",
"Other": "其它",
"Hardware": "硬件信息",
"Password": "密码",
"PasswordWithoutSpecialCharHelpText": "不能包含特殊字符",
"Pending": "等待",
@@ -106,6 +137,7 @@
"RefreshHardware": "更新硬件信息",
"RemoteAppListHelpMessage": "使用此功能前,请确保已将应用加载器上传到应用服务器并成功发布为一个 RemoteApp 应用 <b><a href='https://github.com/jumpserver/Jmservisor/releases'>下载应用加载器</a></b>",
"RemoteApps": "远程应用",
"Applications": "应用",
"RemoteType": "应用类型",
"RemoveFromCurrentNode": "从节点移除",
"ReplaceNodeAssetsAdminUserWithThis": "替换资产的管理员",
@@ -133,7 +165,12 @@
"command_filter_list": "命令过滤器列表",
"date_joined": "创建日期",
"ip": "IP",
"sshkey": "sshkey"
"sshkey": "sshkey",
"GroupsHelpMessage": "请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)",
"HomeHelpMessage": "默认家目录 /home/系统用户名: /home/username",
"Home": "家目录",
"LinuxUserAffiliateGroup": "用户附属组",
"ipDomain": "IP(域名)"
},
"audits": {
@@ -148,15 +185,27 @@
},
"common": {
"Action": "动作",
"RequestTickets": "申请工单",
"Actions": "操作",
"CustomCol":"自定义列表字段",
"Activate": "激活",
"NeedSpecifiedFile": "需上传指定格式文件",
"TestPortErrorMsg":"端口错误,请重新输入",
"Active": "激活中",
"actionsTips":"剪切板权限控制目前仅支持 RDP/VNC 协议的连接",
"TableColSettingInfo": "请选择您想显示的列表详细信息。",
"Add": "添加",
"UpdateAssetDetail": "配置更多信息",
"AddSuccessMsg": "添加成功",
"Auth": "认证",
"PushSelected":"推送所选",
"BadRequestErrorMsg": "请求错误,请检查填写内容",
"BadRoleErrorMsg": "请求错误,无该操作权限",
"BadConflictErrorMsg": "正在刷新中,请稍后再试",
"Basic": "基本",
"PleaseAgreeToTheTerms": "请同意条款",
"BasicInfo": "基本信息",
"ApplyInfo": "申请信息",
"Cancel": "取消",
"Close": "关闭",
"Command filter": "命令过滤器",
@@ -166,6 +215,7 @@
"CreatedBy": "创建者",
"CrontabHelpTips": "eg每周日 03:05 执行 <5 3 * * 0> <br> 提示: 使用5位 Linux crontab 表达式 <分 时 日 月 星期> <a href='https://tool.lu/crontab/' target='_blank'>在线工具</a> <br>注意: 如果同时设置了定期执行和周期执行,优先使用定期执行",
"DateEnd": "结束日期",
"Resource": "资源",
"DateLast24Hours": "最近一天",
"DateLast3Months": "最近三月",
"DateLastMonth": "最近一月",
@@ -185,6 +235,7 @@
"MFARequireForSecurity": "为了安全请输入MFA",
"Members": "成员",
"More": "更多",
"Message": "消息",
"MoreActions": "更多操作",
"Name": "名称",
"No": "否",
@@ -202,11 +253,13 @@
"SelectFile": "选择文件",
"Show": "显示",
"Submit": "提交",
"SaveAndAddAnother": "保存并继续添加",
"Test": "测试",
"TestSuccessMsg": "测试成功",
"To": "至",
"Update": "更新",
"Upload": "上传",
"Clone": "克隆",
"Username": "用户名",
"Validity": "有效",
"Invalidity": "无效",
@@ -217,13 +270,17 @@
"bulkDeleteErrorMsg": "批量删除失败: ",
"bulkDeleteSuccessMsg": "批量删除成功",
"bulkRemoveErrorMsg": "批量移除失败: ",
"NeedAssetsAndSystemUserErrMsg": "请先选择授权的系统用户和资产",
"bulkRemoveSuccessMsg": "批量移除成功",
"createBy": "创建者",
"cloneFrom": "克隆自",
"createErrorMsg": "创建失败",
"createSuccessMsg": "创建成功",
"saveSuccessContinueMsg": "创建成功,更新内容后可以继续添加",
"createdBy": "创建人",
"dateCreated": "创建日期",
"dateExpired": "失效日期",
"dateFinished": "完成日期",
"dateStart": "开始日期",
"deleteErrorMsg": "删除失败",
"deleteFailedMsg": "删除失败",
@@ -245,6 +302,7 @@
"onlyCSVFilesTips": "仅支持csv文件导入",
"updateSuccessMsg": "导入更新成功,总共:{count}"
},
"fileType": "文件类型",
"isValid": "有效",
"nav": {
"APIKey": "API Key",
@@ -279,7 +337,8 @@
"NUMBER_REQUIRED": "须包含数字",
"SPECIAL_CHAR_REQUIRED": "须包含特殊字符",
"MIN_LENGTH_ERROR": "密码最小长度 {0} 位"
}
},
"lastCannotBeDeleteMsg": "最后一项,不能被删除"
},
"dashboard": {
"ActiveAsset": "近期被登录过",
@@ -359,7 +418,7 @@
},
"perms": {
"": "",
"Actions": "动作",
"Actions": "权限",
"Asset": "资产",
"Basic": "基本",
"Exclude": "不包含",
@@ -370,10 +429,16 @@
"SystemUser": "系统用户",
"User": "用户",
"UserGroups": "用户组",
"PermName":"授权名称",
"DatabaseAppPermission": "数据库授权",
"RemoteAppPermission": "远程应用授权",
"addApplicationToThisPermission": "添加应用",
"KubernetesAppPermission": "Kubernetes授权",
"addAssetToThisPermission": "添加资产",
"addDatabaseAppToThisPermission": "添加数据库应用",
"addNodeToThisPermission": "添加节点",
"addRemoteAppToThisPermission": "添加远程应用",
"addK8sAppToThisPermission": "添加Kubernetes应用",
"addSystemUserToThisPermission": "添加系统用户",
"addUserGroupToThisPermission": "添加用户组",
"addUserToThisPermission": "添加用户",
@@ -382,6 +447,7 @@
"assetCount": "资产数量",
"connect": "连接",
"databaseApp": "数据库应用",
"KubernetesApp": "Kubernetes",
"dateStart": "开始日期",
"downloadFile": "下载文件",
"hostName": "主机名",
@@ -392,15 +458,23 @@
"refreshSuccess": "刷新成功",
"remoteApp": "远程应用",
"remoteAppCount": "远程应用数量",
"appsCount": "应用数量",
"appsList":"应用列表",
"DatabaseAppCount": "数据库应用数量",
"KubernetesAppCount": "Kubernetes应用数量",
"systemUserCount": "系统用户数量",
"upDownload": "上传下载",
"uploadFile": "上传文件",
"clipboardCopyPaste":"复制粘贴",
"clipboardCopy":"剪切板复制",
"clipboardPaste":"剪切板粘贴",
"userCount": "用户数量",
"userGroupCount": "用户组数量",
"usersAndUserGroups": "用户或用户组"
},
"route": {
"": "",
"Ticket":"工单",
"AdminUserCreate": "创建管理用户",
"AdminUserDetail": "管理用户详情",
"AdminUserList": "管理用户",
@@ -431,7 +505,7 @@
"CreateCommandStorage": "创建命令存储",
"CreateReplayStorage": "创建录像存储",
"Dashboard": "仪表盘",
"DatabaseApp": "数据库应用",
"DatabaseApp": "数据库",
"DatabaseAppCreate": "创建数据库应用",
"DatabaseAppDetail": "数据库详情",
"DatabaseAppPermission": "数据库授权",
@@ -439,6 +513,15 @@
"DatabaseAppPermissionDetail": "数据库授权详情",
"DatabaseAppPermissionUpdate": "更新数据库授权规则",
"DatabaseAppUpdate": "数据库应用更新",
"KubernetesApp": "Kubernetes",
"KubernetesAppCreate": "创建Kubernetes",
"KubernetesAppDetail": "Kubernetes详情",
"KubernetesAppPermission": "Kubernetes授权",
"KubernetesAppPermissionCreate": "创建Kubernetes授权规则",
"KubernetesAppPermissionDetail": "Kubernetes授权详情",
"KubernetesAppPermissionUpdate": "更新Kubernetes授权规则",
"KubernetesAppUpdate": "更新Kubernetes",
"DomainCreate": "创建网域",
"DomainDetail": "网域详情",
"DomainList": "网域列表",
@@ -468,6 +551,11 @@
"RemoteAppPermissionCreate": "创建远程应用授权规则",
"RemoteAppPermissionDetail": "远程应用授权详情",
"RemoteAppPermissionUpdate": "更新远程应用授权规则",
"ApplicationDetail": "应用详情",
"ApplicationPermission": "应用授权",
"ApplicationPermissionCreate": "创建应用授权规则",
"ApplicationPermissionDetail": "应用授权详情",
"ApplicationPermissionUpdate": "更新应用授权规则",
"RemoteAppUpdate": "更新远程应用",
"ReplayStorageUpdate": "更新录像存储",
"SessionDetail": "会话详情",
@@ -484,6 +572,7 @@
"TaskMonitor": "任务监控",
"Terminal": "终端管理",
"TicketDetail": "工单详情",
"TicketCreate": "创建工单",
"Tickets": "工单管理",
"UserCreate": "创建用户",
"UserDetail": "用户详情",
@@ -507,6 +596,7 @@
"active": "激活中",
"alive": "在线",
"asset": "资产",
"target": "目标",
"bucket": "桶名称",
"command": "命令",
"commandStorage": "命令存储",
@@ -534,6 +624,10 @@
"name": "名称",
"protocol": "协议",
"region": "地域",
"sessionActiveCount": "在线会话数量",
"systemCpuLoad": "CPU负载",
"systemDiskUsedPercent": "硬盘使用率",
"systemMemoryUsedPercent": "内存使用率",
"remoteAddr": "远端地址",
"replay": "回放",
"replaySession": "回放会话",
@@ -545,7 +639,9 @@
"systemUser": "系统用户",
"terminalDetail": "终端详情",
"terminalUpdate": "更新终端",
"terminalUpdateStorage": "更新终端存储",
"terminate": "终断",
"sessionTerminate": "会话终断",
"test": "测试",
"type": "类型",
"user": "用户",
@@ -553,7 +649,15 @@
"common": "普通"
},
"Monitor": "监控",
"TerminateTaskSendSuccessMsg": "终断任务已下发,请稍后刷新表格查看"
"sessionMonitor": "监控",
"TerminateTaskSendSuccessMsg": "终断任务已下发,请稍后刷新查看",
"helpText": {
"esUrl": "提示:如果有多台主机,请使用逗号 ( , ) 进行分割。eg: http://www.jumpserver.a.com,http://www.jumpserver.b.com",
"esIndex": "es提供默认indexjumpserver",
"esDocType": "es默认文档类型command",
"s3Endpoint": "S3 格式: http://s3.{REGION_NAME}.amazonaws.com<br>S3(China) 格式: http://s3.{REGION_NAME}.amazonaws.com.cn<br>如: http://s3.cn-north-1.amazonaws.com.cn",
"ossEndpoint": "OSS 格式: http://{REGION_NAME}.aliyuncs.com<br>如: http://oss-cn-hangzhou.aliyuncs.com"
}
},
"setting": {
"ApiKeyList": "API Key 列表",
@@ -584,6 +688,7 @@
"authLdapSearchOu": "用户OU",
"authLdapServerUri": "LDAP地址",
"authLdapUserAttrMap": "LDAP属性映射",
"unselectedUser": "没有选择用户",
"auto": "自动",
"basicSetting": "基本设置",
"communityEdition": "社区版",
@@ -603,6 +708,9 @@
"emailTest": "测试连接",
"emailUserSSL": "使用SSL",
"emailUserTLS": "使用TLS",
"SecurityInsecureCommand": "危险命令告警",
"Insecure_Command_Alert": "危险命令告警",
"SecurityInsecureCommandEmailReceiver": "告警接收邮件",
"helpText": {
"ApiKeyList": "使用api key签名请求头每个请求的头部是不一样的, 请查阅使用文档",
"authLdapSearchFilter": "可能的选项是(cn或uid或sAMAccountName=%(user)s)",
@@ -623,7 +731,8 @@
"terminalHeartbeatInterval": "单位: 秒",
"terminalSessionKeepDuration": "单位:天。 会话、录像、命令记录超过该时长将会被删除(仅影响数据库存储, oss等不受影响)",
"terminalTelnetRegex": "登录telnet服务器成功后的提示正则表达式如: Last\\s*login|success|成功",
"userGuideUrl": "用户第一次登录修改profile后重定向到地址"
"userGuideUrl": "用户第一次登录修改profile后重定向到地址",
"SecurityInsecureCommandEmailReceiver": "多个邮箱时,以半角逗号','分隔"
},
"helpTip": {
"emailUserSSL": "如果SMTP端口是465通常需要启用SSL",
@@ -634,7 +743,8 @@
"securityPasswordNumber": "开启后,用户密码修改、重置必须包含数字字符",
"securityPasswordSpecialChar": "开启后,用户密码修改、重置必须包含特殊字符",
"securityPasswordUpperCase": "开启后,用户密码修改、重置必须包含大写字母",
"securityServiceAccountRegistration": "允许使用bootstrap token注册终端, 当终端注册成功后可以禁止"
"securityServiceAccountRegistration": "允许使用bootstrap token注册终端, 当终端注册成功后可以禁止",
"SecurityInsecureCommand": "开启后,当资产上有危险命令执行时,会发送邮件告警通知"
},
"validatorMessage": {
"EnsureThisValueIsGreaterThanOrEqualTo3": "请确保该值大于或者等于 3",
@@ -676,28 +786,57 @@
"userGuideUrl": "用户向导URL",
"username": "用户名",
"usernamePlaceholder": "请输入用户名",
"refreshLdapCache":"刷新Ldap缓存请稍后"
"refreshLdapCache":"刷新Ldap缓存请稍后",
"LicenseExpired": "许可证已经过期",
"LicenseWillBe": "许可证即将在 ",
"Expire": " 过期"
},
"settings": {
"setting": "设置"
},
"tickets": {
"Accept": "接受",
"AssignedMe": "待处理",
"Accept": "同意",
"AssignedMe": "待我审批",
"Assignee": "处理人",
"Assignees": "待处理人",
"Close": "关闭",
"OpenStatus":"开启",
"CloseStatus":"关闭",
"Comment": "备注",
"MyTickets": "我的工单",
"MyTickets": "我发起的",
"RequestPerm":"授权申请",
"Reject": "拒绝",
"date": "日期",
"reply": "回复",
"status": "状态",
"title": "标题",
"action": "动作",
"IPGroup": "IP 组",
"type": "类型",
"user": "用户",
"Status": "状态",
"Open": "打开"
"Open": "待处理",
"OrgName":"组织名称",
"AssignedInfo":"审批信息",
"OpenTicket": "创建工单",
"HandleTicket": "处理工单",
"FinishedTicket": "完成工单",
"IP": "IP",
"Hostname": "主机名",
"Asset": "资产",
"SystemUser": "系统用户",
"RequestAssetPerm": "申请资产授权",
"RequestApplicationPerm": "申请应用授权",
"Applicant": "申请人",
"Pending": "待处理",
"Approved": "已同意",
"Rejected": "已拒绝",
"Closed": "已完成",
"helpText": {
"ips": "请输入逗号分割的IP地址组",
"fuzzySearch": "支持模糊搜索",
"application": "请输入逗号分割的应用名称组"
}
},
"tree": {
"AddAssetToNode": "添加资产到节点",
@@ -707,6 +846,7 @@
"RenameNode": "重命名节点",
"ShowAssetAllChildrenNode": "显示所有子节点资产",
"ShowAssetOnlyCurrentNode": "仅显示当前节点资产",
"CheckAssetsAmount": "校对资产数量",
"ShowNodeInfo": "显示节点详情",
"TestNodeAssetConnectivity": "测试资产节点可连接性",
"UpdateNodeAssetHardwareInfo": "更新节点资产硬件信息"
@@ -725,11 +865,18 @@
"Email": "邮件",
"FingerPrint": "指纹",
"FirstLogin": "首次登录",
"OrgUser": "组织用户",
"OrgAdmin": "组织管理员",
"OrgAuditor": "组织审计员",
"InviteUser": "邀请用户",
"Invite": "邀请",
"InviteUserInOrg": "邀请用户加入此组织",
"Guide": "向导",
"HelpText": {
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "启用多因子认证,使账号更加安全。<br/> 启用之后您将会在下次登录时进入多因子认证绑定流程;您也可以在(个人信息->快速修改->更改多因子设置)中直接绑定!",
"MFAOfUserFirstLoginUserGuidePage": "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:设置复杂密码,并启用多因子认证)",
"SSHKeyOfProfileSSHUpdatePage": "复制你的公钥到这里"
"SSHKeyOfProfileSSHUpdatePage": "复制你的公钥到这里",
"OrgRoleHelpText": "组织角色是用户在当前组织中的角色"
},
"IAgree": "我同意",
"ImprovePersonalInformation": "完善个人信息",
@@ -737,6 +884,7 @@
"LoginConfirm": "登录复核",
"LoginPasswordSetting": "登录密码设置",
"MFA": "MFA",
"Existing":"已存在",
"MfaLevel": "多因子认证",
"Name": "姓名",
"NewPassword": "新密码",
@@ -748,6 +896,8 @@
"ResetAndDownloadSSHKey": "重置并下载密钥",
"ResetPublicKeyAndDownload": "重置并下载SSH密钥",
"Role": "角色",
"SuperRole": "系统角色",
"OrgRole": "组织角色",
"SSHKey": "SSH公钥",
"SSHKeySetting": "SSH公钥设置",
"Secure": "安全",
@@ -778,14 +928,21 @@
"tabs": {
"assetPermissionRules": "资产授权规则",
"databasePermissionRules": "数据库授权规则",
"k8sPermissionRules": "Kubernetes授权规则",
"grantedAssets": "授权的资产",
"grantedK8Ss": "授权的Kubernetes",
"grantedDatabases": "授权的数据库",
"grantedRemoteApps": "授权的远程应用",
"grantedApplications": "授权的应用",
"ApplicationPermissionRules": "应用授权规则",
"remoteAppPermissionRules": "远程应用授权规则"
},
"dateLastLogin": "最后登录日期",
"UpdatePassword": "更新密码",
"SetPublicKey": "设置SSH公钥"
"SetPublicKey": "设置SSH公钥",
"passwordExpired": "密码过期了",
"passwordWillExpiredPrefixMsg": "密码即将在 ",
"passwordWillExpiredSuffixMsg": "天 后过期,请尽快修改您的密码。"
},
"xpack": {
"Admin": "管理员",
@@ -810,6 +967,9 @@
"ExecutionDetail": "执行详情",
"ExecutionList": "执行列表",
"ExecutionTimes": "执行次数",
"validatorMessage": {
"EnsureThisValueIsGreaterThanOrEqualTo1": "请确保该值大于或者等于 1"
},
"HelpText": {
"CrontabOfCreateUpdatePage": "例如:每周日 03:05 执行 <5 3 * * 0> <br/> 使用5位 Linux crontab 表达式 <分 时 日 月 星期> <a href=\"https://tool.lu/crontab/\" target=\"_blank\">在线工具</a> <br/> 如果同时设置了定期执行和周期执行,优先使用定期执行",
"IntervalOfCreateUpdatePage": "单位:时",
@@ -831,12 +991,22 @@
"Username": "用户名"
},
"Cloud": {
"Aliyun": "阿里云",
"Qcloud": "腾讯云",
"AWS_China": "AWS(中国)",
"AWS_Int": "AWS(国际)",
"HuaweiCloud": "华为云",
"Azure":"Azure(中国)",
"HostnameStrategy": "用于生成资产主机名。例如1. 实例名称 (instanceDemo)2. 实例名称和部分IP(后两位) (instanceDemo-250.1)",
"IsAlwaysUpdate": "资产信息保持最新",
"AccountCreate": "创建账户",
"AccountList": "账户列表",
"AccountUpdate": "更新账户",
"AccountDetail": "账户详情",
"Cloud": "云管中心",
"CloudCenter": "云管中心",
"Provider": "云服务商",
"Validity": "有效",
"IsAlwaysUpdateHelpTips": "每次执行同步任务时是否同步更新资产的信息包括主机名、IP、系统平台、管理用户",
"SyncInstanceTaskCreate": "创建同步实例任务",
"SyncInstanceTaskList": "同步实例任务列表",
@@ -886,6 +1056,13 @@
"ImportLicenseTip": "请导入许可证",
"InterfaceSettings": "界面设置",
"License": "许可证",
"SystemMonitor": "系统监控",
"ServiceRatio": "组件负载统计",
"LoadStatus":"组件状态",
"NormalLoad":"正常",
"HighLoad":"较高",
"CriticalLoad":"严重",
"Offline": "离线",
"LicenseDetail": "许可证详情",
"LicenseFile": "许可证文件",
"NoLicense": "暂无许可证",
@@ -895,8 +1072,20 @@
"OrganizationDetail": "组织详情",
"OrganizationList": "组织管理",
"OrganizationUpdate": "更新组织",
"OrganizationMembership": "组织成员",
"DeleteOrgTitle": "请确保组织内的以下信息已删除",
"DeleteOrgMsg": "用户列表、用户组、资产列表、网域列表、管理用户、系统用户、标签管理、资产授权规则"
"DeleteOrgMsg": "用户列表、用户组、资产列表、网域列表、管理用户、系统用户、标签管理、资产授权规则",
"OrgRole": "组织角色",
"users_amount": "用户数量",
"groups_amount": "用户组数量",
"assets_amount": "资产数量",
"admin_users_amount": "管理用户数量",
"system_users_amount": "系统用户数量",
"applications_amount": "应用数量",
"asset_perms_amount": "资产授权数量",
"app_perms_amount": "应用授权数量",
"CreateOrgMsg": "请去组织详情内添加用户",
"AddOrgMembers": "添加组织成员"
},
"RestoreButton": "恢复默认",
"SubscriptionID": "订阅授权ID",
@@ -913,9 +1102,9 @@
"loginImageTip": "提示:将会显示在企业版用户登录页面(建议图片大小为: 492*472px",
"loginTitle": "登录页面标题",
"loginTitleTip": "提示将会显示在企业版用户登录页面eg: 欢迎使用JumpServer开源堡垒机)",
"logoIndex": "管理页面logo",
"logoIndex": "Logo (带文字)",
"logoIndexTip": "提示:将会显示在管理页面左上方(建议图片大小为: 185px*55px",
"logoLogout": "退出页面logo",
"logoLogout": "Logo (不带文字)",
"logoLogoutTip": "提示将会显示在企业版用户退出页面建议图片大小为82px*82px",
"restoreDialogMessage": "您确定要恢复默认初始化吗?",
"restoreDialogTitle": "你确认吗",

View File

@@ -2,8 +2,25 @@
"": "",
"applications": {
"": "",
"applicationsType": {
"chrome": "Chrome",
"mysql_workbench": "MySQL Workbench",
"vmware_client":"Vmware Client",
"custom":"Custom",
"mysql": "MySQL",
"oracle": "Oracle",
"postgresql": "PostgreSQL",
"mariadb": "MariaDB",
"k8s": "kubernetes"
},
"applicationsCategory": {
"remote_app": "Remote app",
"db": "Database app",
"cloud": "Cloud app"
},
"appPath": "App path",
"appType": "App type",
"appName": "App name",
"asset": "Asset",
"database": "Database",
"host": "Host",
@@ -17,6 +34,7 @@
"mysql_workbench": "MySQL Workbench",
"mysql_workbench_ip": "DB IP",
"mysql_workbench_name": "DB Name",
"mysql_workbench_port": "DB Port",
"mysql_workbench_username": "DB Account",
"mysql_workbench_password": "DB Password",
"vmware_client": "vSphere Client",
@@ -28,20 +46,31 @@
"custom_target": "target URL",
"custom_username": "Account",
"custom_password": "Password",
"Custom": "Custom"
"Custom": "Custom",
"cluster": "Cluster",
"kubernetes":"Kubernetes",
"clusterHelpTextMessage": "Tips: https://172.16.8.8:8443",
"DBInfo": "Database Info"
},
"assets": {
"Action": "Action",
"ActiveSelected": "Active selected",
"AdminUser": "Admin user",
"ReplaceNodeAssetsAdminUser":"Replace node assets admin user with this",
"AdminUserDetail": "Admin user detail",
"AdminUserListHelpMessage": "Admin users are asset (charged server) on the root, or have NOPASSWD: ALL sudo permissions users, JumpServer users of the system using the user to `push system user`, `get assets hardware information`, etc.\n",
"Asset": "Asset",
"HardwareInfo": "Hardware info",
"Hardware": "Hardware",
"AccountList": "Account list",
"AssetDetail": "Asset detail",
"AssetList": "Asset list",
"AssetListHelpMessage": "The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node\n",
"AssetNumber": "Asset number",
"AssetUserList": "Asset user list",
"TestGatewayTestConnection":"Test gateway test connection",
"TestGatewayHelpMessage": "If use nat, set the ssh real port",
"SshPort": "SSH Port",
"Assets": "Assets",
"Auth": "Auth",
"AutoGenerateKey": "Auto generate ssh key",
@@ -63,6 +92,8 @@
"DateUpdated": "Date updated",
"DeactiveSelected": "Deactive selected",
"Disk": "Disk",
"AdDomain": "AD Domain",
"AdDomainHelpText": "AD domain provided to domain users for login",
"Domain": "Domain",
"DomainDetail": "Domain detail",
"DomainHelpMessage": "The domain function is added to address the fact that some environments (such as the hybrid cloud) cannot be connected directly by jumping on the gateway server.\nJMS => Domain gateway => Target assets",
@@ -106,6 +137,7 @@
"RefreshHardware": "Refresh hardware",
"RemoteAppListHelpMessage": "Before using this feature, make sure that the application loader has been uploaded to the application server and successfully published as a RemoteApp application <b><a href='https://github.com/jumpserver/Jmservisor/releases'> Download application loader</a></b>",
"RemoteApps": "Remote apps",
"Applications": "Applications",
"RemoteType": "Remote type",
"RemoveFromCurrentNode": "Remove from node",
"ReplaceNodeAssetsAdminUserWithThis": "Replace node assets admin user with this",
@@ -133,7 +165,12 @@
"command_filter_list": "Command filter list",
"date_joined": "Date joined",
"ip": "IP",
"sshkey": "sshkey"
"sshkey": "sshkey",
"GroupsHelpMessage": "Please fill in user groups, separated by commas if there are multiple user groups(Please fill in the existing user groups)",
"HomeHelpMessage": "Default home directory: /home/system username",
"Home": "Home",
"LinuxUserAffiliateGroup": "Linux user affiliate group",
"ipDomain": "IP(Domain)"
},
"audits": {
"Hosts": "Host",
@@ -148,15 +185,27 @@
"common": {
"Nothing": "Nothing",
"Action": "Action",
"CustomCol":"Custom table col",
"RequestTickets": "Request tickets",
"Actions": "Actions",
"NeedSpecifiedFile": "Required to upload the specified format file",
"TestPortErrorMsg":"Port Error, please check",
"Activate": "Activate",
"actionsTips":"Clipboard's copy and paste control only support RDP/VNC protocol.",
"Active": "Active",
"TableColSettingInfo": "Please select the list details you want to display",
"Add": "Add",
"PleaseAgreeToTheTerms": "Please agree to the terms",
"PushSelected":"Push selected",
"UpdateAssetDetail": "Update more detail",
"AddSuccessMsg": "Add success",
"Auth": "Authorization",
"BadRequestErrorMsg": "Bad request, please check again",
"BadRoleErrorMsg": "Bad request, no permission for this operation",
"BadConflictErrorMsg": "Refreshing, please try again later",
"Basic": "Basic",
"BasicInfo": "Basic info",
"ApplyInfo": "Apply info",
"Cancel": "Cancel",
"Close": "Close",
"Command filter": "Command filter",
@@ -166,6 +215,7 @@
"CreatedBy": "Created by",
"CrontabHelpTips": "eg: Every Sunday 03:05 run <5 3 * * 0> <br>Tips:Using 5 digits linux crontab expressions<min hour day month week> (<a href='https://tool.lu/crontab/' target='_blank'>Online tools</a>) <br>Note:If both Regularly perform and Cycle perform are set,give priority to Regularly perform",
"DateEnd": "End date",
"Resource": "Resource",
"DateLast24Hours": "Last 24 hours",
"DateLast3Months": "Last 3 months",
"DateLastMonth": "Last month",
@@ -185,6 +235,7 @@
"MFARequireForSecurity": "MFA required for security",
"Members": "Members",
"More": "More",
"Message": "Message",
"MoreActions": "Actions",
"Name": "Name",
"No": "No",
@@ -207,6 +258,7 @@
"To": "To",
"Update": "Update",
"Upload": "Upload",
"Clone": "Clone",
"Username": "Username",
"Validity": "Validity",
"Invalidity": "Invalidity",
@@ -218,11 +270,15 @@
"bulkDeleteSuccessMsg": "Bulk delete success",
"bulkRemoveErrorMsg": "Bulk remove failed: ",
"bulkRemoveSuccessMsg": "Bulk remove success",
"NeedAssetsAndSystemUserErrMsg": "Need assets and systemuser",
"createBy": "Create by",
"cloneFrom": "Clone from",
"createErrorMsg": "Create error",
"createSuccessMsg": "Create success",
"saveSuccessContinueMsg": "Create success, you may add another",
"createdBy": "Created by",
"dateCreated": "Date created",
"dateFinished": "Date finished",
"dateExpired": "Date expired",
"dateStart": "Date start",
"deleteErrorMsg": "Delete failed",
@@ -233,6 +289,7 @@
"disableSelected": "Disable selected",
"fieldRequiredError": "This field is required",
"getErrorMsg": "Get failed",
"fileType": "File type",
"imExport": {
"ExportAll": "Export all",
"ExportOnlyFiltered": "Export only filtered",
@@ -278,7 +335,8 @@
"NUMBER_REQUIRED": "Number required",
"SPECIAL_CHAR_REQUIRED": "Special char required",
"MIN_LENGTH_ERROR": "Password minimum length {}"
}
},
"lastCannotBeDeleteMsg": "The last one can't be delete"
},
"dashboard": {
"ActiveAsset": "Asset active",
@@ -358,7 +416,7 @@
},
"perms": {
"": "",
"Actions": "Actions",
"Actions": "Permission",
"Asset": "Asset",
"Basic": "Basic",
"Exclude": "Exclude",
@@ -369,18 +427,25 @@
"SystemUser": "System user",
"User": "User",
"UserGroups": "UserGroups",
"DatabaseAppPermission": "Databases permissions",
"RemoteAppPermission": "Remote apps permissions",
"KubernetesAppPermission": "Kubernetes permissions",
"addAssetToThisPermission": "Add asset to this permission",
"addDatabaseAppToThisPermission": "Add DatabaseApp to this permission",
"addK8sAppToThisPermission": "Add KubernetesApp to this permission",
"addApplicationToThisPermission": "Add Application to this permission",
"addNodeToThisPermission": "Add node to this permission",
"addRemoteAppToThisPermission": "Add RemoteApp to this permission",
"addSystemUserToThisPermission": "System user",
"addUserGroupToThisPermission": "Add user group to this permission",
"addUserToThisPermission": "Add user to this permission",
"all": "All",
"PermName":"Perm name",
"assetAndNode": "Assets and node",
"assetCount": "Asset count",
"connect": "Connect",
"databaseApp": "DatabaseApp",
"KubernetesApp": "KubernetesApp",
"dateStart": "Date start",
"downloadFile": "Download file",
"hostName": "Hostname",
@@ -391,15 +456,23 @@
"refreshSuccess": "Refresh success",
"remoteApp": "RemoteApp",
"remoteAppCount": "RemoteApp count",
"appsCount": "App count",
"appsList":"App list",
"DatabaseAppCount": "DatabaseApp count",
"KubernetesAppCount": "KubernetesApp count",
"systemUserCount": "System user count",
"upDownload": "Upload download",
"uploadFile": "Upload file",
"clipboardCopyPaste":"Copy Paste",
"clipboardCopy":"Clipboard copy",
"clipboardPaste":"Clipboard paste",
"userCount": "User count",
"userGroupCount": "User group count",
"usersAndUserGroups": "Users and user groups"
},
"route": {
"": "",
"Ticket": "Tickets",
"AdminUserCreate": "Admin user create",
"AdminUserDetail": "Admin user detail",
"AdminUserList": "Admin users",
@@ -438,6 +511,14 @@
"DatabaseAppPermissionDetail": "Databases permissions detail",
"DatabaseAppPermissionUpdate": "Databases permissions update",
"DatabaseAppUpdate": "Database app update",
"KubernetesApp": "Kubernetes apps",
"KubernetesAppCreate": "Kubernetes app create",
"KubernetesAppDetail": "Kubernetes app detail",
"KubernetesAppPermission": "Kubernetes permissions",
"KubernetesAppPermissionCreate": "Kubernetes permissions create",
"KubernetesAppPermissionDetail": "Kubernetes permissions detail",
"KubernetesAppPermissionUpdate": "Kubernetes permissions update",
"KubernetesAppUpdate": "Kubernetes app update",
"DomainCreate": "Domain create",
"DomainDetail": "Domain detail",
"DomainList": "Domains",
@@ -464,6 +545,7 @@
"RemoteApp": "Remote apps",
"RemoteAppDetail": "Remote app detail",
"RemoteAppPermission": "Remote apps permissions",
"ApplicationPermission": "Application permissions",
"RemoteAppPermissionCreate": "Remote apps permission create",
"RemoteAppPermissionDetail": "Remote apps permissions detail",
"RemoteAppPermissionUpdate": "Remote app permission update",
@@ -483,6 +565,7 @@
"TaskMonitor": "Task monitor",
"Terminal": "Terminal",
"TicketDetail": "Ticket detail",
"TicketCreate": "Ticket create",
"Tickets": "Tickets",
"UserCreate": "User create",
"UserDetail": "User detail",
@@ -506,6 +589,7 @@
"active": "active",
"alive": "alive",
"asset": "Asset",
"target": "Target",
"bucket": "Bucket",
"command": "Command",
"commandStorage": "Command storage",
@@ -521,6 +605,10 @@
"duration": "Duration",
"endPoint": "Endpoint",
"endpointSuffix": "Endpoint suffix",
"sessionActiveCount": "session active count",
"systemCpuLoad": "cpu load",
"systemDiskUsedPercent": "disk used percent",
"systemMemoryUsedPercent": "memory used percent",
"go": "Go",
"goto": "Goto",
"hosts": "Hosts",
@@ -544,7 +632,9 @@
"systemUser": "System user",
"terminalDetail": "Terminal detail",
"terminalUpdate": "Update terminal",
"terminalUpdateStorage": "Update terminal storage",
"terminate": "Terminate",
"sessionTerminate": "Session Terminate",
"test": "Test",
"type": "Type",
"user": "Use",
@@ -552,7 +642,15 @@
"common": "common"
},
"Monitor": "Monitor",
"TerminateTaskSendSuccessMsg": "Terminate task has been send, Please check later"
"sessionMonitor": "Session Monitor",
"TerminateTaskSendSuccessMsg": "Terminate task has been send, Please check later",
"helpText": {
"esUrl": "Tip: If you have multiple hosts, use comma (,) to split (eg: http://www.jumpserver.a.com,http://www.jumpserver.b.com)",
"esIndex":"Es provides the default index: jumpserver",
"esDocType": "Es provides the default document type: command",
"s3Endpoint": "S3: http://s3.{REGION_NAME}.amazonaws.com<br>S3(China): http://s3.{REGION_NAME}.amazonaws.com.cn<br>Example: http://s3.cn-north-1.amazonaws.com.cn",
"ossEndpoint": "OSS: http://{REGION_NAME}.aliyuncs.com<br>Example: http://oss-cn-hangzhou.aliyuncs.com"
}
},
"setting": {
"ApiKeyList": "Api key list",
@@ -583,6 +681,7 @@
"authLdapSearchOu": "User OU",
"authLdapServerUri": "LDAP server",
"authLdapUserAttrMap": "User attr map",
"unselectedUser": "Unselected user",
"auto": "Auto",
"basicSetting": "Basic setting",
"communityEdition": "Community edition",
@@ -675,7 +774,10 @@
"userGuideUrl": "User Guide URL",
"username": "Username",
"usernamePlaceholder": "Please input username",
"refreshLdapCache":"Refreshing Ldap cache "
"refreshLdapCache":"Refreshing Ldap cache ",
"LicenseExpired": "License expired",
"LicenseWillBe": "License will expire at ",
"Expire": ""
},
"settings": {
"setting": "Setting"
@@ -684,10 +786,19 @@
"Accept": "Accept",
"AssignedMe": "Assigned me",
"Assignee": "Assignee",
"RequestPerm":"Request Perm",
"AssignedInfo":"Assigned Info",
"OpenTicket": "Open Ticket",
"HandleTicket": "Handle Ticket",
"FinishedTicket": "Finished Ticket",
"Assignees": "Assignees",
"Close": "Close",
"OpenStatus":"Open",
"CloseStatus":"Close",
"Comment": "Comment",
"MyTickets": "My tickets",
"action": "Action",
"IPGroup": "IP 组",
"Reject": "Reject",
"date": "Date",
"reply": "Reply",
@@ -696,7 +807,24 @@
"type": "Type",
"user": "User",
"Status": "Status",
"Open": "Open"
"Open": "Open",
"OrgName":"Org name",
"IP": "IP",
"Hostname": "Hostname",
"Asset": "Asset",
"SystemUser": "System user",
"Applicant": "Applicant",
"RequestAssetPerm": "Request asset perm",
"RequestApplicationPerm": "Request application perm",
"Pending": "Open",
"Approved": "Approved",
"Rejected": "Rejected",
"Closed": "Closed",
"helpText": {
"ips": "Enter the IP address group, separated by commas",
"fuzzySearch": "Support for fuzzy search",
"application": "Enter the application group, separated by commas"
}
},
"tree": {
"AddAssetToNode": "Add asset to node",
@@ -706,12 +834,14 @@
"RenameNode": "Rename node",
"ShowAssetAllChildrenNode": "Show asset all children node",
"ShowAssetOnlyCurrentNode": "Show asset only current node",
"CheckAssetsAmount": "Check assets amount",
"ShowNodeInfo": "Show node information",
"TestNodeAssetConnectivity": "Test node asset connectivity",
"UpdateNodeAssetHardwareInfo": "Update node asset hardware information"
},
"users": {
"Account": "Account",
"Existing":"Existing",
"Authentication": "Account",
"Comment": "Comment",
"ConfirmPassword": "Confirm password",
@@ -724,11 +854,19 @@
"Email": "Email",
"FingerPrint": "Fingerprint",
"FirstLogin": "First login",
"InviteUser": "Invite user",
"InviteUserInOrg": "Invite user in this org",
"Invite": "Invite",
"Guide": "Guide",
"OrgUser": "Org User",
"OrgAdmin": "Org Admin",
"OrgAuditor": "Org Auditor",
"HelpText": {
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "Enable multi-factor authentication to make the account more secure <br/> After is enabled, you will enter the multi-factor authentication binding process on your next login <br/> You can also bind directly in (personal information -> fast modifier -> modifier multiple factor Settings)",
"MFAOfUserFirstLoginUserGuidePage": "To protect the security of you and the company <br/> please properly keep your account, password, key and other important and sensitive information <br/> (e.g., set a complex password and enable multi-factor authentication)",
"SSHKeyOfProfileSSHUpdatePage": "Copy your public key here"
"SSHKeyOfProfileSSHUpdatePage": "Copy your public key here",
"OrgRoleHelpText": "Organizational roles are the user's role in the current organization"
},
"IAgree": "I agree",
"ImprovePersonalInformation": "Improve personal information",
@@ -747,6 +885,8 @@
"ResetAndDownloadSSHKey": "Reset and download SSH Key",
"ResetPublicKeyAndDownload": "Reset public key and download",
"Role": "Role",
"SuperRole": "Super role",
"OrgRole": "Org role",
"SSHKey": "SSH Key",
"SSHKeySetting": "SSH Key setting",
"Secure": "Secure",
@@ -777,13 +917,20 @@
"tabs": {
"assetPermissionRules": "Asset permission rules",
"databasePermissionRules": "Database Permission rules",
"k8sPermissionRules": "Kubernetes Permission rules",
"grantedAssets": "Granted assets",
"grantedK8Ss":"Granted K8Ss",
"grantedDatabases": "Granted databases",
"grantedRemoteApps": "Granted remote apps",
"grantedApplications": "Granted applications",
"ApplicationPermissionRules": "Application permission rules",
"remoteAppPermissionRules": "Remote app permission rules"
},
"UpdatePassword": "",
"UpdatePublicKey": ""
"UpdatePublicKey": "",
"passwordExpired": "Password expired",
"passwordWillExpiredPrefixMsg": "The password will expire in ",
"passwordWillExpiredSuffixMsg": " days.Please change your password as soon as possible."
},
"xpack": {
"Admin": "Admin",
@@ -808,6 +955,9 @@
"ExecutionDetail": "Execution detail",
"ExecutionList": "Execution list",
"ExecutionTimes": "Execution times",
"validatorMessage": {
"EnsureThisValueIsGreaterThanOrEqualTo1": "Ensure this value is greater than or equal to 1"
},
"HelpText": {
"CrontabOfCreateUpdatePage": "For example: every Sunday at 03:05 execute <5 3 * * 0> <br/> Using the 5-bit Linux crontab expression <minute hour day month week> (<a href=\"https://tool.lu/crontab/\" target=\"_blank\"> Online tool </a>) <br/> If both regularly perform and cycle perform execution are set, use regularly perform first",
"IntervalOfCreateUpdatePage": "Unit: hour",
@@ -829,12 +979,22 @@
"Username": "Username"
},
"Cloud": {
"Aliyun": "Ali Cloud",
"Qcloud": "Tencent Cloud",
"AWS_China": "AWS(China)",
"AWS_Int": "AWS(International)",
"HuaweiCloud": "Huawei Cloud",
"Azure":"Azure(China)",
"HostnameStrategy": "Used to produce the asset hostname. For example, 1. Instance name (instanceDemo)2. Instance name and Partial IP (instanceDemo-250.1)",
"IsAlwaysUpdate": "Asset info is kept up-to-date",
"AccountCreate": "Create account",
"AccountList": "Account list",
"AccountUpdate": "Update account",
"AccountDetail": "Account detail",
"Cloud": "Cloud center",
"CloudCenter": "Cloud center",
"Provider": "Provider",
"Validity": "Validity",
"IsAlwaysUpdateHelpTips": "Whether the asset information, including Hostname, IP, Platform, and AdminUser, is updated synchronously each time a synchronization task is performed",
"SyncInstanceTaskCreate": "Create sync instance task",
"SyncInstanceTaskList": "Sync instance task list",
@@ -885,6 +1045,13 @@
"InterfaceSettings": "Interface setting",
"License": "License",
"LicenseDetail": "License detail",
"SystemMonitor": "System Monitor",
"ServiceRatio": "Service ratio",
"LoadStatus":"Status",
"NormalLoad":"Normal",
"HighLoad":"High",
"Offline": "Offline",
"CriticalLoad":"Critical",
"LicenseFile": "License file",
"NoLicense": "No License",
"Node": "Node",
@@ -893,8 +1060,20 @@
"OrganizationDetail": "Org detail",
"OrganizationList": "Organlizations",
"OrganizationUpdate": "Update org",
"OrganizationMembership": "Organization membership",
"DeleteOrgTitle":"Please ensure that the following information in the organization has been deleted",
"DeleteOrgMsg":"User list、User group、Asset list、Domain list、Admin user、System user、Labels、Asset permission"
"DeleteOrgMsg":"User list、User group、Asset list、Domain list、Admin user、System user、Labels、Asset permission",
"OrgRole": "Org role",
"CreateOrgMsg": "Please go to Organization Details to add users",
"AddOrgMembers": "Add organization members",
"users_amount": "Users amount",
"groups_amount": "Groups amount",
"assets_amount": "Assets amount",
"admin_users_amount": "Admin users amount",
"system_users_amount": "System users amount",
"applications_amount": "Applications amount",
"asset_perms_amount": "Asset perms amount",
"app_perms_amount": "App perms amount"
},
"RestoreButton": "Restore Default",
"SubscriptionID": "Subscription ID",
@@ -911,9 +1090,9 @@
"loginImageTip": "Tips: This will be displayed on the enterprise user login page. (suggest image size: 492px*472px)",
"loginTitle": "Title of login page",
"loginTitleTip": "Tips: This will be displayed on the enterprise user login page. (eg: Welcome to the JumpServer open source fortress)",
"logoIndex": "Logo of management page",
"logoIndex": "Logo (It contains text)",
"logoIndexTip": "Tips: This will appear at the top left of the administration page. (suggest image size: 185px*55px)",
"logoLogout": "Logo of logout page",
"logoLogout": "Logo (It contains no text)",
"logoLogoutTip": "Tips: This will be displayed on the enterprise user logout page. (suggest image size: 82px*82px)",
"restoreDialogMessage": "This will restore default Settings of the interface !!!",
"restoreDialogTitle": "Are you sure?",

View File

@@ -1,10 +1,10 @@
<template>
<div class="footer" :style="style">
<div class="pull-right">
Version <strong>2.0.2</strong> <span v-if="!publicSettings.XPACK_LICENSE_IS_VALID"> GPLv2. </span>
Version <strong> dev </strong> <span v-if="!publicSettings.XPACK_LICENSE_IS_VALID"> GPLv2. </span>
</div>
<div v-if="!publicSettings.XPACK_LICENSE_IS_VALID" style="padding-left:20px;">
<strong>Copyright</strong> FIT2CLOUD 飞致云 © 2014-2020
<strong>Copyright</strong> FIT2CLOUD 飞致云 © 2014-2021
</div>
</div>
</template>

View File

@@ -5,6 +5,8 @@
:method="method"
:form="form"
:url="iUrl"
:has-save-continue="iHasSaveContinue"
:has-reset="iHasReset"
:is-submitting="isSubmitting"
v-bind="$attrs"
v-on="$listeners"
@@ -13,50 +15,66 @@
</template>
<script>
import AutoDataForm from '@/components/AutoDataForm'
import deepmerge from 'deepmerge'
export default {
name: 'GenericCreateUpdateForm',
components: {
AutoDataForm
},
props: {
// 创建对象的地址
url: {
type: String,
default: ''
},
// 更新的对象
object: {
type: Object,
default: null
},
// form的默认值
initial: {
type: Object,
default: () => ({})
},
// 提交前清理form的值
cleanFormValue: {
type: Function,
default: (value) => value
},
// 当提交的时候,怎么处理
onSubmit: {
type: Function,
default: null
},
// 如何提交数据
performSubmit: {
type: Function,
default(validValues) {
return this.$axios[this.method](this.iUrl, validValues)
}
},
// 创建成功的msg
createSuccessMsg: {
type: String,
default: function() {
return this.$t('common.createSuccessMsg')
}
},
// 更新成功的msg
saveSuccessContinueMsg: {
type: String,
default: function() {
return this.$t('common.saveSuccessContinueMsg')
}
},
updateSuccessMsg: {
type: String,
default: function() {
return this.$t('common.updateSuccessMsg')
}
},
// 创建成功的跳转路由
createSuccessNextRoute: {
type: Object,
default: function() {
@@ -64,6 +82,7 @@ export default {
return { name: routeName }
}
},
// 更新成功的跳转路由
updateSuccessNextRoute: {
type: Object,
default: function() {
@@ -71,12 +90,21 @@ export default {
return { name: routeName }
}
},
objectDetailRoute: {
type: Object,
default: function() {
const routeName = this.$route.name.replace('Update', 'Detail').replace('Create', 'Detail')
return { name: routeName }
}
},
// 获取下一个路由
getNextRoute: {
type: Function,
default(res, method) {
return method === 'post' ? this.createSuccessNextRoute : this.updateSuccessNextRoute
}
},
// 获取提交的方法
getMethod: {
type: Function,
default: function() {
@@ -88,6 +116,7 @@ export default {
}
}
},
// 获取创建和更新的url function
getUrl: {
type: Function,
default: function() {
@@ -101,12 +130,47 @@ export default {
},
onPerformSuccess: {
type: Function,
default(res, method, vm) {
const msg = method === 'post' ? this.createSuccessMsg : this.updateSuccessMsg
default(res, method, vm, addContinue) {
let msg = method === 'post' ? this.createSuccessMsg : this.updateSuccessMsg
if (addContinue) {
msg = this.saveSuccessContinueMsg
}
let msgLinkName = this.$t('common.Resource')
if (res.name) {
msgLinkName = res.name
} else if (res.hostname) {
msgLinkName = res.hostname
}
const detailRoute = this.objectDetailRoute
detailRoute['params'] = { 'id': res.id }
const route = this.getNextRoute(res, method)
this.$emit('submitSuccess', res)
this.$message.success(msg)
setTimeout(() => this.$router.push(route), 100)
const h = this.$createElement
this.$log.debug('router is: ', detailRoute)
if (this.hasDetailInMsg) {
this.$message({
message: h('p', null, [
h('el-link', {
on: {
click: () => this.$router.push(detailRoute)
},
style: { 'vertical-align': 'top' }
}, msgLinkName),
h('span', { style: {
'padding-left': '5px',
'height': '18px',
'line-height': '18px',
'font-size': '13.5px',
'font-weight': ' 400' }}, msg)
]),
type: 'success'
})
} else {
this.$message.success(msg)
}
if (!addContinue) {
setTimeout(() => this.$router.push(route), 100)
}
}
},
onPerformError: {
@@ -125,13 +189,22 @@ export default {
}
}
}
},
hasSaveContinue: {
type: Boolean,
default: null
},
hasDetailInMsg: {
type: Boolean,
default: true
}
},
data() {
return {
form: {},
loading: true,
isSubmitting: false
isSubmitting: false,
clone: false
}
},
computed: {
@@ -140,44 +213,77 @@ export default {
},
iUrl() {
return this.getUrl()
},
iHasSaveContinue() {
if (this.hasSaveContinue != null) {
return this.hasSaveContinue
}
return this.method === 'post'
},
iHasReset() {
if (this.hasReset != null) {
return this.hasReset
}
return this.method === 'put'
}
},
async created() {
this.$log.debug('Object init is: ', this.object)
this.loading = true
try {
const values = await this.getFormValue()
this.$log.debug('Final object is: ', values)
this.form = Object.assign(this.form, values)
} finally {
this.loading = false
}
},
methods: {
handleSubmit(values) {
handleSubmit(values, formName, addContinue) {
let handler = this.onSubmit || this.defaultOnSubmit
handler = handler.bind(this)
values = this.cleanFormValue(values)
return handler(values)
return handler(values, formName, addContinue)
},
defaultOnSubmit(validValues) {
defaultOnSubmit(validValues, formName, addContinue) {
this.isSubmitting = true
this.performSubmit(validValues)
.then((res) => this.onPerformSuccess.bind(this)(res, this.method, this))
.then((res) => this.onPerformSuccess.bind(this)(res, this.method, this, addContinue))
.catch((error) => this.onPerformError(error, this.method, this))
.finally(() => { this.isSubmitting = false })
},
async getFormValue() {
if (this.method !== 'put') {
const cloneFrom = this.$route.query['clone_from']
if (this.method !== 'put' && !cloneFrom) {
return Object.assign(this.form, this.initial)
}
let object = this.object
if (object === null) {
object = await this.getObjectDetail()
if (!object) {
if (cloneFrom) {
this.$log.debug('Clone from: ', cloneFrom)
const url = `${this.url}${cloneFrom}/`
object = await this.getObjectDetail(url)
if (object['name']) {
object.name = this.$t('common.cloneFrom') + ' ' + object.name
} else if (object['hostname']) {
object.hostname = this.$t('common.cloneFrom') + ' ' + object.hostname
}
} else {
object = await this.getObjectDetail(this.iUrl)
}
}
if (object) {
if (object['attrs']) {
object = deepmerge(object, object['attrs'])
}
this.$log.debug('Object is: ', object)
this.$emit('update:object', object)
}
return object
},
async getObjectDetail() {
return this.$axios.get(this.iUrl)
async getObjectDetail(url) {
this.$log.debug('Get object detail: ', url)
return this.$axios.get(url)
}
}
}

View File

@@ -1,5 +1,5 @@
<template>
<Page>
<Page v-bind="$attrs">
<IBox>
<GenericCreateUpdateForm ref="createUpdateForm" v-bind="$attrs" v-on="$listeners" />
</IBox>

View File

@@ -1,6 +1,6 @@
<template>
<Page v-bind="$attrs">
<ListTable v-bind="$attrs" />
<ListTable ref="ListTable" v-bind="$attrs" />
</Page>
</template>

View File

@@ -1,6 +1,5 @@
<template>
<Dialog
v-if="dialogSetting.dialogVisible"
:title="this.$t('common.updateSelected')"
:visible.sync="dialogSetting.dialogVisible"
width="70%"
@@ -99,6 +98,7 @@ export default {
const url = this.url
const msg = this.updateSuccessMsg
this.$axios.patch(url, validValues).then((res) => {
vm.$emit('update')
this.$message.success(msg)
vm.dialogSetting.dialogVisible = false
}).catch(error => {

View File

@@ -82,7 +82,7 @@ export default {
case 'userPage':
if (this.currentOrgUsePagePerm) {
this.$store.dispatch('users/setCurrentRole', rolec.USER)
console.log('Switch to: ', rolec.USER)
// console.log('Switch to: ', rolec.USER)
window.location.href = `/ui/`
}
break
@@ -97,7 +97,7 @@ export default {
},
logout() {
// Clean Status
const statusList = ['currentOrg', 'currentRole', 'jms_current_org', 'jms_current_role', 'sidebarStatus', 'django_language', 'X-JMS-ORG', 'activeTab']
const statusList = ['currentOrg', 'currentRole', 'jms_current_org', 'jms_current_role', 'sidebarStatus', 'X-JMS-ORG', 'activeTab']
for (const i in statusList) {
this.$cookie.delete(statusList[i])
}

View File

@@ -31,25 +31,46 @@ export default {
}
},
computed: {
supportedLangMapper() {
return this.supportLanguages.reduce((map, obj) => {
map[obj.code] = obj
return map
})
},
currentLang() {
const cookieCode = this.$cookie.get(this.LANG_COOKIE_NAME)
let lang = this.supportLanguages.find((v) => v.cookieCode === cookieCode)
const langCode = this.getLangCode()
let lang = this.supportedLangMapper[langCode]
if (!lang) {
lang = this.supportLanguages[0]
this.changeLangTo(lang)
}
if (lang.code !== this.$i18n.locale) {
this.changeLangTo(lang)
}
return lang
}
},
mounted() {
if (this.currentLang.code !== this.$i18n.locale) {
this.changeLangTo(this.currentLang)
}
},
methods: {
changeLangTo(item) {
this.$i18n.locale = item.code
localStorage.setItem('lang', item.code)
this.$cookie.set(this.LANG_COOKIE_NAME, item.cookieCode)
window.location.reload()
},
getLangCode() {
let langCode = localStorage.lang
if (!langCode) {
langCode = this.$cookie.get(this.LANG_COOKIE_NAME)
}
if (!langCode) {
langCode = navigator.language || navigator.userLanguage
}
langCode = langCode.substr(0, 2)
langCode = langCode.replace('zh', 'cn')
if (langCode) {
return langCode
}
}
}
}

View File

@@ -0,0 +1,51 @@
<template>
<div>
<!-- <el-link class="el-link" target="_blank" @click="goToTickets">{{ $t('route.Ticket') }}</el-link>-->
<el-badge :value="assignedTicketCount" :hidden="assignedTicketCount===0" size="mini" type="primary">
<el-link class="el-link" target="_blank" @click="goToTickets">{{ $t('route.Ticket') }}</el-link>
</el-badge>
</div>
</template>
<script>
import { getTicketOpenCount } from '@/api/ticket'
import { mapGetters } from 'vuex'
export default {
name: 'WebTerminal',
data() {
return {
assignedTicketCount: 0
}
},
computed: {
...mapGetters([
'currentUser'
])
},
created() {
this.ticketsOpenedCount()
},
methods: {
ticketsOpenedCount() {
getTicketOpenCount(this.currentUser.id).then(data => {
this.assignedTicketCount = data.count
})
},
goToTickets() {
this.$router.push({ name: 'TicketList' })
}
}
}
</script>
<style scoped>
.el-link {
color: #606266 !important;
font-size: 13px;
font-weight: 400
}
.el-badge ::v-deep .el-badge__content.is-fixed{
top:10px;
}
</style>

View File

@@ -0,0 +1,24 @@
<template>
<el-link class="el-link" :href="webTerminalUrl" target="_blank">{{ $t('route.WebTerminal') }}</el-link>
</template>
<script>
import { BASE_URL } from '@/utils/common'
export default {
name: 'WebTerminal',
computed: {
webTerminalUrl() {
return `${BASE_URL}/luna/?_=${Date.now()}`
}
}
}
</script>
<style scoped>
.el-link {
color: #606266 !important;
font-size: 13px;
font-weight: 400
}
</style>

View File

@@ -10,6 +10,19 @@
<div class="header-item">
<Language />
</div>
<div
v-if="
publicSettings.TICKETS_ENABLED
&& publicSettings.XPACK_LICENSE_IS_VALID
&& !isOrgAuditor
"
class="header-item"
>
<Tickets />
</div>
<div class="header-item">
<WebTerminal />
</div>
<div class="header-item header-profile">
<AccountDropdown />
</div>
@@ -23,6 +36,9 @@ import Hamburger from '@/components/Hamburger'
import AccountDropdown from './AccountDropdown'
import Help from './Help'
import Language from './Language'
import WebTerminal from './WebTerminal'
import Tickets from './Tickets'
import rolc from '@/utils/role'
export default {
components: {
@@ -30,7 +46,9 @@ export default {
Hamburger,
AccountDropdown,
Language,
Help
Help,
Tickets,
WebTerminal
},
data() {
return {
@@ -38,8 +56,11 @@ export default {
},
computed: {
...mapGetters([
'sidebar'
])
'sidebar', 'publicSettings', 'currentOrgRoles'
]),
isOrgAuditor() {
return rolc.getRolesDisplay(this.currentOrgRoles).includes('OrgAuditor') || rolc.getRolesDisplay(this.currentOrgRoles).includes('Auditor')
}
},
methods: {
toggleSideBar() {

View File

@@ -0,0 +1,64 @@
<template>
<div v-if="!loading">
<el-alert v-if="isExpire" type="error">
{{ isExpire }}
</el-alert>
</div>
</template>
<script>
import { toSafeLocalDateStr } from '@/utils/common'
import { mapGetters } from 'vuex'
export default {
name: 'LicenseExpireTip',
data() {
return {
loading: true,
licenseData: {}
}
},
computed: {
...mapGetters([
'publicSettings',
'currentUser'
]),
isExpire() {
if (!this.publicSettings.XPACK_ENABLED || this.currentUser.role !== 'Admin') {
return false
}
const intervalDays = this.getIntervalDays(this.licenseData.date_expired)
if (intervalDays < 0) {
return this.$t('setting.LicenseExpired')
}
if (intervalDays < 7) {
return this.$t('setting.LicenseWillBe') + this.licenseData.date_expired + this.$t('setting.Expire')
}
return false
}
},
mounted() {
if (this.publicSettings.XPACK_ENABLED && this.currentUser.role === 'Admin') {
this.$axios.get('/api/v1/xpack/license/detail').then(res => {
this.licenseData = res
}).finally(() => {
this.loading = false
})
} else {
this.loading = false
}
},
methods: {
getIntervalDays(date) {
const dateExpired = new Date(toSafeLocalDateStr(date))
const dateNow = new Date()
const intervalTime = dateExpired.getTime() - dateNow.getTime()
return Math.floor(intervalTime / (24 * 3600 * 1000))
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,22 +1,34 @@
<template>
<div class="page-heading">
<el-row :gutter="0">
<el-col :span="16" class="page-heading-left">
<slot><h2>{{ title }}</h2></slot>
</el-col>
<el-col :span="8">
<div class="page-heading-right">
<slot name="rightSide" />
</div>
</el-col>
</el-row>
<div>
<slot name="globalNotification">
<LicenseExpireTip />
<PasswordExpireTip />
</slot>
<div class="page-heading">
<el-row :gutter="0">
<el-col :span="16" class="page-heading-left">
<slot><h2>{{ title }}</h2></slot>
</el-col>
<el-col :span="8">
<div class="page-heading-right">
<slot name="rightSide" />
</div>
</el-col>
</el-row>
</div>
</div>
<!-- <Breadcrumb />-->
</template>
<script>
import LicenseExpireTip from '@/layout/components/Page/LicenseExpireTip'
import PasswordExpireTip from '@/layout/components/Page/PasswordExpireTip'
export default {
name: 'PageHeading',
components: {
LicenseExpireTip,
PasswordExpireTip
},
props: {
title: {
type: String,

View File

@@ -0,0 +1,55 @@
<template>
<div>
<el-alert v-if="isExpire" type="error">
{{ isExpire }}
</el-alert>
</div>
</template>
<script>
import { toSafeLocalDateStr } from '@/utils/common'
import { mapGetters } from 'vuex'
export default {
name: 'PasswordExpireTip',
data() {
return {
loading: true,
securityData: {}
}
},
computed: {
...mapGetters([
'publicSettings',
'currentUser'
]),
isExpire() {
// 用户来源不是Local时不显示密码过期提示
if (this.currentUser.source !== 'local') {
return false
}
const intervalTime = this.getIntervalDays(this.currentUser.date_password_last_updated)
const securityPasswordExpirationTime = this.publicSettings.SECURITY_PASSWORD_EXPIRATION_TIME
if (intervalTime >= securityPasswordExpirationTime) {
return this.$t('users.passwordExpired')
}
if (securityPasswordExpirationTime - intervalTime <= 5) {
return this.$t('users.passwordWillExpiredPrefixMsg') + (securityPasswordExpirationTime - intervalTime) + this.$t('users.passwordWillExpiredSuffixMsg')
}
return false
}
},
methods: {
getIntervalDays(date) {
const dateExpired = new Date(toSafeLocalDateStr(date))
const dateNow = new Date()
const intervalTime = dateNow.getTime() - dateExpired.getTime()
return Math.floor(intervalTime / (24 * 3600 * 1000))
}
}
}
</script>
<style scoped>
</style>

View File

@@ -9,7 +9,14 @@
<div>
<el-tabs v-if="submenu.length > 0" slot="submenu" v-model="iActiveMenu" class="page-submenu" @tab-click="handleTabClick">
<el-tab-pane v-for="item in submenu" :key="item.name" :label="item.title" :label-content="item.labelContent" :name="item.name" />
<template v-for="item in submenu">
<el-tab-pane :key="item.name" :label-content="item.labelContent" :name="item.name">
<span slot="label">
{{ item.title }}
<slot name="badge" :tab="item.name" />
</span>
</el-tab-pane>
</template>
</el-tabs>
<transition name="fade-transform" mode="out-in">
<slot />

View File

@@ -77,7 +77,7 @@ export default {
position: relative;
overflow: hidden;
width: 100%;
/deep/ {
::v-deep {
.el-scrollbar__bar {
bottom: 0px;
}

View File

@@ -11,7 +11,7 @@ export default [
{
path: '',
name: 'RemoteAppList',
meta: { title: i18n.t('route.RemoteApp') },
meta: { title: i18n.t('route.RemoteApp'), activeMenu: '/applications/remote-apps' },
component: () => import('@/views/applications/RemoteApp/RemoteAppList')
},
{
@@ -63,5 +63,32 @@ export default [
component: () => import('@/views/applications/DatabaseApp/DatabaseAppDetail/index'),
meta: { title: i18n.t('route.DatabaseAppDetail'), activeMenu: '/applications/database-apps' },
hidden: true
},
{
path: 'kubernetes-apps',
name: 'KubernetesAppList',
component: () => import('@/views/applications/KubernetesApp/KubernetesAppList'),
meta: { title: i18n.t('route.KubernetesApp') }
},
{
path: 'kubernetes-apps/create',
name: 'KubernetesAppCreate',
component: () => import('@/views/applications/KubernetesApp/KubernetesAppCreateUpdate'),
meta: { title: i18n.t('route.KubernetesAppCreate'), activeMenu: '/applications/kubernetes-apps', action: 'create' },
hidden: true
},
{
path: 'kubernetes-apps/:id/update',
name: 'KubernetesAppUpdate',
component: () => import('@/views/applications/KubernetesApp/KubernetesAppCreateUpdate'),
meta: { title: i18n.t('route.KubernetesAppUpdate'), activeMenu: '/applications/kubernetes-apps', action: 'update' },
hidden: true
},
{
path: 'kubernetes-apps/:id',
name: 'KubernetesAppDetail',
component: () => import('@/views/applications/KubernetesApp/KubernetesAppDetail/index'),
meta: { title: i18n.t('route.KubernetesAppDetail'), activeMenu: '/applications/kubernetes-apps' },
hidden: true
}
]

View File

@@ -12,7 +12,7 @@ export default [
path: '',
name: 'AssetList',
component: () => import('@/views/assets/Asset/AssetList.vue'),
meta: { title: i18n.t('route.AssetList') }
meta: { title: i18n.t('route.AssetList'), activeMenu: '/assets/assets' }
},
{
path: 'create',
@@ -34,6 +34,13 @@ export default [
component: () => import('@/views/assets/Asset/AssetCreateUpdate.vue'),
meta: { title: i18n.t('route.AssetUpdate'), activeMenu: '/assets/assets' },
hidden: true
},
{
path: 'detail/:id/update',
name: 'AssetMoreInformationEdit',
component: () => import('@/views/assets/Asset/AssetMoreInformationEdit.vue'),
meta: { title: i18n.t('common.UpdateAssetDetail'), activeMenu: '/assets/assets' },
hidden: true
}
]
},
@@ -47,7 +54,7 @@ export default [
path: '',
name: 'DomainList',
component: () => import('@/views/assets/Domain/DomainList.vue'),
meta: { title: i18n.t('route.DomainList') }
meta: { title: i18n.t('route.DomainList'), activeMenu: '/assets/domains' }
},
{
path: 'create',
@@ -104,7 +111,7 @@ export default [
path: '',
name: 'AdminUserList',
component: () => import('@/views/assets/AdminUser/AdminUserList'),
meta: { title: i18n.t('route.AdminUserList') }
meta: { title: i18n.t('route.AdminUserList'), activeMenu: '/assets/admin-users' }
},
{
path: 'create',
@@ -174,7 +181,7 @@ export default [
path: '',
name: 'CommandFilterList',
component: () => import('@/views/assets/CommandFilter/CommandFilterList.vue'),
meta: { title: i18n.t('route.CommandFilterList') }
meta: { title: i18n.t('route.CommandFilterList'), activeMenu: '/assets/cmd-filters' }
},
{
path: ':id/update',
@@ -265,7 +272,7 @@ export default [
path: '',
name: 'LabelList',
component: () => import('@/views/assets/Label/LabelList.vue'),
meta: { title: i18n.t('route.LabelList') }
meta: { title: i18n.t('route.LabelList'), activeMenu: '/assets/labels' }
},
{
path: 'create',

View File

@@ -45,5 +45,12 @@ export default [
name: 'CeleryTaskLog',
hidden: true,
meta: { title: i18n.t('route.CeleryTaskLog'), roles: ['SuperAdmin', 'Admin', 'Auditor', 'User'] }
},
{
path: '/ops/task/task/:id/log/',
component: () => import('@/views/ops/CeleryTaskLog'),
name: 'TaskLog',
hidden: true,
meta: { title: i18n.t('route.CeleryTaskLog'), roles: ['SuperAdmin', 'Admin', 'Auditor', 'User'] }
}
]

View File

@@ -140,8 +140,12 @@ export const allRoleRoutes = [
component: Layout,
redirect: '/tickets/tickets/',
children: TicketsRoutes,
hidden: true,
meta: {
licenseRequired: true
// hidden: ({ settings }) => {
// return !settings.TICKETS_ENABLED
// }
}
},
{

View File

@@ -1,5 +1,7 @@
import i18n from '@/i18n/i18n'
import rolec from '@/utils/role'
import { BASE_URL } from '@/utils/common'
export default [
{
path: 'tasks',
@@ -32,7 +34,7 @@ export default [
path: 'command-executions/create',
name: 'BatchCommand',
component: () => import('@/views/ops/CommandExecution'),
meta: { title: i18n.t('route.BatchCommand'), commandExecutionRequired: true }
meta: { title: i18n.t('route.BatchCommand'), hidden: ({ settings }) => !settings.SECURITY_COMMAND_EXECUTION }
},
// {
// path: 'celery/task/:id',
@@ -42,9 +44,9 @@ export default [
// meta: { title: i18n.t('route.CeleryTaskLog') }
// },
{
path: 'task/monitor',
path: `${BASE_URL}/core/flower?_=${Date.now()}`,
name: 'TaskMonitor',
component: () => window.open(`/core/flower?_=${Date.now()}`),
// component: () => window.open(`/core/flower?_=${Date.now()}`),
meta: { title: i18n.t('route.TaskMonitor'), permissions: [rolec.PERM_SUPER] }
}
]

View File

@@ -31,77 +31,45 @@ const assetPermissionRoutes = [
}
]
const remoteAppPermissionRoutes = [
const ApplicationPermissionRoutes = [
{
path: 'remote-app-permissions',
path: 'app-permissions',
component: empty,
meta: { title: i18n.t('route.RemoteAppPermission'), licenseRequired: true },
meta: { title: i18n.t('route.ApplicationPermission') },
redirect: '',
children: [
{
path: '',
name: 'RemoteAppPermissionList',
component: () => import('@/views/perms/RemoteAppPermission/RemoteAppPermissionList'),
meta: { title: i18n.t('route.RemoteAppPermission') }
name: 'ApplicationPermissionList',
component: () => import('@/views/perms/ApplicationPermission/ApplicationPermissionList'),
meta: { title: i18n.t('route.ApplicationPermission'), activeMenu: '/perms/app-permissions' }
},
{
path: 'create',
component: () => import('@/views/perms/RemoteAppPermission/RemoteAppPermissionCreateUpdate'),
name: 'RemoteAppPermissionCreate',
component: () => import('@/views/perms/ApplicationPermission/ApplicationPermissionCreateUpdate'),
name: 'ApplicationPermissionCreate',
hidden: true,
meta: { title: i18n.t('route.RemoteAppPermissionCreate'), activeMenu: '/perms/remote-app-permissions', action: 'create' }
},
{
path: 'update',
component: () => import('@/views/perms/RemoteAppPermission/RemoteAppPermissionCreateUpdate'),
name: 'RemoteAppPermissionUpdate',
hidden: true,
meta: { title: i18n.t('route.RemoteAppPermissionUpdate'), activeMenu: '/perms/remote-app-permissions', action: 'update' }
meta: { title: i18n.t('route.ApplicationPermissionCreate'), activeMenu: '/perms/app-permissions', action: 'create' }
},
{
path: ':id',
component: () => import('@/views/perms/RemoteAppPermission/RemoteAppPermissionDetail/index'),
name: 'RemoteAppPermissionDetail',
component: () => import('@/views/perms/ApplicationPermission/ApplicationPermissionDetail/index'),
name: 'ApplicationPermissionDetail',
hidden: true,
meta: { title: i18n.t('route.RemoteAppPermissionDetail'), activeMenu: '/perms/remote-app-permissions' }
meta: { title: i18n.t('route.ApplicationPermissionDetail'), activeMenu: '/perms/app-permissions' }
},
{
path: ':id/update',
component: () => import('@/views/perms/ApplicationPermission/ApplicationPermissionCreateUpdate'),
name: 'ApplicationPermissionUpdate',
hidden: true,
meta: { title: i18n.t('route.ApplicationPermissionUpdate'), activeMenu: '/perms/app-permissions', action: 'update' }
}]
}
]
const databasePermissionRoutes = [
{
path: 'database-app-permissions',
name: 'DatabaseAppPermissionList',
component: () => import('@/views/perms/DatabaseAppPermission/DatabaseAppPermissionList'),
meta: { title: i18n.t('route.DatabaseAppPermission') }
},
{
path: 'database-app-permissions/create',
component: () => import('@/views/perms/DatabaseAppPermission/DatabaseAppPermissionCreateUpdate'), // Parent router-view
name: 'DatabaseAppPermissionCreate',
hidden: true,
meta: { title: i18n.t('route.DatabaseAppPermissionCreate'), activeMenu: '/perms/database-app-permissions' }
},
{
path: 'database-app-permissions/update',
component: () => import('@/views/perms/DatabaseAppPermission/DatabaseAppPermissionCreateUpdate'), // Parent router-view
name: 'DatabaseAppPermissionUpdate',
hidden: true,
meta: { title: i18n.t('route.DatabaseAppPermissionUpdate'), activeMenu: '/perms/database-app-permissions', action: 'update' }
},
{
path: 'database-app-permissions/:id',
component: () => import('@/views/perms/DatabaseAppPermission/DatabaseAppPermissionDetail/index'),
name: 'DatabaseAppPermissionDetail',
hidden: true,
meta: { title: i18n.t('route.DatabaseAppPermissionDetail'), activeMenu: '/perms/database-app-permissions' }
}
]
export default [
... assetPermissionRoutes,
... remoteAppPermissionRoutes,
... databasePermissionRoutes
... ApplicationPermissionRoutes
]

View File

@@ -1,6 +1,8 @@
import i18n from '@/i18n/i18n'
import rolec from '@/utils/role'
import empty from '@/layout/empty'
import { BASE_URL } from '@/utils/common'
export default [
{
path: 'session',
@@ -22,15 +24,16 @@ export default [
hidden: true
},
{
path: 'luna',
path: `${BASE_URL}/luna/?_=${Date.now()}`,
name: 'WebTerminal',
component: () => window.open(`/luna/?_=${Date.now()}`),
// component: () => window.open(`/luna/?_=${Date.now()}`),
meta: { title: i18n.t('route.WebTerminal') }
// hidden: true
},
{
path: 'sftp',
path: `${BASE_URL}/koko/elfinder/sftp/?`,
name: 'FileManager',
component: () => window.open(`/koko/elfinder/sftp/?`),
// component: () => window.open(`/koko/elfinder/sftp/?`),
meta: { title: i18n.t('route.FileManager') }
},
{

View File

@@ -4,7 +4,8 @@ export default [
path: 'tickets',
name: 'TicketList',
component: () => import('@/views/tickets/TicketList'),
meta: { title: i18n.t('route.Tickets'), icon: 'check-square-o' }
meta: { title: i18n.t('route.Tickets'), icon: 'check-square-o', activeMenu: '/tickets/tickets' },
hidden: true
},
{
path: 'tickets/:id',
@@ -12,5 +13,33 @@ export default [
component: () => import('@/views/tickets/TicketDetail/index'),
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets' },
hidden: true
},
{
path: 'tickets/request-asset-perm/create',
name: 'RequestAssetPermTicketCreateUpdate',
component: () => import('@/views/tickets/RequestAssetPerm/RequestAssetPermTicketCreateUpdate'),
meta: { title: i18n.t('route.TicketCreate'), activeMenu: '/tickets/tickets' },
hidden: true
},
{
path: 'tickets/request-asset-perm/:id',
name: 'AssetsTicketDetail',
component: () => import('@/views/tickets/RequestAssetPerm/Detail/index'),
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets' },
hidden: true
},
{
path: 'tickets/request-application-perm/create',
name: 'RequestApplicationPermTicketCreateUpdate',
component: () => import('@/views/tickets/RequestApplicationPerm/RequestApplicationPermTicketCreateUpdate'),
meta: { title: i18n.t('route.TicketCreate'), activeMenu: '/tickets/tickets' },
hidden: true
},
{
path: 'tickets/request-application-perm/:id',
name: 'AppsTicketDetail',
component: () => import('@/views/tickets/RequestApplicationPerm/Detail/index'),
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets' },
hidden: true
}
]

View File

@@ -1,10 +1,7 @@
import Layout from '@/layout/index'
import i18n from '@/i18n/i18n'
import rolec from '@/utils/role'
const scheme = document.location.protocol
const port = document.location.port ? ':' + document.location.port : ''
const URL = scheme + '//' + document.location.hostname + port
import { BASE_URL } from '@/utils/common'
export default [
// 404 page must be placed at the end !!!
@@ -47,6 +44,12 @@ export default [
name: 'MyDatebases',
component: () => import('@/userviews/apps/DatabaseApp'),
meta: { title: i18n.t('route.DatabaseApp'), permissions: [rolec.PERM_USE] }
},
{
path: '/apps/kubernetes',
name: 'MyKubernetes',
component: () => import('@/userviews/apps/KubernetesApp'),
meta: { title: i18n.t('route.KubernetesApp'), permissions: [rolec.PERM_USE] }
}
]
},
@@ -55,7 +58,7 @@ export default [
component: Layout,
meta: {
permissions: [rolec.PERM_USE],
commandExecutionRequired: true
hidden: ({ settings }) => !settings.SECURITY_COMMAND_EXECUTION
},
children: [
{
@@ -66,6 +69,63 @@ export default [
}
]
},
{
path: '/tickets',
component: Layout,
hidden: true,
meta: {
title: i18n.t('route.Tickets'),
icon: 'history',
permissions: [rolec.PERM_USE],
licenseRequired: true,
hidden: ({ settings }) => {
return !settings.TICKETS_ENABLED
}
},
children: [
{
path: '',
name: 'TicketList',
component: () => import('@/views/tickets/TicketList'),
meta: { title: i18n.t('route.Tickets'), icon: 'check-square-o', activeMenu: '/tickets', permissions: [rolec.PERM_USE] }
},
{
path: 'tickets/request-asset-perm/create',
name: 'RequestAssetPermTicketCreateUpdate',
component: () => import('@/views/tickets/RequestAssetPerm/RequestAssetPermTicketCreateUpdate'),
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets', permissions: [rolec.PERM_USE] },
hidden: true
},
{
path: 'tickets/request-asset-perm/:id',
name: 'AssetsTicketDetail',
component: () => import('@/views/tickets/RequestAssetPerm/Detail/index'),
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets', permissions: [rolec.PERM_USE] },
hidden: true
},
{
path: 'tickets/request-application-perm/create',
name: 'RequestApplicationPermTicketCreateUpdate',
component: () => import('@/views/tickets/RequestApplicationPerm/RequestApplicationPermTicketCreateUpdate'),
meta: { title: i18n.t('route.TicketCreate'), activeMenu: '/tickets/tickets', permissions: [rolec.PERM_USE] },
hidden: true
},
{
path: 'tickets/request-application-perm/:id',
name: 'AppsTicketDetail',
component: () => import('@/views/tickets/RequestApplicationPerm/Detail/index'),
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets', permissions: [rolec.PERM_USE] },
hidden: true
},
{
path: 'tickets/:id',
name: 'TicketDetail',
component: () => import('@/views/tickets/TicketDetail/index'),
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets', permissions: [rolec.PERM_USE] },
hidden: true
}
]
},
{
path: `external-luna`,
component: Layout,
@@ -74,7 +134,7 @@ export default [
},
children: [
{
path: `${URL}/luna/`,
path: `${BASE_URL}/luna/`,
meta: { title: i18n.t('route.WebTerminal'), icon: 'window-maximize', activeMenu: '/assets', permissions: [rolec.PERM_USE] }
}
]
@@ -87,7 +147,7 @@ export default [
},
children: [
{
path: `${URL}/koko/elfinder/sftp/`,
path: `${BASE_URL}/koko/elfinder/sftp/`,
meta: { title: i18n.t('route.WebFTP'), icon: 'file', activeMenu: '/assets', permissions: [rolec.PERM_USE] }
}
]

View File

@@ -13,6 +13,7 @@ const getters = {
currentOrgRoles: state => state.users.roles,
currentOrgPerms: state => state.users.perms,
MFAVerifyAt: state => state.users.MFAVerifyAt,
MFA_TTl: state => state.settings.publicSettings.SECURITY_MFA_VERIFY_TTL
MFA_TTl: state => state.settings.publicSettings.SECURITY_MFA_VERIFY_TTL,
tableConfig: state => state.table.tableConfig
}
export default getters

View File

@@ -17,39 +17,26 @@ function hasPermission(roles, route) {
// console.log('Has route permission: ', route.path, requirePermsSum, userRolesSum, ' => ', has, roles)
return has
}
function hasLicense(licState, route) {
if (licState) {
return licState
}
let requireLic = route.meta ? route.meta.licenseRequired : null
if (!requireLic) {
requireLic = false
}
return licState === requireLic
}
function hasCommand(cmdBulkExecutionEnable, route) {
const routeRequireCmd = route.meta ? route.meta.commandExecutionRequired : false
if (!routeRequireCmd) {
return true
}
if (!cmdBulkExecutionEnable) {
function hasLicense(route, rootState) {
const licenseIsValid = rootState.settings.publicSettings.XPACK_LICENSE_IS_VALID
const licenseRequired = route.meta ? route.meta.licenseRequired : false
if (!licenseIsValid && licenseRequired) {
return false
}
return true
}
export function filterLicRoutes(routes, roles) {
export function filterLicenseRequiredRoutes(routes, rootState) {
const res = []
routes.forEach(route => {
const tmp = {
...route
}
if (hasLicense(roles, tmp)) {
if (hasLicense(route, rootState)) {
if (tmp.children) {
tmp.children = filterLicRoutes(tmp.children, roles)
tmp.children = filterLicenseRequiredRoutes(tmp.children, rootState)
}
res.push(tmp)
}
@@ -58,16 +45,24 @@ export function filterLicRoutes(routes, roles) {
return res
}
export function filterCmdRoutes(routes, roles) {
function isNeedHidden(route, rootState) {
let hidden = route.meta ? route.meta.hidden : false
if (typeof hidden === 'function') {
hidden = hidden({ route: route, settings: rootState.settings.publicSettings })
}
return hidden
}
export function filterHiddenRoutes(routes, rootState) {
const res = []
routes.forEach(route => {
const tmp = {
...route
}
if (hasCommand(roles, tmp)) {
if (!isNeedHidden(route, rootState)) {
if (tmp.children) {
tmp.children = filterCmdRoutes(tmp.children, roles)
tmp.children = filterHiddenRoutes(tmp.children, rootState)
}
res.push(tmp)
}
@@ -84,18 +79,18 @@ export function filterCmdRoutes(routes, roles) {
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
for (const route of routes) {
const tmp = {
...route
}
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
}
return res
}
@@ -115,10 +110,10 @@ const actions = {
generateRoutes({ commit, rootState }, roles) {
return new Promise(resolve => {
let accessedRoutes = filterAsyncRoutes(allRoleRoutes, roles)
accessedRoutes = filterCmdRoutes(accessedRoutes, rootState.settings.publicSettings.SECURITY_COMMAND_EXECUTION)
accessedRoutes = filterLicRoutes(accessedRoutes, rootState.settings.publicSettings.XPACK_LICENSE_IS_VALID)
accessedRoutes = filterHiddenRoutes(accessedRoutes, rootState)
accessedRoutes = filterLicenseRequiredRoutes(accessedRoutes, rootState)
if (accessedRoutes.length === 0) {
console.log('No route find')
// console.log('No route find')
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)

View File

@@ -35,6 +35,10 @@ const actions = {
link.rel = 'shortcut icon'
link.href = response.data.LOGO_URLS.favicon
document.getElementsByTagName('head')[0].appendChild(link)
// 动态修改Title
if (response.data.LOGIN_TITLE) { document.title = response.data.LOGIN_TITLE }
commit('SET_PUBLIC_SETTINGS', response.data)
resolve(response)
}).catch(error => {

View File

@@ -0,0 +1,28 @@
import VueCookie from 'vue-cookie'
import Vue from 'vue'
function getTableConfigfromCookie() {
return VueCookie.get('tableConfig') ? JSON.parse(VueCookie.get('tableConfig')) : {}
}
const state = {
tableConfig: getTableConfigfromCookie()
}
const mutations = {
SET_TABLE_CONFIG: (state, tableConfig) => {
Vue.set(state.tableConfig, tableConfig.key, tableConfig.value)
VueCookie.set('tableConfig', JSON.stringify(state.tableConfig), 14)
}
}
const actions = {
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@@ -37,6 +37,15 @@ const mutations = {
SET_ORGS: (state, orgs) => {
state.orgs = orgs
},
MODIFY_ORG: (state, org) => {
state.orgs = state.orgs.map(oldOrg => {
if (oldOrg.id === org.id) {
oldOrg.name = org.name
}
return oldOrg
}
)
},
ADD_ORG: (state, org) => {
state.orgs.push(org)
},
@@ -89,7 +98,7 @@ const actions = {
commit('SET_PROFILE', response)
resolve(response)
}).catch(error => {
console.log(error)
// console.log(error)
reject(error)
})
})
@@ -125,6 +134,9 @@ const actions = {
addAdminOrg({ commit, state }, org) {
commit('ADD_ORG', org)
},
modifyOrg({ commit, state }, org) {
commit('MODIFY_ORG', org)
},
// user logout
logout({ commit, state }) {
return new Promise((resolve, reject) => {

View File

@@ -96,7 +96,7 @@ td .el-button.el-button--mini {
font-weight: 600;
}
.el-button.el-button--default:focus:not(.is-disabled), .el-button.el-button--default:hover:not(.is-disabled) {
.el-button.el-button--default:hover:not(.is-disabled) {
color: #606266;
border-color: #d2d2d2;
background-color: #e6e6e6;
@@ -214,6 +214,10 @@ td .el-button.el-button--mini {
color: white;
}
.el-select-dropdown__item.is-disabled:hover{
color:#c0c4cc;
}
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after {
color: $--color-primary;
}
@@ -431,7 +435,7 @@ a {
.el-button--danger.is-plain {
color: $--color-danger;
background: white;
background: #ffffff;
border-color: $--color-danger;
}

View File

@@ -148,3 +148,12 @@ input[type=file] {
font-size: 12px;
opacity: 80;
}
.el-table__body {
width: 100%;
table-layout: fixed !important;
}
.el-table__column-filter-trigger i {
color: #888888 !important;
}

View File

@@ -84,7 +84,7 @@ li.is-active {
// line-height: 30px !important;
//}
////重置字体大小 菜单宽度
//.el-submenu /deep/ .el-submenu__title, .submenu-title-noDropdown{
//.el-submenu ::v-deep .el-submenu__title, .submenu-title-noDropdown{
// height: 46px !important;
// line-height: 46px !important;
//}

View File

@@ -21,7 +21,7 @@ export default {
data() {
return {
tableConfig: {
url: `/api/v1/perms/users/database-apps/`,
url: `/api/v1/perms/users/applications/?category=db`,
columns: [
{
prop: 'name',
@@ -30,18 +30,19 @@ export default {
sortable: true
},
{
prop: 'get_type_display',
prop: 'type_display',
align: 'center',
label: this.$t('assets.Type')
},
{
prop: 'database',
prop: 'attrs.database',
align: 'center',
label: this.$t('assets.Database')
},
{
prop: 'comment',
align: 'center',
showOverflowTooltip: true,
label: this.$t('assets.Comment')
},
{
@@ -52,6 +53,7 @@ export default {
formatterArgs: {
hasDelete: false,
hasUpdate: false,
hasClone: false,
extraActions: [
{
name: 'connect',
@@ -69,13 +71,13 @@ export default {
headerActions: {
hasExport: false,
hasImport: false,
hasRefresh: false,
hasRefresh: true,
hasCreate: false,
hasBulkDelete: false,
hasBulkUpdate: false,
hasLeftActions: false,
hasSearch: true,
hasRightActions: false
hasRightActions: true
}
}
},

View File

@@ -0,0 +1,96 @@
<template>
<Page>
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</Page>
</template>
<script>
import ListTable from '@/components/ListTable/index'
import Page from '@/layout/components/Page/index'
import { ActionsFormatter } from '@/components/ListTable/formatters'
export default {
name: 'KubernetesApp',
components: {
ListTable,
Page
},
props: {
},
data() {
return {
tableConfig: {
url: `/api/v1/perms/users/applications/?category=cloud`,
columns: [
{
prop: 'name',
align: 'center',
label: this.$t('assets.Name'),
sortable: true
},
{
prop: 'type_display',
align: 'center',
label: this.$t('assets.Type')
},
{
prop: 'attrs.cluster',
align: 'center',
label: this.$t('applications.cluster')
},
{
prop: 'comment',
align: 'center',
showOverflowTooltip: true,
label: this.$t('assets.Comment')
},
{
prop: 'id',
align: 'center',
label: this.$t('assets.Action'),
formatter: ActionsFormatter,
formatterArgs: {
hasDelete: false,
hasUpdate: false,
hasClone: false,
extraActions: [
{
name: 'connect',
fa: 'fa-terminal',
type: 'primary',
callback: function({ row, col, cellValue, reload }) {
window.open(`/luna/?type=k8s_app&login_to=${cellValue}`, '_blank')
}
}
]
}
}
]
},
headerActions: {
hasExport: false,
hasImport: false,
hasRefresh: true,
hasCreate: false,
hasBulkDelete: false,
hasBulkUpdate: false,
hasLeftActions: false,
hasSearch: true,
hasRightActions: true
}
}
},
computed: {
},
mounted() {
},
methods: {
}
}
</script>
<style lang='less' scoped>
</style>

View File

@@ -20,7 +20,7 @@ export default {
data() {
return {
tableConfig: {
url: `/api/v1/perms/users/remote-apps/`,
url: `/api/v1/perms/users/applications/?category=remote_app`,
columns: [
{
prop: 'name',
@@ -29,18 +29,19 @@ export default {
sortable: true
},
{
prop: 'get_type_display',
prop: 'type_display',
align: 'center',
label: this.$t('assets.RemoteType')
label: this.$t('assets.Type')
},
{
prop: 'asset_info.hostname',
prop: 'attrs.asset_info.hostname',
align: 'center',
label: this.$t('assets.Asset')
},
{
prop: 'comment',
align: 'center',
showOverflowTooltip: true,
label: this.$t('assets.Comment')
},
{
@@ -51,6 +52,7 @@ export default {
formatterArgs: {
hasDelete: false,
hasUpdate: false,
hasClone: false,
extraActions: [
{
name: 'connect',
@@ -68,13 +70,13 @@ export default {
headerActions: {
hasExport: false,
hasImport: false,
hasRefresh: false,
hasRefresh: true,
hasCreate: false,
hasBulkDelete: false,
hasBulkUpdate: false,
hasLeftActions: false,
hasSearch: true,
hasRightActions: false
hasRightActions: true
}
}
},

View File

@@ -59,6 +59,10 @@ export default {
key: this.$t('assets.Platform'),
value: row.platform
},
{
key: this.$t('common.Activate'),
value: row.is_active
},
{
key: this.$t('assets.Comment'),
value: row.comment
@@ -86,6 +90,17 @@ export default {
}
}
},
{
prop: 'platform',
label: this.$t('assets.Platform'),
width: '120px'
},
{
prop: 'comment',
label: this.$t('assets.Comment'),
showOverflowTooltip: true,
width: '180px'
},
{
prop: 'id',
align: 'center',
@@ -95,12 +110,16 @@ export default {
formatterArgs: {
hasDelete: false,
loading: true,
hasClone: false,
hasUpdate: false,
extraActions: [
{
name: 'connect',
fa: 'fa-terminal',
type: 'primary',
can: (row, cellValue) => {
return row.is_active
},
callback: function({ row, col, cellValue, reload }) {
window.open(`/luna/?login_to=${cellValue}`, '_blank')
}
@@ -121,7 +140,12 @@ export default {
]
}
}
]
],
tableAttrs: {
rowClassName({ row }) {
return !row.is_active ? 'row_disabled' : ''
}
}
},
headerActions: {
hasExport: false,
@@ -136,10 +160,10 @@ export default {
},
methods: {
refreshAllFavorites() {
this.tableConfig.columns[3].formatterArgs.loading = true
this.tableConfig.columns[this.tableConfig.columns.length - 1].formatterArgs.loading = true
this.$axios.get('/api/v1/assets/favorite-assets/').then(resp => {
this.allFavorites = resp
this.tableConfig.columns[3].formatterArgs.loading = false
this.tableConfig.columns[this.tableConfig.columns.length - 1].formatterArgs.loading = false
})
},
addOrDeleteFavorite(assetId) {
@@ -175,4 +199,8 @@ export default {
.el-card {
border: 0 !important;
}
.row_disabled,.row_disabled:hover,.row_disabled:hover > td{
cursor: not-allowed;
background-color:rgba(192,196,204,0.28) !important;
}
</style>

View File

@@ -8,6 +8,8 @@
:update-success-next-route="updateSuccessNextRoute"
:clean-form-value="cleanFormValue"
:get-method="getMethod"
:on-perform-success="onPerformSuccess"
:perform-submit="performSubmit"
/>
</IBox>
</template>
@@ -15,6 +17,7 @@
<script>
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm/index'
import { IBox } from '@/components'
import { Required } from '@/components/DataForm/rules'
export default {
name: 'ProfileUpdate',
@@ -68,9 +71,7 @@ export default {
label: this.$t('users.IAgree'),
type: 'checkbox',
checked: false,
rules: [
{ required: true }
],
rules: [Required],
helpText: this.$t('users.HelpText.MFAOfUserFirstLoginUserGuidePage')
}
},
@@ -87,6 +88,17 @@ export default {
methods: {
getMethod() {
return 'put'
},
performSubmit(validValues) {
if (!validValues.terms) {
this.$message.error(this.$t('common.PleaseAgreeToTheTerms'))
return Promise.reject()
}
return this.$axios['put'](this.url, validValues)
},
onPerformSuccess() {
this.$message.success(this.$t('common.updateSuccessMsg'))
setTimeout(() => this.$router.push({ name: 'UserGuide' }), 100)
}
}
}

View File

@@ -7,6 +7,7 @@
:url="url"
:get-method="getMethod"
class="password-update"
:update-success-next-route="updateSuccessNextRoute"
/>
</IBox>
</template>
@@ -50,6 +51,9 @@ export default {
type: 'password'
}
}
},
updateSuccessNextRoute: {
path: '/'
}
}
},

View File

@@ -54,12 +54,39 @@ export default {
type: 'primary',
label: this.$t('common.Update')
},
has: this.object.mfa_enabled,
callbacks: {
click: function() {
window.location.href = `/core/auth/profile/otp/update/?next=${this.$route.fullPath}`
}.bind(this)
}
},
{
title: this.$t('users.UpdatePassword'),
attrs: {
type: 'primary',
label: this.$t('common.Update'),
disabled: this.$store.state.users.profile.source !== 'local'
},
callbacks: {
click: function() {
this.$emit('update:activeMenu', 'PasswordUpdate')
}.bind(this)
}
},
{
title: this.$t('users.UpdateSSHKey'),
attrs: {
type: 'primary',
label: this.$t('common.Update'),
disabled: this.$store.state.users.profile.source !== 'local'
},
callbacks: {
click: function() {
this.$emit('update:activeMenu', 'SSHUpdate')
}.bind(this)
}
},
{
title: this.$t('users.ResetPublicKeyAndDownload'),
attrs: {

View File

@@ -1,12 +1,14 @@
<template>
<IBox>
<GenericCreateUpdateForm
ref="GenericCreateUpdateForm"
:fields="fields"
:fields-meta="fieldsMeta"
:initial="object"
:url="url"
:get-method="getMethod"
:more-buttons="moreButtons"
:on-perform-success="onPerformSuccess"
/>
</IBox>
</template>
@@ -46,7 +48,8 @@ export default {
public_key: {
el: {
type: 'textarea',
placeholder: 'ssh-rsa AAAA...'
placeholder: 'ssh-rsa AAAA...',
autosize: { minRows: 3 }
},
helpText: this.$t('users.HelpText.SSHKeyOfProfileSSHUpdatePage')
}
@@ -64,6 +67,10 @@ export default {
methods: {
getMethod() {
return 'put'
},
onPerformSuccess() {
this.$refs.GenericCreateUpdateForm.$refs.form.$refs.dataForm.resetForm('form')
this.$message.success(this.$t('common.updateSuccessMsg'))
}
}
}

View File

@@ -1,7 +1,7 @@
<template>
<GenericDetailPage :object.sync="user" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
<keep-alive>
<component :is="config.activeMenu" :object="user" />
<component :is="config.activeMenu" :object="user" @update:activeMenu="handleUpdate" />
</keep-alive>
</GenericDetailPage>
</template>
@@ -60,6 +60,9 @@ export default {
])
}
return submenu
},
handleUpdate(value) {
this.config.activeMenu = value
}
}
}

View File

@@ -17,7 +17,7 @@ export function getCurrentRoleFromCookie() {
}
export function saveCurrentRoleToCookie(role) {
console.log('Save current role to cookie: ', role)
// console.log('Save current role to cookie: ', role)
return VueCookie.set(CURRENT_ROLE_KEY, role, 14)
}
@@ -26,7 +26,7 @@ export function getCurrentOrgFromCookie() {
try {
org = JSON.parse(VueCookie.get(CURRENT_ORG_KEY))
} catch (e) {
console.log('Current org in cookie: ', org)
// console.log('Current org in cookie: ', org)
}
return org
}

View File

@@ -65,15 +65,23 @@ function cleanDateStr(d) {
case 1:
d = d.split('+')[0].trimRight()
break
case 2:
d = d.replace(/-/g, '/')
}
}
return null
return d
}
export function toSafeLocalDateStr(d) {
if (d === '') {
return ''
}
const date = safeDate(d)
// let date_s = date.toLocaleString(getUserLang(), {hour12: false});
const date_s = date.toLocaleString(getUserLang(), { hourCycle: 'h23' })
// const date_s = date.toLocaleString(getUserLang(), { hourCycle: 'h23' })
const date_s =
date.toLocaleDateString(getUserLang(), { hourCycle: 'h23' }) +
' ' +
date.toLocaleTimeString(getUserLang(), { hourCycle: 'h23' })
return date_s
}
@@ -136,6 +144,20 @@ export function getDaysAgo(days, now) {
return new Date(now.getTime() - 3600 * 1000 * 24 * days)
}
export function getDaysFuture(days, now) {
if (!now) {
now = new Date()
}
return new Date(now.getTime() + 3600 * 1000 * 24 * days)
}
export function getDayEnd(now) {
if (!now) {
now = new Date()
}
return new Date(new Date(now.toLocaleDateString()).getTime() + 24 * 60 * 60 * 1000 - 1)
}
export function setUrlParam(url, name, value) {
const urlArray = url.split('?')
if (urlArray.length === 1) {
@@ -164,3 +186,9 @@ export function getDayFuture(days, now) {
}
return new Date(now.getTime() + 3600 * 1000 * 24 * days)
}
const scheme = document.location.protocol
const port = document.location.port ? ':' + document.location.port : ''
const BASE_URL = scheme + '//' + document.location.hostname + port
export { BASE_URL }

View File

@@ -1,4 +1,4 @@
import { hasUUID } from '@/utils/common'
import { hasUUID, BASE_URL } from '@/utils/common'
import store from '@/store'
function getPropOrg() {
@@ -36,17 +36,20 @@ function hasCurrentOrgPermission() {
return orgInList
}
function changeOrg(orgId) {
async function changeOrg(orgId) {
const org = getOrgIdMapper()[orgId]
if (!org) {
console.debug('Error: org not found')
} else {
console.debug('Change to org: ', org)
}
// 重置Role为空
await store.dispatch('users/setCurrentRole', null)
store.dispatch('users/setCurrentOrg', org).then(() => {
console.log('Set current org to: ', org)
// console.log('Set current org to: ', org)
if (hasUUID(location.href)) {
location.href = process.env.VUE_APP_PUBLIC_PATH
location.href = BASE_URL
} else {
window.location.reload(true)
}

View File

@@ -1,9 +1,10 @@
import axios from 'axios'
import i18n from '@/i18n/i18n'
import NProgress from 'nprogress' // progress bar
import { getTokenFromCookie } from '@/utils/auth'
import { refreshSessionIdAge } from '@/api/users'
import { Message, MessageBox } from 'element-ui'
import store from '@/store'
import axiosRetry from 'axios-retry'
// create an axios instance
const service = axios.create({
@@ -13,11 +14,12 @@ const service = axios.create({
})
function beforeRequestAddToken(config) {
if (store.getters.token) {
const csrfToken = getTokenFromCookie()
if (csrfToken) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['X-CSRFToken'] = store.getters.token
config.headers['X-CSRFToken'] = csrfToken
}
if (store.getters.currentOrg) {
config.headers['X-JMS-ORG'] = store.getters.currentOrg.id
@@ -36,7 +38,7 @@ function beforeRequestAddTimezone(config) {
service.interceptors.request.use(
config => {
// do something before request is sent
NProgress.start()
// NProgress.start()
beforeRequestAddToken(config)
beforeRequestAddTimezone(config)
return config
@@ -71,6 +73,13 @@ function ifBadRequest({ response, error }) {
if (response.status === 400) {
error.message = i18n.t('common.BadRequestErrorMsg')
}
if (response.status === 403) {
error.message = i18n.t('common.BadRoleErrorMsg')
}
if (response.status === 409) {
error.response.status = 409
error.message = i18n.t('common.BadConflictErrorMsg')
}
}
export function flashErrorMsg({ response, error }) {
@@ -88,6 +97,19 @@ export function flashErrorMsg({ response, error }) {
}
}
let timer = null
function refreshSessionAgeDelay(response) {
if (response.request.responseURL.indexOf('/users/profile/') !== -1) {
return
}
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function() {
refreshSessionIdAge()
}, 30 * 1000)
}
// response interceptor
service.interceptors.response.use(
/**
@@ -101,7 +123,8 @@ service.interceptors.response.use(
* You can also judge the status by HTTP Status Code
*/
response => {
NProgress.done()
// NProgress.done()
refreshSessionAgeDelay(response)
const res = response.data
if (response.config.raw === 1) {
@@ -110,7 +133,7 @@ service.interceptors.response.use(
return res
},
error => {
NProgress.done()
// NProgress.done()
if (!error.response) {
return Promise.reject(error)
}
@@ -123,4 +146,9 @@ service.interceptors.response.use(
}
)
axiosRetry(service, {
// 默认不开启请求重试
retries: 0
})
export default service

View File

@@ -57,7 +57,7 @@ async function changeCurrentOrgIfNeed({ to, from, next }) {
}
const currentOrg = store.getters.currentOrg
if (!currentOrg || typeof currentOrg !== 'object') {
console.log('Not has current org')
// console.log('Not has current org')
orgUtil.change2PropOrg()
return reject('change prop org')
}

View File

@@ -10,9 +10,10 @@ export default {
},
data() {
return {
fields: [
[this.$t('common.Basic'), ['name', 'type']],
[this.$t('applications.mysql'), ['host', 'port', 'database']],
[this.$t('common.Basic'), ['name', 'type', 'domain']],
[this.$t('applications.DBInfo'), ['attrs']],
[this.$t('common.Other'), ['comment']]
],
fieldsMeta: {
@@ -23,14 +24,56 @@ export default {
value: 'mysql'
}],
disabled: true
},
host: {
type: 'input'
},
domain: {
el: {
multiple: false,
clearable: true,
ajax: {
url: '/api/v1/assets/domains/'
}
}
}
},
url: '/api/v1/applications/database-apps/'
url: '/api/v1/applications/applications/',
getUrl() {
const params = this.$route.params
let url = `/api/v1/applications/applications/`
const method = this.getMethod()
if (params.id) {
url = `${url}${params.id}/`
}
return method === 'post' ? `${url}?type=${this.$route.query.type}` : `${url}?category=db`
},
performSubmit(validValues) {
const params = this.$route.params
const baseUrl = `/api/v1/applications/applications/`
const url = (params.id) ? `${baseUrl}${params.id}/` : baseUrl
const method = this.getMethod()
validValues.attrs = {
host: validValues.host,
port: validValues.port,
database: validValues.database
}
validValues.category = 'db'
return this.$axios[method](`${url}?type=${validValues.type}`, validValues)
}
}
},
computed: {
initial() {
return this.$route.query
},
getMethod() {
const params = this.$route.params
if (params.id) {
return 'put'
} else {
return 'post'
}
}
}
}

View File

@@ -33,19 +33,19 @@ export default {
},
{
key: this.$t('applications.type'),
value: this.object.get_type_display
value: this.object.type_display
},
{
key: this.$t('applications.host'),
value: this.object.host
value: this.object.attrs.host
},
{
key: this.$t('applications.port'),
value: JSON.stringify(this.object.port)
value: JSON.stringify(this.object.attrs.port)
},
{
key: this.$t('applications.database'),
value: this.object.database
value: this.object.attrs.database
},
{
key: this.$t('common.dateCreated'),

View File

@@ -19,7 +19,7 @@ export default {
data() {
return {
DatabaseApp: {
name: '', get_type_display: '', host: '', port: '', database: '', date_created: '', created_by: '', comment: ''
name: '', get_type_display: '', host: '', port: '', database: '', date_created: '', created_by: '', comment: '', attrs: ''
},
config: {
activeMenu: 'DatabaseAppDetail',
@@ -28,7 +28,11 @@ export default {
title: this.$t('route.DatabaseAppDetail'),
name: 'DatabaseAppDetail'
}
]
],
actions: {
detailApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
deleteApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`
}
}
}
}

View File

@@ -1,9 +1,10 @@
<template>
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" />
<GenericListPage ref="GenericListTable" :table-config="tableConfig" :header-actions="headerActions" />
</template>
<script>
import { GenericListPage } from '@/layout/components'
import { mapGetters } from 'vuex'
export default {
components: {
@@ -12,21 +13,49 @@ export default {
data() {
return {
tableConfig: {
url: '/api/v1/applications/database-apps/',
url: '/api/v1/applications/applications/?category=db',
columns: [
'name', 'get_type_display', 'host', 'port', 'database', 'comment', 'actions'
'name', 'type_display', 'attrs.host', 'attrs.port', 'attrs.database', 'comment', 'actions'
],
columnsMeta: {
get_type_display: {
label: this.$t('applications.type')
type_display: {
label: this.$t('applications.type'),
width: '120px'
},
database: {
'attrs.host': {
label: this.$t('applications.host'),
width: '140px'
},
'attrs.port': {
label: this.$t('applications.port'),
width: '80px'
},
'attrs.database': {
label: this.$t('applications.database'),
showOverflowTooltip: true
},
actions: {
prop: '',
formatterArgs: {
hasClone: false,
performDelete: function({ row, col, cellValue, reload }) {
this.$axios.delete(
`/api/v1/applications/applications/${row.id}/`
).then(res => {
this.$refs.GenericListTable.$refs.ListTable.reloadTable()
// this.$message.success(this.$t('common.deleteSuccessMsg'))
}).catch(error => {
this.$message.error(this.$t('common.deleteErrorMsg' + ' ' + error))
})
}.bind(this)
}
}
}
},
headerActions: {
hasCreate: false,
hasExport: false,
hasImport: false,
hasBulkDelete: false,
createRoute: 'DatabaseAppCreate',
moreActionsTitle: this.$t('common.Create'),
@@ -36,16 +65,55 @@ export default {
name: 'MySQL',
title: 'MySQL',
type: 'primary',
can: true,
has: true,
callback: this.createMysql.bind(this)
},
{
name: 'PostgreSQL',
title: 'PostgreSQL',
type: 'primary',
has: this.isValidateLicense,
callback: this.createPostgreSQL.bind(this)
},
{
name: 'MariaDB',
title: 'MariaDB',
type: 'primary',
has: this.isValidateLicense,
callback: this.createMariaDB.bind(this)
},
{
name: 'Oracle',
title: 'Oracle',
type: 'primary',
has: this.isValidateLicense,
callback: this.createOracle.bind(this)
}
]
}
}
},
computed: {
...mapGetters(['publicSettings', 'currentOrg'])
},
methods: {
createMysql() {
this.$router.push({ name: 'DatabaseAppCreate', query: { type: 'mysql' }})
},
createPostgreSQL() {
this.$router.push({ name: 'DatabaseAppCreate', query: { type: 'postgresql' }})
},
createMariaDB() {
this.$router.push({ name: 'DatabaseAppCreate', query: { type: 'mariadb' }})
},
createOracle() {
this.$router.push({ name: 'DatabaseAppCreate', query: { type: 'oracle' }})
},
isValidateLicense() {
if (this.publicSettings.XPACK_ENABLED) {
return this.publicSettings.XPACK_LICENSE_IS_VALID
}
return false
}
}
}

View File

@@ -0,0 +1,76 @@
<template>
<GenericCreateUpdatePage v-bind="$data" />
</template>
<script>
import { GenericCreateUpdatePage } from '@/layout/components'
export default {
components: {
GenericCreateUpdatePage
},
data() {
return {
initial: {
type: 'k8s'
},
fields: [
[this.$t('common.Basic'), ['name', 'type', 'domain']],
[this.$t('applications.kubernetes'), ['attrs']],
[this.$t('common.Other'), ['comment']]
],
fieldsMeta: {
type: {
disabled: true
},
cluster: {
helpText: this.$t('applications.clusterHelpTextMessage')
},
domain: {
el: {
multiple: false,
clearable: true,
ajax: {
url: '/api/v1/assets/domains/'
}
}
}
},
url: '/api/v1/applications/applications/',
getUrl() {
const params = this.$route.params
let url = `/api/v1/applications/applications/`
if (params.id) {
url = `${url}${params.id}/`
}
return `${url}?type=k8s`
},
performSubmit(validValues) {
const params = this.$route.params
const baseUrl = `/api/v1/applications/applications/`
const url = (params.id) ? `${baseUrl}${params.id}/` : baseUrl
const method = this.getMethod()
validValues.attrs = {
cluster: validValues.cluster
}
validValues.category = 'cloud'
return this.$axios[method](`${url}?type=${validValues.type}`, validValues)
}
}
},
computed: {
getMethod() {
const params = this.$route.params
if (params.id) {
return 'put'
} else {
return 'post'
}
}
}
}
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,63 @@
<template>
<el-row :gutter="20">
<el-col :md="14" :sm="24">
<DetailCard :title="cardTitle" :items="detailItems" />
</el-col>
</el-row>
</template>
<script>
import DetailCard from '@/components/DetailCard'
import { toSafeLocalDateStr } from '@/utils/common'
export default {
name: 'DatabaseAppDetail',
components: {
DetailCard
},
props: {
object: {
type: Object,
default: () => ({})
}
},
computed: {
cardTitle() {
return this.object.name
},
detailItems() {
return [
{
key: this.$t('common.Name'),
value: this.object.name
},
{
key: this.$t('applications.type'),
value: this.object.type_display
},
{
key: this.$t('applications.cluster'),
value: this.object.attrs.cluster
},
{
key: this.$t('common.dateCreated'),
value: toSafeLocalDateStr(this.object.date_created)
},
{
key: this.$t('common.createdBy'),
value: this.object.created_by
},
{
key: this.$t('common.Comment'),
value: this.object.comment
}
]
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,44 @@
<template>
<GenericDetailPage :object.sync="KubernetesApp" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
<keep-alive>
<component :is="config.activeMenu" :object="KubernetesApp" />
</keep-alive>
</GenericDetailPage>
</template>
<script>
import { GenericDetailPage, TabPage } from '@/layout/components'
import KubernetesAppDetail from './KubernetesAppDetail'
export default {
components: {
GenericDetailPage,
KubernetesAppDetail,
TabPage
},
data() {
return {
KubernetesApp: {
name: '', type_display: '', cluster: '', date_created: '', created_by: '', comment: '', attrs: ''
},
config: {
activeMenu: 'KubernetesAppDetail',
submenu: [
{
title: this.$t('route.KubernetesAppDetail'),
name: 'KubernetesAppDetail'
}
],
actions: {
detailApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
deleteApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`
}
}
}
}
}
</script>
<style lang='scss' scoped>
</style>

View File

@@ -0,0 +1,60 @@
<template>
<GenericListPage ref="GenericListTable" :table-config="tableConfig" :header-actions="headerActions" />
</template>
<script>
import { GenericListPage } from '@/layout/components'
export default {
components: {
GenericListPage
},
data() {
return {
tableConfig: {
url: '/api/v1/applications/applications/?category=cloud',
columns: [
'name', 'type', 'attrs.cluster', 'comment', 'actions'
],
columnsMeta: {
'attrs.cluster': {
label: this.$t('applications.cluster')
},
comment: {
width: '340px'
},
type: {
width: '140px'
},
actions: {
prop: '',
formatterArgs: {
hasClone: false,
performDelete: function({ row, col, cellValue, reload }) {
this.$axios.delete(
`/api/v1/applications/applications/${row.id}/`
).then(res => {
this.$refs.GenericListTable.$refs.ListTable.reloadTable()
// this.$message.success(this.$t('common.deleteSuccessMsg'))
}).catch(error => {
this.$message.error(this.$t('common.deleteErrorMsg' + ' ' + error))
})
}.bind(this)
}
}
}
},
headerActions: {
hasBulkDelete: false,
hasExport: false,
hasImport: false,
createRoute: 'KubernetesAppCreate'
}
}
}
}
</script>
<style>
</style>

View File

@@ -22,14 +22,76 @@ export default {
path: pathInitial
},
fields: [
[this.$t('common.Basic'), ['name', 'asset', 'type', 'path']],
[appTypeMeta.title, ['params']],
[this.$t('common.Basic'), ['name', 'type', 'domain']],
[appTypeMeta.title, ['attrs']],
[this.$t('common.Other'), ['comment']]
],
url: '/api/v1/applications/remote-apps/',
url: '/api/v1/applications/applications/',
getUrl() {
const params = this.$route.params
let url = `/api/v1/applications/applications/`
if (params.id) {
url = `${url}${params.id}/`
}
return `${url}?type=${this.$route.query.type}`
},
performSubmit(validValues) {
const params = this.$route.params
const baseUrl = `/api/v1/applications/applications/`
const url = (params.id) ? `${baseUrl}${params.id}/` : baseUrl
const method = this.getMethod()
switch (validValues.type) {
case 'chrome': {
validValues.attrs = {
chrome_target: validValues.chrome_target,
chrome_username: validValues.chrome_username,
chrome_password: validValues.chrome_password,
asset: validValues.asset,
path: validValues.path
}
break
}
case 'mysql_workbench': {
validValues.attrs = {
mysql_workbench_ip: validValues.mysql_workbench_ip,
mysql_workbench_port: validValues.mysql_workbench_port,
mysql_workbench_name: validValues.mysql_workbench_name,
mysql_workbench_username: validValues.mysql_workbench_username,
mysql_workbench_password: validValues.mysql_workbench_password,
asset: validValues.asset,
path: validValues.path
}
break
}
case 'vmware_client': {
validValues.attrs = {
vmware_password: validValues.vmware_password,
vmware_username: validValues.vmware_username,
vmware_target: validValues.vmware_target,
asset: validValues.asset,
path: validValues.path
}
break
}
case 'custom': {
validValues.attrs = {
custom_cmdline: validValues.custom_cmdline,
custom_target: validValues.custom_target,
custom_username: validValues.custom_username,
custom_password: validValues.custom_password,
asset: validValues.asset,
path: validValues.path
}
break
}
}
validValues.category = 'remote_app'
console.log(validValues)
return this.$axios[method](`${url}?type=${validValues.type}`, validValues)
},
fieldsMeta: {
asset: {
rules: [{ required: false }],
rules: [{ required: true }],
el: {
multiple: false,
ajax: {
@@ -50,10 +112,30 @@ export default {
],
disabled: true
},
params: {
type: 'group',
items: fieldsMap
}
asset_info: {
type: 'input',
hidden: () => true
},
domain: {
el: {
multiple: false,
clearable: true,
ajax: {
url: '/api/v1/assets/domains/'
}
}
},
...fieldsMap
}
}
},
computed: {
getMethod() {
const params = this.$route.params
if (params.id) {
return 'put'
} else {
return 'post'
}
}
}

View File

@@ -32,17 +32,17 @@ export default {
key: this.$t('common.Name'),
value: this.object.name
},
{
key: this.$t('applications.asset'),
value: this.object.asset_info.hostname
},
// {
// key: this.$t('applications.asset'),
// value: this.object.attrs.asset_info.hostname
// },
{
key: this.$t('applications.appType'),
value: this.object.get_type_display
value: this.object.type_display
},
{
key: this.$t('applications.appPath'),
value: this.object.path
value: this.object.attrs.path
},
{
key: this.$t('common.dateCreated'),

View File

@@ -17,9 +17,10 @@ export default {
TabPage
},
data() {
const vm = this
return {
RemoteApp: {
name: '', asset: '', get_type_display: '', path: '', date_created: '', created_by: '', comment: ''
name: '', asset: '', get_type_display: '', path: '', date_created: '', created_by: '', comment: '', attrs: ''
},
config: {
activeMenu: 'RemoteAppDetail',
@@ -28,7 +29,14 @@ export default {
title: this.$t('route.RemoteAppDetail'),
name: 'RemoteAppDetail'
}
]
],
actions: {
detailApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
deleteApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
updateCallback: function(item) {
vm.$router.push({ name: 'RemoteAppUpdate', params: { id: vm.RemoteApp.id }, query: { type: vm.RemoteApp.type }})
}
}
}
}
}

View File

@@ -1,5 +1,5 @@
<template>
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" :help-message="helpMessage" />
<GenericListPage ref="GenericListTable" :table-config="tableConfig" :header-actions="headerActions" :help-message="helpMessage" />
</template>
<script type="text/jsx">
@@ -15,26 +15,39 @@ export default {
return {
helpMessage: this.$t('assets.RemoteAppListHelpMessage'),
tableConfig: {
url: '/api/v1/applications/remote-apps/',
url: '/api/v1/applications/applications/?category=remote_app',
columns: [
'name', 'type', 'asset', 'comment', 'actions'
'name', 'type', 'attrs.asset', 'comment', 'actions'
],
columnsMeta: {
type: {
displayKey: 'get_type_display'
displayKey: 'get_type_display',
width: '140px'
},
asset: {
'attrs.asset': {
label: this.$t('assets.Assets'),
showOverflowTooltip: true,
formatter: function(row, column, cellValue, index) {
const route = { to: { name: 'AssetDetail', params: { id: cellValue }}}
return <router-link{...{ attrs: route }} >{ row.asset_info.hostname }</router-link>
return <router-link{...{ attrs: route }} >{ row.attrs.asset_info.hostname }</router-link>
}
},
actions: {
formatterArgs: {
hasClone: false,
onUpdate: ({ row }) => {
vm.$router.push({ name: 'RemoteAppUpdate', params: { id: row.id }, query: { type: row.type }})
}
},
performDelete: function({ row, col, cellValue, reload }) {
this.$axios.delete(
`/api/v1/applications/applications/${row.id}/`
).then(res => {
this.$refs.GenericListTable.$refs.ListTable.reloadTable()
// this.$message.success(this.$t('common.deleteSuccessMsg'))
}).catch(error => {
this.$message.error(this.$t('common.deleteErrorMsg' + ' ' + error))
})
}.bind(this)
}
}
}
@@ -43,6 +56,8 @@ export default {
hasCreate: false,
hasMoreActions: false,
hasBulkDelete: false,
hasExport: false,
hasImport: false,
// createRoute: 'RemoteAppCreate',
moreActionsTitle: this.$t('common.Create'),
moreActionsType: 'primary',

View File

@@ -18,7 +18,7 @@ export const REMOTE_APP_TYPE_FIELDS_MAP = {
label: i18n.t('applications.chrome_username')
},
{
id: 'chrome_password', el: {}, attrs: {}, type: 'input', prop: 'chrome_password',
id: 'chrome_password', el: { 'show-password': true }, attrs: {}, type: 'password', prop: 'chrome_password',
label: i18n.t('applications.chrome_password')
}
],
@@ -27,6 +27,10 @@ export const REMOTE_APP_TYPE_FIELDS_MAP = {
id: 'mysql_workbench_ip', el: {}, attrs: {}, type: 'input', prop: 'mysql_workbench_ip',
label: i18n.t('applications.mysql_workbench_ip')
},
{
id: 'mysql_workbench_port', el: {}, attrs: {}, type: 'input', prop: 'mysql_workbench_port',
label: i18n.t('applications.mysql_workbench_port')
},
{
id: 'mysql_workbench_name', el: {}, attrs: {}, type: 'input', prop: 'mysql_workbench_name',
label: i18n.t('applications.mysql_workbench_name')
@@ -36,7 +40,7 @@ export const REMOTE_APP_TYPE_FIELDS_MAP = {
label: i18n.t('applications.mysql_workbench_username')
},
{
id: 'mysql_workbench_password', el: {}, attrs: {}, type: 'input', prop: 'mysql_workbench_password',
id: 'mysql_workbench_password', el: { 'show-password': true }, attrs: {}, type: 'password', prop: 'mysql_workbench_password',
label: i18n.t('applications.mysql_workbench_password')
}
],
@@ -50,7 +54,7 @@ export const REMOTE_APP_TYPE_FIELDS_MAP = {
label: i18n.t('applications.vmware_username')
},
{
id: 'vmware_password', el: {}, attrs: {}, type: 'input', prop: 'vmware_password',
id: 'vmware_password', el: { 'show-password': true }, attrs: {}, type: 'password', prop: 'vmware_password',
label: i18n.t('applications.vmware_password')
}
],
@@ -68,7 +72,7 @@ export const REMOTE_APP_TYPE_FIELDS_MAP = {
label: i18n.t('applications.custom_username')
},
{
id: 'custom_password', el: {}, attrs: {}, type: 'input', prop: 'custom_password',
id: 'custom_password', el: { 'show-password': true }, attrs: {}, type: 'password', prop: 'custom_password',
label: i18n.t('applications.custom_password')
}
]

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