Compare commits

...

1271 Commits

Author SHA1 Message Date
zhaojisen
3e009c17fd fixed: Fixed an issue where multiple selections across pages could not be reversed 2024-12-04 15:14:44 +08:00
feng
66532f4d4b fix: ticket duplicate submission 2024-11-26 16:27:27 +08:00
wangruidong
42f27eb30f perf: users use Select2 2024-10-17 10:34:54 +08:00
wangruidong
57920bf771 fix: user lack permission to view the type tree 2024-10-10 17:11:40 +08:00
ZhaoJiSen
290772f44e Merge pull request #4384 from jumpserver/pr@v3@fix_special_chara
fixed: Fixed + and - not being special characters
2024-10-09 18:37:03 +08:00
zhaojisen
f140f2f59e fixed: Fixed + and - not being special characters 2024-10-09 18:35:40 +08:00
zhaojisen
7b1883e012 fixed: Fixed + and - not being special characters 2024-10-09 18:28:00 +08:00
wisonic
352ac7e828 fix: assets proportion tooltip cause page bounced at first time 2024-09-27 15:16:26 +08:00
ZhaoJiSen
1cbd58664c Merge pull request #4375 from jumpserver/pr@v3@perf_import_style
perf: Import UI style optimizations
2024-09-26 17:35:26 +08:00
zhaojisen
e48da6be9b perf: Import UI style optimizations 2024-09-26 17:30:14 +08:00
wangruidong
fa31b36550 fix: xterm output truncate 2024-09-26 17:16:22 +08:00
ibuler
6b93a6563d perf: revert pre org if logout 2024-08-30 17:02:49 +08:00
feng
d561701049 perf: Ticket flow rule user display name(username) 2024-08-19 16:06:55 +08:00
Ewall555
edbf477c1e perf: Translate ticket cancel button 2024-08-15 15:49:49 +08:00
ZhaoJiSen
6a2578b339 Merge pull request #4292 from jumpserver/pr@v3@fix_loading
perf: add button loading status
2024-08-08 15:01:35 +08:00
zhaojisen
2cb7569cb0 perf: add button loading status 2024-08-08 15:00:11 +08:00
fit2bot
9e0c623b9a perf: add button loading status (#4287)
* perf: add button loading status

* perf: add button loading status

---------

Co-authored-by: zhaojisen <1301338853@qq.com>
2024-08-08 11:38:04 +08:00
feng
40bf040501 perf: Asset authorization: The number of accounts displayed is incorrect ignore @SPEC 2024-08-07 17:51:21 +08:00
halo
efee7c7bbf perf: Email service authentication username is optional 2024-07-26 14:20:20 +08:00
ZhaoJiSen
5daecb84ae Merge pull request #4251 from jumpserver/pr@v3@fix_data_refresh
fixed: Fixed an issue where the validation date does not refresh
2024-07-23 10:09:16 +08:00
feng
3be325214d perf: You can modify sudo permissions multiple times 2024-07-22 18:18:11 +08:00
zhaojisen
581509f42f fixed: Fixed an issue where the validation date does not refresh 2024-07-22 14:52:45 +08:00
zhaojisen
654b36b064 fixed: Fixed the issue that parameter push parameters could not be saved 2024-07-17 10:49:02 +08:00
feng626
dcec73ae67 Merge pull request #4224 from jumpserver/pr@v3@download
perf: Downloading files does not trigger the beforeunload event
2024-07-16 12:42:48 +08:00
feng
00bafa8164 perf: Downloading files does not trigger the beforeunload event 2024-07-16 12:40:31 +08:00
zhaojisen
da09af79a7 fixed:Fixed an issue where the user was unable to enter non-MD content 2024-07-15 19:08:46 +08:00
zhaojisen
b596815ea5 fixed:Fixed an issue where the user was unable to enter non-MD content 2024-07-15 19:08:46 +08:00
ibuler
cb37273e80 perf: table search two times, one init one search 2024-07-12 10:20:55 +08:00
zhaojisen
c5bf7d0ad2 fixed: Fixed an issue where push parameters could not be saved when adding an asset account 2024-07-11 15:18:11 +08:00
wangruidong
c31195a67a perf: profile improvement mfa disabled 2024-07-10 14:21:27 +08:00
wangruidong
1eb59b379a perf: profile improvement mfa cancel hide 2024-07-09 16:14:43 +08:00
ZhaoJiSen
b7cee17156 Merge pull request #4188 from jumpserver/pr@v3@fix_unuse_item
fixed: Remove unused fields
2024-07-08 19:01:07 +08:00
zhaojisen
f1c8874010 fixed: Remove unused fields 2024-07-08 18:57:37 +08:00
Gerry.tan
5ccaa3b77d perf: 优化用户校验页面对密码进行加密传输 2024-06-24 10:38:00 +08:00
w940853815
27d3637330 Revert "fix: 待我审批列表页面默认过滤出还未审批的工单"
This reverts commit 91c44d0500.
2024-06-19 19:33:40 +08:00
wangruidong
9c8ceb04f0 perf: Disable editing labels for the root organization 2024-06-19 15:46:46 +08:00
wangruidong
ccd7b319c8 fix: 审计台仪表盘今日数据有问题 2024-06-18 17:59:18 +08:00
feng626
55637c7fa1 Merge pull request #4063 from jumpserver/pr@dev@account_template
fix: Account tempalte update secret failed
2024-06-18 14:54:14 +08:00
feng
4e95c88318 fix: Account tempalte update secret failed 2024-06-18 14:52:50 +08:00
wangruidong
1ff49ca16d perf: 系统设置-组织管理:最新创建成功后返回列表未按创建时间排序 2024-06-17 17:30:34 +08:00
wangruidong
91c44d0500 fix: 待我审批列表页面默认过滤出还未审批的工单 2024-06-13 16:15:35 +08:00
wangruidong
0b3a9844f7 fix: Crontab - Failed to set minute range 2024-06-13 10:40:22 +08:00
feng
95b58f3c96 perf: Complete asset hardware information 2024-06-11 18:49:03 +08:00
wangruidong
128b9c79ba fix: 工单-待我审批默认筛选打开的工单 2024-06-11 17:46:09 +08:00
halo
4eda83f83d perf: 优化图标不对齐的问题 2024-06-07 15:55:20 +08:00
halo
4cd0071054 feat: 支持批量测试资产可连接性 2024-06-07 11:18:35 +08:00
wangruidong
67a2a9be6a If non-existent values are entered into the select component, won't trigger a search request. 2024-06-05 16:01:46 +08:00
ibuler
f927a2a3cc perf: action 添加 token 2024-06-04 10:32:48 +08:00
Gerry.tan
ca40cb34da feat: 新增 dameng 图标 2024-05-31 11:11:06 +08:00
feng
d725e5497d perf: console dashboard api 2024-05-21 16:26:34 +08:00
ibuler
56f6c17275 perf: 优先选择上个 org 切换 2024-05-16 13:53:47 +08:00
ibuler
e1bde89b29 fix: 修复切换到全局组织回不来的问题
perf: 修改组织切换
2024-05-15 19:14:58 +08:00
feng626
e9da168c9f Merge pull request #3930 from jumpserver/pr@dev@mfa
perf: User personal settings mfa new window opens
2024-05-15 16:41:15 +08:00
feng
c19ef24ec9 perf: User personal settings mfa new window opens 2024-05-15 16:40:17 +08:00
wangruidong
fb7c4a8b2a fix: 竖屏审批工单时,动作显示不出来 2024-05-15 15:33:03 +08:00
wangruidong
428ba49f9c perf: 根据type生成导出文件名 2024-05-11 10:02:53 +08:00
ibuler
7602d6e270 fix: 修复自动切换到 root org 回不来的问题 2024-05-10 19:07:46 +08:00
wangruidong
7b62ce2d33 fix: 批量传输下载结果文件名undefined 2024-05-07 14:42:06 +08:00
jiangweidong
6ed40c45b0 perf: 云同步支持同步完成后自动删除云端已经释放的资产 2024-05-07 14:40:29 +08:00
wangruidong
31238e0398 fix: 命令存储创建es后跳转路由不对 2024-04-28 10:24:44 +08:00
wangruidong
676ac2bbf6 perf: 创建、更新用户时MFA选项根据系统设置选项进行动态渲染 2024-04-26 11:35:12 +08:00
Bai
d5415b84c9 feat: Support asset tree node drag to another one 2024-04-24 18:06:12 +08:00
Bai
5e91917ba4 perf: 优化 Web 资产详情时根据 autofill 类型返回对应的 spec_info 信息 2024-04-23 13:08:59 +08:00
zhaojisen
c4361b4c17 perf: 修复默认值相关内容,优化按钮禁用条件 2024-04-23 10:17:43 +08:00
Bai
70b5ec3683 fix: Fixed User first login long wait.(SYSTEM ORG) 2024-04-22 19:17:11 +08:00
Bai
c93a061852 fix: Linux Platform id is not 1, create gateway error 2024-04-22 15:54:56 +08:00
feng626
4351d20a1e Merge pull request #3867 from jumpserver/pr@dev@asset
perf: 新建/更新资产时,新tab页面打开
2024-04-22 14:17:29 +08:00
feng
ce23d53e3c perf: 新建/更新资产时,新tab页面打开 2024-04-22 14:16:59 +08:00
wangruidong
32fc16126f perf: dashboard typo 2024-04-22 13:24:52 +08:00
wangruidong
b9a99148e3 perf: 修改用户详情页-资产授权规则默认字段展示 2024-04-18 15:47:42 +08:00
ibuler
2a4b99484b perf: 修改查看 account 密码,切换 route 查询多次 2024-04-18 13:39:45 +08:00
ibuler
bd26894135 perf: 优化查看 account secret api 次数 2024-04-18 13:29:20 +08:00
wangruidong
01e55d7f6e perf: 全局组织禁用资产的测试可连接性 2024-04-18 11:40:10 +08:00
wangruidong
d0a7201683 perf: 优化查看资产账号列表过长显示不全 2024-04-18 11:25:52 +08:00
feng626
e2dcc98ab3 Merge pull request #3856 from jumpserver/pr@dev@dashboard
perf: dashboard 数字保留小数后两位
2024-04-17 20:05:45 +08:00
feng
8dd4f89395 perf: dashboard 数字保留小数后两位 2024-04-17 20:04:53 +08:00
feng
33a997f3bb fix: 仪表盘计算不准确 2024-04-17 19:16:59 +08:00
wangruidong
1adee78456 perf: QuickJob asset tree css 2024-04-17 14:45:12 +08:00
ibuler
baf2b6cd9b perf: view secret request many times 2024-04-17 14:25:36 +08:00
jiangweidong
28e756e163 perf: 优化云同步测试账号再次打开后不显示上次选的地域信息 2024-04-16 17:44:17 +08:00
老广
251873a7e9 Merge pull request #3850 from jumpserver/pr@dev@perf_krry_page
perf: 优化 krry paging
2024-04-16 17:33:20 +08:00
wangruidong
ff9bd42322 perf: 适配手机端 2024-04-16 15:40:30 +08:00
ibuler
2c245020cd perf: 优化 krry paging
chrome: remove debug
2024-04-16 14:34:20 +08:00
wangruidong
429d2fec40 perf: asset tree css 2024-04-16 11:38:40 +08:00
feng626
c2f8fe45a1 Merge pull request #3848 from jumpserver/pr@dev@records
fix: 改密推送任务记录查看失败
2024-04-16 10:36:08 +08:00
feng
caa5e7df75 fix: 改密推送任务记录查看失败 2024-04-16 10:34:05 +08:00
ibuler
8a439dec8d fix: 修复先删除标签resource,再创建报错 2024-04-15 19:43:51 +08:00
ibuler
eabf5a117d perf: account template support labels 2024-04-15 19:43:31 +08:00
wangruidong
c30672a014 fix: 会话详情中文件传输显示有误 2024-04-15 14:42:40 +08:00
wangruidong
5c5df32181 perf: 优化停止任务log输出 2024-04-12 11:29:41 +08:00
Bai
ce831df717 fix: I18n 2024-04-12 11:20:26 +08:00
feng626
04688930ad Merge pull request #3841 from jumpserver/pr@dev@change_secret
fix: 改密任务记录搜索失败
2024-04-12 11:03:01 +08:00
feng
ae5787ae52 fix: 改密任务记录搜索失败 2024-04-12 11:01:39 +08:00
wangruidong
1dfdfe3932 fix: xss处理后无class属性 2024-04-11 19:23:52 +08:00
wangruidong
f409abbf79 perf: 修改按钮样式,添加网关label 2024-04-11 17:33:55 +08:00
wangruidong
a6232da3d0 perf: 修改任务停止日志输出提示 2024-04-11 14:40:35 +08:00
feng626
2690538db6 Merge pull request #3837 from jumpserver/pr@dev@logo
fix: 点击logo前端页面卡住
2024-04-11 14:38:35 +08:00
feng
6d0af7a149 fix: 点击logo前端页面卡住 2024-04-11 14:32:05 +08:00
Bai
babc048eb0 fix: Session duration recurring 2024-04-11 11:01:36 +08:00
wangruidong
d49be903e8 perf: Domain detail add gateways 2024-04-10 16:46:47 +08:00
Bai
332058b0ea perf: Domain detail add asset 2024-04-10 16:46:47 +08:00
feng
e355abc1af perf: 切换zh hant 2024-04-10 15:31:39 +08:00
Bai
99200d58bb feat: LDAP User Auth support cache user_dn 2024-04-09 20:11:06 +08:00
ibuler
61bb97efa9 perf: xss attack 2024-04-09 17:09:01 +08:00
fit2bot
c5e030e2fe perf: Update user orgs roles (#3829)
* perf: Update user orgs roles

* perf: Update user orgs roles

---------

Co-authored-by: Bai <baijiangjie@gmail.com>
2024-04-09 16:51:05 +08:00
fit2bot
8a60ad774f perf: Update user orgs roles (#3828)
Co-authored-by: Bai <baijiangjie@gmail.com>
2024-04-09 15:44:07 +08:00
hzhfit2cloud
b08e6de527 支持中文繁体 2024-04-09 15:20:39 +08:00
feng
a96851686f fix: 修复xss漏洞 工单查不到数据问题 2024-04-08 18:56:22 +08:00
wangruidong
1b0adbfe71 fix: Web资产克隆,选择器信息丢失 2024-04-08 18:54:43 +08:00
wangruidong
c28419438b fix: 虚拟账号列表排序无效 2024-04-08 16:34:10 +08:00
jiangweidong
8807f24bcd perf: 云同步支持火山引擎 2024-04-08 14:04:56 +08:00
Bai
b8a914eb02 perf: ROOT Org show orgs-and-roles in user-detail page 2024-04-08 14:02:10 +08:00
wangruidong
8bbc66a281 fix: 授权资产查看账号失败 2024-04-08 11:35:13 +08:00
feng626
19bab778ca Merge pull request #3818 from jumpserver/pr@dev@user_session
fix: 修复用户下线失败问题
2024-04-03 16:41:43 +08:00
feng
134bcda895 fix: 修复用户下线失败问题 2024-04-03 16:14:54 +08:00
wangruidong
8b725fa4f6 perf: TransferSelect Dialog手机端适配 2024-04-02 19:21:33 +08:00
wangruidong
205f8bc280 fix: 勾选指定账号时,数量显示有问题 2024-04-02 19:20:44 +08:00
wangruidong
4963446b74 fix: 统一成模板 2024-04-02 19:19:40 +08:00
wangruidong
98be3903db fix: 华为交换机执行快捷命令报错 2024-04-02 18:51:19 +08:00
wangruidong
2cb7a859a8 perf: 授权的资产列表中支持查看某个资产授权的账号 2024-04-02 17:06:55 +08:00
wangruidong
d51571c530 perf: 搜索框和右侧在同一水平线上 2024-03-29 18:29:50 +08:00
wangruidong
bf0d19ac0b perf: 工单页面去掉一些筛选字段 2024-03-29 14:17:44 +08:00
wangruidong
28062f5d60 perf: Web,ChatGPT资产测试可连接性按钮禁用 2024-03-28 18:20:35 +08:00
ibuler
720dee578a fix: 修复创建账号时,资产没有默认值的问题 2024-03-28 18:15:50 +08:00
feng626
cc0d78a4e7 Merge pull request #3809 from jumpserver/pr@dev@role
fix: 修改content type 权限
2024-03-28 15:22:13 +08:00
feng
9f0b904043 fix: 修改content type 权限 2024-03-28 15:21:31 +08:00
dependabot[bot]
90cbb25d47 build(deps): bump express from 4.18.2 to 4.19.2
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-27 16:18:44 +08:00
dependabot[bot]
450d9562c3 build(deps): bump ip from 1.1.8 to 1.1.9
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.8 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.8...v1.1.9)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-27 16:18:19 +08:00
dependabot[bot]
a69eec5ef6 build(deps): bump axios from 0.21.1 to 0.28.0
Dependabot couldn't find the original pull request head commit, a01b1986d1e53870ecc18abd7ee72104fba7a08b.
2024-03-27 16:17:56 +08:00
wangruidong
5f6fc7e3b4 fix: 待审批列表显示没有过滤的问题 2024-03-27 16:10:22 +08:00
wangruidong
62b6ca026e perf: 优化资产、网域、网关的操作体验 2024-03-27 16:02:39 +08:00
Bai
006f258938 perf: Optimize the context menu for right-clicking on the asset tree. 2024-03-27 10:56:47 +08:00
feng626
756b7db6b6 Merge pull request #3798 from jumpserver/pr@dev@perm_asset
perf: 资产授权列表优化
2024-03-26 18:07:51 +08:00
feng
a8d7c01f94 perf: 资产授权列表优化 2024-03-26 18:06:37 +08:00
feng
0c7e7ecc99 feat: 拆分 feishu lark 2024-03-26 17:11:22 +08:00
feng626
342a70c441 Merge pull request #3795 from jumpserver/pr@dev@asset_perm
fix: 资产授权列表没有有效期字段
2024-03-22 16:01:12 +08:00
wangruidong
815247f5b5 perf: 封装SwitchFormatter组件 2024-03-22 15:28:13 +08:00
wangruidong
e6721a9905 feat: 支持开启、关闭定时任务执行 2024-03-22 15:28:13 +08:00
feng
d6305fddfd fix: 资产授权列表没有有效期字段 2024-03-22 14:47:15 +08:00
Eric
6db2d5ae31 perf: 支持发布机卸载远程应用 2024-03-21 16:12:13 +08:00
feng626
9b43bba6f8 Merge pull request #3790 from jumpserver/pr@dev@change_secret_record
feat: 改密记录可批量重试 新增更多过滤选项
2024-03-21 11:09:26 +08:00
feng
f9d89b30e2 feat: 改密记录可批量重试 新增更多过滤选项 2024-03-21 11:08:30 +08:00
feng626
be29794b4c Merge pull request #3788 from jumpserver/pr@dev@change_secret
perf: 改密记录可查看密文
2024-03-21 11:05:11 +08:00
jiangweidong
97bf4f1b97 feat: 云账号测试支持选择地域 2024-03-20 16:39:12 +08:00
Bai
76e7684e26 perf: 优化 Session 支持 duration 字段 2024-03-20 15:52:23 +08:00
wangruidong
2bfd764da7 perf: 支持导出收集账号列表的数据 2024-03-18 18:02:58 +08:00
wangruidong
daf3defe14 perf: 修改oracle默认数据库提示 2024-03-18 18:02:34 +08:00
Eric
d8c165ca78 perf: 支持发布机仅初始化配置 2024-03-18 15:52:22 +08:00
feng
42115b2c30 perf: 改密记录可查看密文 2024-03-15 17:29:23 +08:00
老广
447013abf5 Merge pull request #3784 from jumpserver/pr@dev@perf_asset_tree
perf: 优化资产树 root 节点宽度
2024-03-15 14:43:24 +08:00
ibuler
44216326f9 perf: 优化资产树 root 节点宽度 2024-03-15 06:38:32 +00:00
feng
fd64e71a3b perf: 优化user secret key 处理逻辑 2024-03-15 11:46:44 +08:00
feng626
7b990a264f Merge pull request #3782 from jumpserver/pr@dev@translate
perf: translate
2024-03-15 10:34:20 +08:00
feng
7687917aae perf: translate 2024-03-15 10:33:13 +08:00
wangruidong
02b71619de perf: 支持终断批量快捷命令执行的任务 2024-03-14 11:18:04 +08:00
Bai
da90b23f99 perf: 优化我的资产页面不能编辑 Labels 的问题 2024-03-14 11:08:58 +08:00
wangruidong
b8090abcdc perf: 添加oracle默认数据库提示 2024-03-14 11:08:31 +08:00
feng626
45b07dd628 Merge pull request #3777 from jumpserver/pr@dev@customize_footer
feat: 自定义footer
2024-03-12 14:34:44 +08:00
feng
ad7a6a5579 feat: 自定义footer 2024-03-12 14:22:28 +08:00
wangruidong
67c8521d5c fix: 自动任务跳转执行列表显示结果不准确的问题 2024-03-12 11:18:33 +08:00
wangruidong
18fc63b8c0 feat: 支持一键导出批量命令执行日志 2024-03-12 11:01:27 +08:00
w940853815
64d2854e49 Revert "fix: 刷新页面搜索条件丢失的问题"
This reverts commit 99f83a6bc4.
2024-03-11 18:20:24 +08:00
wangruidong
99f83a6bc4 fix: 刷新页面搜索条件丢失的问题 2024-03-07 14:22:20 +08:00
feng626
ca9f90624e Merge pull request #3772 from jumpserver/pr@dev@detail_formatter
perf: 支持点击“转到”后进行颜色标识
2024-03-06 19:11:00 +08:00
feng
05edf98514 perf: 支持点击“转到”后进行颜色标识 2024-03-06 19:10:04 +08:00
Bai
45b8c622bc perf: 优化界面设置页面主题Logo预览的背景颜色 2024-03-06 17:53:01 +08:00
feng626
faf1fb60b7 Merge pull request #3769 from jumpserver/pr@dev@account_gather
feat: 账号收集任务可单独添加资产
2024-03-05 15:00:27 +08:00
feng
f28825ba74 feat: 账号收集任务可单独添加资产 2024-03-05 14:59:02 +08:00
wangruidong
3626fd024d fix: 刷新页面根据搜索条件过滤出对应的资源 2024-03-04 19:13:18 +08:00
feng626
79ec0610a8 Merge pull request #3766 from jumpserver/revert-3764-pr@dev@fix_url_search
Revert "fix: 刷新页面根据搜索条件过滤出对应的资源"
2024-03-04 17:54:35 +08:00
Bryan
eee5889d51 Revert "fix: 刷新页面根据搜索条件过滤出对应的资源" 2024-03-04 17:53:46 +08:00
feng626
038ec49d5e Merge pull request #3764 from jumpserver/pr@dev@fix_url_search
fix: 刷新页面根据搜索条件过滤出对应的资源
2024-03-04 17:41:49 +08:00
wangruidong
669ffc73fd fix: 刷新页面根据搜索条件过滤出对应的资源 2024-03-04 14:24:39 +08:00
feng626
278562e855 Merge pull request #3763 from jumpserver/pr@dev@account_push
perf: 社区版账号推送,隐藏掉定时推送按钮
2024-03-01 16:57:37 +08:00
feng
fafa088a5e perf: 社区版账号推送,隐藏掉定时推送按钮 2024-03-01 16:51:06 +08:00
wangruidong
1ff65a2293 fix: sftp会话详情禁用监控按钮 2024-02-29 14:33:39 +08:00
Bai
1c69c61432 fix: 修复文件传输权限位 2024-02-29 14:13:42 +08:00
wangruidong
046870e366 fix: 隐藏停止任务按钮 2024-02-28 20:51:59 +08:00
feng626
b44af12f3b Merge pull request #3756 from jumpserver/pr@dev@push_params
fix: 平台中的改密参数和推送参数没了
2024-02-28 18:49:47 +08:00
feng
d9a9c7e229 fix: 平台中的改密参数和推送参数没了 2024-02-28 18:48:06 +08:00
wangruidong
55dfa5889b perf: 终断任务按钮状态变化优化 2024-02-28 10:00:13 +08:00
wangruidong
b35b5bd774 fix: 远程应用账号列表排版问题 2024-02-28 09:59:52 +08:00
feng626
1ee9e5df78 Merge pull request #3751 from jumpserver/pr@dev@push_account
fix: 【资产列表】资产详情中推送账号,推送参数未获取平台的参数
2024-02-27 16:56:50 +08:00
feng
2fe6cb37e6 fix: 【资产列表】资产详情中推送账号,推送参数未获取平台的参数 2024-02-27 16:55:48 +08:00
feng626
da570e21ee Merge pull request #3747 from jumpserver/pr@dev@luna
fix: 跳转到 luna 组织不对
2024-02-27 10:23:55 +08:00
wangruidong
16adcd299a perf: 平台参数联动逻辑优化 2024-02-26 19:49:26 +08:00
wangruidong
df8a464c36 fix: Web资产的选择器未根据平台参数联动 2024-02-26 16:40:15 +05:00
wangruidong
52e121cfdb perf: 禁用redis/clickhouse测试可连接性按钮 2024-02-26 15:53:20 +05:00
feng626
f014fc6426 Merge pull request #3748 from jumpserver/pr@dev@user_group
perf: Default组织下,标签关联用户资源,去掉组件用户
2024-02-26 17:21:00 +08:00
feng
88a6c2bb2b perf: Default组织下,标签关联用户资源,去掉组件用户 2024-02-26 17:08:05 +08:00
feng
8fa31fe0c2 fix: 跳转到 luna 组织不对 2024-02-26 16:24:50 +08:00
wangruidong
8f246c18e1 perf: 作业日志添加任务类型 2024-02-26 13:43:25 +08:00
wangruidong
9d999a7119 fix: LDAP用户导入会超时 2024-02-22 11:37:58 +08:00
wangruidong
dc8f237fec fix: 类别、类型排序无效 2024-02-21 14:21:38 +08:00
jiangweidong
41d0615ab5 feat: 支持工单链接直接免密审批 2024-02-21 11:39:57 +08:00
feng626
fadc3e7dd0 Merge pull request #3736 from jumpserver/pr@dev@account
perf: 账号收集添加资产名称模糊搜索
2024-02-20 18:41:58 +08:00
feng
152f56b496 perf: 账号收集添加资产名称模糊搜索 2024-02-20 18:39:48 +08:00
wangruidong
ed4f8dea90 perf: 终断批量快捷命令执行的任务 2024-02-20 15:13:11 +08:00
feng626
dced020a20 Merge pull request #3734 from jumpserver/pr@dev@perm_user
perf: 授权用户列表显示角色
2024-02-19 14:48:46 +08:00
feng
bf3c87575c perf: 授权用户列表显示角色 2024-02-19 14:43:01 +08:00
Eric
dadb54090c perf: 会话活动日志 2024-02-06 18:32:53 +08:00
ibuler
3f683b012c perf: 修改 ai chat 的位置 2024-02-06 17:59:13 +08:00
wangruidong
ecb1e91136 fix: Web资产选择器需跟平台的参数适配 2024-02-05 18:17:33 +08:00
wangruidong
454947f08b perf: 支持改密日志记录保留天数 2024-02-05 18:07:04 +08:00
feng
3ff6c6fe2f fix: 导出下载更新模版用不同的action 去对应后台serializer 2024-02-05 17:51:56 +08:00
Bai
527cc4d727 fix: 修复用户登录403的问题(DEFAULT组织由后端进行设置) 2024-02-05 16:52:31 +08:00
wangruidong
3b4201d2bf perf: 修改翻译 2024-02-05 15:54:42 +08:00
wangruidong
ba109da324 perf: 禁止用户自身更新自己的某些属性 2024-02-04 18:01:21 +08:00
jiangweidong
2a92c7657c perf: 支持账号批量更新功能 (#3717) 2024-02-04 17:50:22 +08:00
feng626
beb8ace5bd Merge pull request #3723 from jumpserver/pr@dev@asset_select
perf: 改密推送 选择资产支持标签搜索
2024-02-04 17:29:13 +08:00
feng
5e1225524c perf: 改密推送 选择资产支持标签搜索 2024-02-04 17:27:46 +08:00
wangruidong
931042eb2f perf: 国际电话区号选项从api返回 2024-02-04 14:52:50 +08:00
feng626
383577bb18 Merge pull request #3721 from jumpserver/pr@dev@ssh_key
perf: 【模版账号】创建/更新 模版账号表单中,希望增加上传密钥按钮
2024-02-02 18:44:59 +08:00
feng
f9e94386de perf: 【模版账号】创建/更新 模版账号表单中,希望增加上传密钥按钮 2024-02-02 18:41:11 +08:00
feng
5879eed926 perf: 优化用户session 会话过期 2024-02-02 17:54:46 +08:00
wangruidong
e1e54bf7a3 fix: 控制台,审计台仪表盘图表显示不对 2024-01-31 13:55:05 +08:00
ibuler
9e0c43589d perf: 优化首页日期 tab 按钮的颜色 2024-01-31 10:00:11 +08:00
wangruidong
ca3b0cfce5 perf: crontab修改提示翻译 2024-01-30 17:48:25 +08:00
jiangweidong
245b3f4ad2 perf: 资源详情页面标题长度超过一行则省略表示 2024-01-29 16:52:12 +08:00
ibuler
ea575e0515 perf: 优化数量显示,异步获取 hover 的内容 2024-01-29 16:50:42 +08:00
“huailei000”
ff63d2ca39 perf: 优化快捷命令输出框跟随浏览器高度 2024-01-29 16:49:38 +08:00
feng626
045af27999 Merge pull request #3711 from jumpserver/pr@dev@account_template
feat: 账号模版可导入导出
2024-01-29 16:42:45 +08:00
feng626
257365932c Merge pull request #3710 from jumpserver/pr@dev@account_bulk_test
feat: 批量测试账号可连接性
2024-01-29 16:42:18 +08:00
feng
aca4e4077f feat: 账号模版可导入导出 2024-01-29 16:19:19 +08:00
feng
ce80d36b8b feat: 批量测试账号可连接性 2024-01-29 14:40:49 +08:00
wangruidong
7dd5256303 perf: 用户详情 - 授权资产减少默认字段 2024-01-25 17:19:09 +08:00
wangruidong
fcf1093b4c perf: 安全模式返回授权的资产 2024-01-25 17:08:15 +08:00
feng626
5f9e9afffb Merge pull request #3709 from jumpserver/pr@dev@translate
perf: 翻译
2024-01-25 14:36:18 +08:00
feng
1bba9980c2 perf: 翻译 2024-01-25 14:35:13 +08:00
halo
6cbcee7656 perf: 指定账号enter异常刷新整个页面 2024-01-22 06:38:19 +00:00
halo
2084c50f95 perf: 资产授权弹窗选择时点击遮罩退出问题 2024-01-22 06:37:53 +00:00
“huailei000”
20d98bf09e perf: 优化更多操作高度;优化快捷命令右侧图标;优化账号管理列表-定期执行在中、英文状态下的宽度 2024-01-19 10:32:38 +00:00
“huailei000”
05c2f1f859 perf: 快捷命令toolbar增加折叠功能 2024-01-19 10:31:36 +00:00
“huailei000”
1e9107ec4a perf: 兼容luna显示智能问答 2024-01-19 09:23:19 +00:00
wangruidong
96a3f0a334 fix: 网关列表资产数量点击详情中没有资产 2024-01-18 08:11:46 +00:00
“huailei000”
6938299940 perf: 会话详情添加文件传输;操作日志默认显示动作、资源类型 2024-01-18 08:10:58 +00:00
wangruidong
0d1eb82fca perf: The cron interval execution must be greater than 10 minutes 2024-01-18 08:06:14 +00:00
feng626
ddf268e8ec Merge pull request #3695 from jumpserver/revert-3692-pr@dev@login_expire
Revert "perf: 登录过期自动退出"
2024-01-17 21:11:10 +08:00
老广
ae6fb878da Revert "perf: 登录过期自动退出" 2024-01-17 21:07:33 +08:00
feng626
d7099c118b Merge pull request #3692 from jumpserver/pr@dev@login_expire
perf: 登录过期自动退出
2024-01-17 15:58:16 +08:00
feng
1b73591366 perf: 登录过期自动退出 2024-01-17 15:47:21 +08:00
ibuler
6f3f66df73 perf: auto decide create in new page or current page in asset or permission 2024-01-15 19:37:47 +08:00
ibuler
598020a89b perf: revert asset create with labels 2024-01-15 19:36:48 +08:00
“huailei000”
88486e2b00 perf: 优化时间区间选择组件选择时间不准确问题 2024-01-14 22:57:36 -08:00
“huailei000”
faf8521c91 perf: 优化标签列表-资产数量弹窗中的搜索条件不显示在url中 2024-01-14 22:29:42 -08:00
feng
ccd74fb76f fix: m2m 表单渲染错乱 2024-01-14 22:14:48 -08:00
ibuler
d76c6fdbd8 perf: 优化 tag search 避免多次请求 2024-01-14 18:29:12 -08:00
wangruidong
af6a55d3f4 feat: 同步ldap用户消息通知 2024-01-12 12:02:42 +05:00
“huailei000”
c052961efe perf: 优化仪表盘翻译 2024-01-11 14:43:24 +08:00
“huailei000”
57d339f513 perf: 优化批量操作图标大小 2024-01-11 14:43:01 +08:00
feng626
097771175d Merge pull request #3678 from jumpserver/pr@dev@dashboard_translate
perf: 前端dashboard 翻译
2024-01-09 17:49:43 +08:00
feng
0922557abc perf: 前端dashboard 翻译 2024-01-09 17:40:37 +08:00
feng626
6842da1960 Merge pull request #3676 from jumpserver/pr@dev@perm_create
perf: 【资产授权】选择资产弹窗左侧树中,去掉搜索、刷新按钮
2024-01-08 17:26:27 +08:00
feng
eb839b4113 perf: 【资产授权】选择资产弹窗左侧树中,去掉搜索、刷新按钮 2024-01-08 17:25:34 +08:00
“huailei000”
7e3e8fbf2f perf: 替换批量更新图标 2024-01-08 16:04:37 +08:00
jiangweidong
3800151763 perf: 邮箱支持exchange协议 2024-01-08 12:34:50 +05:00
ibuler
0121505f28 perf: 优化页面显示 2024-01-08 14:49:12 +08:00
ibuler
b9a6f5d3ac perf: 修复标签导入和搜索的问题 2024-01-03 17:07:15 +08:00
feng626
179b568b16 Merge pull request #3672 from jumpserver/pr@dev@history_account
feat: 历史账号定期删除 可设置保留数量
2024-01-03 11:04:22 +08:00
feng
c563697efd feat: 历史账号定期删除 可设置保留数量 2024-01-02 19:10:54 +08:00
fit2bot
fa9281aa92 perf: 优化节点树 (#3670)
Co-authored-by: ibuler <ibuler@qq.com>
2024-01-02 16:18:54 +08:00
feng
cc8d94f666 fix: Default组织下,标签关联用户资源,去掉组件用户 2023-12-29 11:14:26 +05:00
“huailei000”
1416405644 perf: 优化批量更新资产不能使用问题 2023-12-29 11:10:04 +05:00
wangruidong
d29b3effbc fix: 更新用户组权限问题 2023-12-29 07:41:28 +05:00
wangruidong
bd9456ba2d fix: 创建用户失败 2023-12-28 15:35:22 +05:00
wangruidong
0c9d5d9b6b fix: 用户角色修改后更新页面与用户列表页显示不一致 2023-12-28 15:35:22 +05:00
Bai
b19ddd6799 fix: 修复组件中没有暴露 SQLServer 端口的问题 2023-12-28 14:52:41 +05:00
wangruidong
ea48b6ebf3 perf: 修改文件上传超时时间 2023-12-27 18:57:17 +08:00
“huailei000”
fba2f77874 perf: 优化智能问答返回显示系统消息 2023-12-27 17:11:20 +08:00
wangruidong
3c900ce387 perf: 账号删除添加提示确认框 2023-12-27 14:07:15 +05:00
“huailei000”
7df11b907f perf: 优化资产详情页面布局 2023-12-27 15:27:43 +08:00
wangruidong
cdd51a9c16 perf: 统计任务执行结果 2023-12-26 15:45:08 +08:00
wangruidong
51a4c013d3 perf: 修改文件上传超时时间 2023-12-26 10:48:36 +08:00
feng
111aafb4bb fix: 【用户登录会话失效问题】SESSION_COOKIE_AGE 配置不生效的问题 2023-12-25 13:24:02 +05:00
“huailei000”
81f0b13730 perf: 优化网关克隆平台显示不正确问题 2023-12-25 15:10:10 +08:00
“huailei000”
a36a9e7645 perf: 智能问答禁止密码自动填充 2023-12-22 13:23:17 +05:00
Eric
cfe6db6ec5 perf: 虚拟应用增加许可证验证 2023-12-22 12:24:08 +05:00
“huailei000”
ccb221559a perf: 账号收集翻译 2023-12-22 11:33:43 +08:00
“huailei000”
ace8501648 perf: 实体组织下批量更新用户可以选择用户组 2023-12-21 17:29:38 +08:00
“huailei000”
ff627bb0db perf: 优化chat滚动条宽度 2023-12-21 16:19:59 +08:00
ibuler
dba8d84f2d perf: 优化导入 dialog 大小 2023-12-21 15:38:34 +08:00
wangruidong
e24bfff6ab fix: 同名文件可以删除任意一个 2023-12-21 15:38:15 +08:00
wangruidong
cdcc4226db perf: 界面设置提交后不刷新页面 2023-12-21 15:37:26 +08:00
“huailei000”
09b52738ca perf: 优化站内信布局;显示站内信关闭按钮 2023-12-21 11:02:42 +08:00
“huailei000”
e324b16e1b perf: 优化标签管理编辑按钮位置 2023-12-21 10:26:20 +08:00
fit2bot
1b52e4cb93 perf: 优化 label search (#3634)
Co-authored-by: ibuler <ibuler@qq.com>
2023-12-20 20:48:00 +08:00
“huailei000”
6ac31b05e5 perf: window资产密文类型不是密码时不能设置推送参数 2023-12-20 19:37:57 +08:00
wangruidong
6c24c9a2d9 fix: 账号备份开启拆分两部分后再关闭还是会拆成两份发送邮件 2023-12-20 16:25:17 +05:00
fit2bot
b79ef4f7a8 perf: 更新白名单后返回列表页 (#3630)
Co-authored-by: wangruidong <940853815@qq.com>
2023-12-20 19:18:04 +08:00
“huailei000”
d584f83f59 perf: 用户列表全局组织下可以批量更新用户,不能更新用户组 2023-12-20 19:00:00 +08:00
feng626
22ed0ec0ca Merge pull request #3629 from jumpserver/pr@dev@user
perf: 更新创建用户 权限位关联,用户角色 select2
2023-12-20 18:29:09 +08:00
feng
436e9e59f1 perf: 更新创建用户 权限位关联,用户角色 select2 2023-12-20 18:27:44 +08:00
wangruidong
ba63d52275 fix: 添加资产id查询条件 2023-12-20 15:14:45 +05:00
“huailei000”
aa48e24881 perf: 全局组织下禁止批量更新 2023-12-20 17:57:29 +08:00
“huailei000”
f59b33a27f perf: 优化标签组件内容为空时面板的位置 2023-12-20 17:38:57 +08:00
ibuler
9f87708b96 perf: 修改拼写错误 2023-12-20 17:02:57 +08:00
Eric
78819e9a04 perf: 页面配置是否启用 Vitual App 2023-12-20 13:19:49 +05:00
“huailei000”
8e981d52eb perf: 优化标签管理编辑 2023-12-20 15:52:33 +08:00
wangruidong
9f4c798ddb perf: 修改文件上传超时时间 2023-12-19 16:18:36 +05:00
ibuler
7eac62635b perf: 支持点击 label 搜索 2023-12-19 17:00:50 +08:00
ibuler
3739d710f8 perf: 标签支持多个搜索 2023-12-19 15:26:10 +08:00
“huailei000”
aae5aa9d7f perf: 标签列表关联成功资产后刷新列表 2023-12-19 14:54:19 +08:00
“huailei000”
88be5d5fe8 perf: 优化资产选择节点组件取消不能关闭弹窗问题 2023-12-19 13:57:37 +08:00
fit2bot
6ac54c9865 perf: 优化 labels list 显示 (#3617)
* perf: 修改 label list 显示

* perf: 优化 labels list 显示

---------

Co-authored-by: ibuler <ibuler@qq.com>
2023-12-18 22:38:35 +08:00
ibuler
5cefbbfb51 perf: 修改 labels 关联 2023-12-18 18:06:18 +08:00
ibuler
f10e31bd60 perf: 修复标签搜索 2023-12-15 18:32:28 +08:00
wangruidong
e8e8b5bfca perf: 文件传输提示优化 2023-12-15 18:12:19 +08:00
“huailei000”
19adbca7b3 perf:优化资产授权详情授权用户、授权资产点击多选框会全部选中问题 2023-12-15 15:11:05 +08:00
feng626
ba28d5619a Merge pull request #3612 from jumpserver/pr@dev@acl_rules
perf: acl 自定义规则添加首次登录选项
2023-12-15 14:16:26 +08:00
feng
68363a4c52 perf: acl 自定义规则添加首次登录选项 2023-12-15 14:13:43 +08:00
“huailei000”
f947fa8d36 perf: 优化新建聊天不重新创建socket 2023-12-15 11:03:45 +08:00
ibuler
c1d0994781 perf: 优化 chat css 2023-12-14 19:53:17 +08:00
fit2bot
fde576fe8b perf: 上传目标目录指定在/tmp下 (#3608)
Co-authored-by: wangruidong <940853815@qq.com>
2023-12-14 19:45:27 +08:00
“huailei000”
27864be41d perf: 优化chat初始化socket 2023-12-14 19:32:31 +08:00
ibuler
9d461be4a0 perf: 优化聊天样式 2023-12-14 19:12:03 +08:00
“huailei000”
bcbfe114d8 perf: 优化点击新聊天提示词没有重置问题 2023-12-14 17:37:13 +08:00
ibuler
af8448adeb merge: with dev 2023-12-14 16:59:46 +08:00
fit2bot
9b7a360ea1 perf: chat增加提示词 (#3603)
Co-authored-by: “huailei000” <2280131253@qq.com>
Co-authored-by: huailei <31801270+huailei000@users.noreply.github.com>
2023-12-14 16:37:48 +08:00
ibuler
3bf7eed52e perf: 修复资产详情中 label 的问题 2023-12-14 16:36:59 +08:00
ibuler
c1d80469db perf: 修改 prompt 2023-12-14 14:07:38 +08:00
jiangweidong
e6a1039387 feat: 云同步支持设置策略关系、支持不匹配不符合策略的资产 (#3592) 2023-12-14 11:31:23 +08:00
wangruidong
ad7b2c4e8f perf: 上传大小限制从接口获取 2023-12-14 11:27:56 +08:00
fit2bot
d51a787598 perf: 使用slot处理文件列表 (#3599)
* perf: 使用slot处理文件列表

* perf: 大小文件超出限制不上传

---------

Co-authored-by: wangruidong <940853815@qq.com>
2023-12-14 10:34:06 +08:00
ibuler
2ac9183047 perf: 修改 icon 颜色 2023-12-14 10:30:42 +08:00
ibuler
3b9f0b56fb perf: 优化右侧聊天 2023-12-13 18:08:42 +08:00
“huailei000”
d900a8d5a3 perf: 已锁定的ip按钮增加个数显示 2023-12-13 17:16:16 +08:00
fit2bot
6daa16b3ca perf: add strip-ansi (#3596)
* perf: 修改 requrements

* perf: add strip-ansi

---------

Co-authored-by: ibuler <ibuler@qq.com>
2023-12-13 16:38:45 +08:00
“huailei000”
28891234db perf: 优化socket地址 2023-12-13 16:34:24 +08:00
ibuler
1d7bdcb512 perf: 修改 requrements 2023-12-13 16:31:41 +08:00
“huailei000”
0964031922 perf: 优化chat 停止按钮位置 2023-12-13 16:14:15 +08:00
ibuler
6568f47760 perf: 优化文件上传的样式 2023-12-13 11:24:04 +08:00
“huailei000”
448e89c733 perf: update yarn.lock 2023-12-13 11:20:11 +08:00
“huailei000”
68dccaa93c perf: 优化抽屉按钮效果 2023-12-13 10:54:50 +08:00
“huailei000”
b0cefb05e0 perf: 优化chat提示 2023-12-12 19:48:35 +08:00
wangruidong
d6bf4c86df perf: 显示上传文件大小 2023-12-12 19:07:02 +08:00
feng626
993ded5ca8 Merge pull request #3586 from jumpserver/pr@dev@perf_chat
perf: chat
2023-12-12 18:52:19 +08:00
huailei
951df9e63a Merge branch 'dev' into pr@dev@perf_chat 2023-12-12 18:51:43 +08:00
“huailei000”
4b98806b3e perf: chat 2023-12-12 18:50:19 +08:00
ibuler
536520c78c perf: 优化英语翻译 2023-12-12 18:43:51 +08:00
ibuler
63650912b8 perf: placeholder 字体大小 2023-12-12 18:43:31 +08:00
feng626
b65069adeb Merge pull request #3583 from jumpserver/pr@dev@translate
perf: 翻译
2023-12-12 16:35:00 +08:00
feng
7bd3179488 perf: 翻译 2023-12-12 16:33:16 +08:00
feng
bf6ecca1fa perf: 翻译 2023-12-12 16:32:54 +08:00
ibuler
69449740df perf: 修改一下大小 2023-12-12 14:16:35 +08:00
feng626
591f3ae39a Merge pull request #3579 from jumpserver/pr@dev@system_task
perf: 【优化系统任务】支持显示 执行周期、下次开始时间 字段
2023-12-12 14:16:12 +08:00
ibuler
463d2b16bf perf: chatai icon 2023-12-12 11:13:15 +08:00
feng
cc47d93988 perf: 【优化系统任务】支持显示 执行周期、下次开始时间 字段 2023-12-12 11:06:53 +08:00
fit2bot
19e42c48e4 feat: 支持批量发送文件 (#3574)
* feat: 支持批量发送文件

* feat: 支持批量发送文件

* perf: 同名文件处理

---------

Co-authored-by: wangruidong <940853815@qq.com>
2023-12-12 10:40:41 +08:00
“huailei000”
6b37c71b6d feat: 添加chat聊天 2023-12-11 18:24:24 +08:00
feng
3af6b1d1fe feat: 系统设置配置gpt 2023-12-11 18:24:24 +08:00
fit2bot
2445ecb6e5 perf: 优化 select2 成 transfer select2 (#3576)
* feat: 添加 tranSelect

* perf: 优化 select2 成 transfer select2

---------

Co-authored-by: ibuler <ibuler@qq.com>
2023-12-11 16:14:06 +08:00
feng626
a5ae4110da Merge pull request #3578 from jumpserver/pr@dev@applet
perf: 远程应用上传过程中,如果应用不存在,则创建该应用;如果已存在,则进行应用更新。
2023-12-11 14:29:11 +08:00
feng
dc4378b637 perf: 远程应用上传过程中,如果应用不存在,则创建该应用;如果已存在,则进行应用更新。 2023-12-11 14:26:43 +08:00
Eric
91f2de7797 perf: 添加 panda 监控显示 2023-12-11 14:14:39 +08:00
Eric
29b2829199 perf: 完善 app provider 详情页 2023-12-08 18:48:15 +08:00
feng626
4e23a34af9 Merge pull request #3572 from jumpserver/pr@dev@delete_account_automation
feat: 同步删除远程机器账号
2023-12-08 14:14:28 +08:00
feng
2bd1aaa03e feat: 同步删除远程机器账号 2023-12-07 21:07:29 +08:00
halo
96e23ddb52 fix: 修复字段翻译问题 2023-12-07 14:10:37 +08:00
fit2bot
351e250688 feat: support virtual app (#3558)
* feat: support virtual app

* perf: virtual host 详情

* perf: 调整 virtual app 卡片展示

* perf: 调整 route

* perf: 增加翻译

* perf: 优化更换名称

---------

Co-authored-by: Eric <xplzv@126.com>
2023-12-06 10:38:04 +08:00
liufei20151013
e8ebd1aa64 feat: 云同步支持 ZStack 2023-12-05 17:35:41 +08:00
jiangweidong
6adca44180 perf: 用户列表可展示更多api返回的字段信息 2023-12-05 17:28:33 +08:00
ibuler
f3699069c5 perf: 优化创建 label 2023-12-05 15:15:55 +08:00
fit2bot
2f298a32c3 perf: 修改标签 (#3516)
* perf: 支持全局的 labels

* perf: 修改标签

* perf: 修改 labes

* stash

* pref: stash it

* perf: stash

* perf: 优化 labels

* perf: 优化 tabs

* perf: 优化 tag

* perf: 基本完成

* perf: 优化 labels 创建搜索

* perf: 优化 labels

* perf: 优化完成标签功能

* perf: 修改 label name

---------

Co-authored-by: ibuler <ibuler@qq.com>
2023-12-05 10:30:56 +08:00
fit2bot
3ba81a43cb feat: 资产详情页面添加历史执行命令列表页面 (#3568)
* feat: 资产详情页面添加历史执行命令列表页面

---------

Co-authored-by: wangruidong <940853815@qq.com>
2023-12-04 15:20:57 +08:00
fit2bot
813f17b52f perf: 优化工单处理提示消息页面 (#3567)
Co-authored-by: wangruidong <940853815@qq.com>
2023-11-30 11:40:04 +08:00
jiangweidong
0fc292b1ad perf: 支持slack通知和认证 (#3556) 2023-11-29 17:50:09 +08:00
fit2bot
f3f5bbe114 feat: 用户详情展示所有会话 (#3563)
Co-authored-by: wangruidong <940853815@qq.com>
2023-11-28 19:07:08 +08:00
feng626
8c732f00e1 Merge pull request #3566 from jumpserver/pr@dev@random
perf: 随机密码生成规则添加可排除字符选项
2023-11-28 14:48:41 +08:00
feng
f91b8c75df perf: 随机密码生成规则添加可排除字符选项 2023-11-28 14:45:58 +08:00
jiangweidong
e86b88bf05 feat: 支持深信服云平台和阿里云专有云 2023-11-27 11:12:14 +08:00
“huailei000”
bf3a2d748d perf: 限制上传logo图片的格式;优化打开文件速度 2023-11-24 18:59:46 +08:00
wangruidong
8a4b81e2e5 feat: 支持备案配置 2023-11-24 13:56:28 +08:00
wangruidong
34406ec32d fix: SFTP对象存储,禁用设为默认存储 2023-11-24 10:32:56 +08:00
feng626
3269a2a3ff Merge pull request #3554 from jumpserver/pr@dev@perf_ldap_user_websocket
perf: ldap接口请求换成websocket连接
2023-11-23 15:05:32 +08:00
feng626
5d50844753 Merge pull request #3555 from jumpserver/pr@dev@add_all_member
perf: 给用户组添加全部成员时,二次确认下
2023-11-22 19:06:04 +08:00
feng
97cc73a4fa perf: 给用户组添加全部成员时,二次确认下 2023-11-22 19:04:56 +08:00
wangruidong
574dae6236 perf: ldap接口请求换成websocket连接 2023-11-22 16:40:10 +08:00
“huailei000”
84c413f51a perf: 优化日志审计默认筛选截止时间多一天 2023-11-22 15:03:56 +08:00
“huailei000”
87a1cce4ca perf: 优化table列表溢出提示没有复制成功问题 2023-11-22 11:01:46 +08:00
吴小白
be89b5d6f2 Merge pull request #3547 from jumpserver/pr@dev@fix_version
fix: 修正 actions version 获取错误
2023-11-17 09:42:52 +08:00
吴小白
200c727426 fix: 修正 actions version 获取错误 2023-11-17 08:20:27 +08:00
wangruidong
d563f66ba1 perf: 修改翻译 2023-11-16 16:21:58 +08:00
feng626
4592eeddd3 Merge pull request #3544 from jumpserver/pr@dev@user_session
perf: 在线会话添加活跃状态过滤
2023-11-16 14:41:50 +08:00
feng
d73a07dff2 perf: 在线会话添加活跃状态过滤 2023-11-16 14:40:16 +08:00
“huailei000”
e8857bf150 perf: ldap导入用户不显示操作栏 2023-11-16 10:48:15 +08:00
wangruidong
0abe33053f perf: 修改翻译 2023-11-15 19:33:51 +08:00
“huailei000”
dc9161c6a7 perf:优化el-data-table组件分页多选记录不准确问题 2023-11-15 19:03:56 +08:00
老广
d588be696b Merge pull request #3540 from jumpserver/pr@dev@perf_winrm_private
perf: 修改 winrm 用户不能连接
2023-11-15 15:40:03 +08:00
ibuler
4b2c806f28 perf: 修改 winrm 用户不能连接 2023-11-15 15:39:15 +08:00
老广
e28b026ead Merge pull request #3539 from jumpserver/pr@dev@fix_select_options_error
fix: 对象存储下拉无法自动加载
2023-11-15 14:58:55 +08:00
wangruidong
c7414e0199 fix: 对象存储下拉无法自动加载 2023-11-15 14:51:38 +08:00
“huailei000”
5a478ebaba perf: 优化用户详情字段不显示问题 2023-11-15 12:17:40 +08:00
老广
08577f0ae5 Merge pull request #3536 from jumpserver/pr@dev@perf_ticket_item_error
perf: 优化工单详情页面渲染卡顿问题
2023-11-15 11:43:16 +08:00
“huailei000”
38527c6a41 perf: 优化工单详情页面渲染卡顿问题 2023-11-15 11:41:23 +08:00
“huailei000”
01c5b7c4a8 perf: 优化登录复核页面卡顿不显示问题 2023-11-15 10:07:01 +08:00
wangruidong
b0b3e0d1c9 fix: 更新组件批量更新去掉sftp选项 2023-11-14 19:25:44 +08:00
jiangweidong
5bc273d057 fix: 云同步任务详情界面删除无用的按钮 2023-11-14 19:23:27 +08:00
wangruidong
e128576763 fix: 作业日志筛选用户出错 2023-11-14 19:22:58 +08:00
“huailei000”
58f4dca599 perf: 优化 TagInput 组件失去焦点后没有自动填充值问题 2023-11-14 17:29:21 +08:00
老广
a6dd7f23c7 Merge pull request #3530 from jumpserver/pr@dev@fix_user_confirm_dialog
perf: 优化用户确认窗口
2023-11-14 14:23:22 +08:00
ibuler
a267d46464 perf: 优化用户确认窗口 2023-11-14 14:19:33 +08:00
“huailei000”
8c829ee498 perf: 优化markdown组件判断 2023-11-14 14:02:49 +08:00
ibuler
bcfa95de06 perf: 修改用户确认 2023-11-14 13:44:54 +08:00
“huailei000”
56745ae944 perf: 优化个人信息详情字段信息显示 2023-11-14 11:35:04 +08:00
wangruidong
5a664f8c2c fix: 账号备份详情页显示sftp服务器信息 2023-11-13 17:06:18 +08:00
ibuler
1fd29e13f8 perf: 修改 reload 2023-11-13 14:55:27 +08:00
ibuler
af863dae75 perf: 修改 table reload 2023-11-13 14:44:38 +08:00
“huailei000”
b1254d2b87 perf: 优化markdown组件样式 2023-11-10 18:25:58 +08:00
“huailei000”
d350cadd4c perf: 优化资产账号模版反复勾选同一个账号会出现无法选中的问题 2023-11-10 16:10:31 +08:00
“huailei000”
cfe9ebd747 perf: 优化创建账号-指定账号组件重复选择问题;优化创建工单-制定账号重复选择问题 2023-11-10 11:16:56 +08:00
“huailei000”
03177d4ce9 perf: 调整公告背景颜色 2023-11-09 19:22:38 +08:00
ibuler
a55b0da22b perf: 优化用户确认 2023-11-09 18:48:42 +08:00
feng626
1b0bdf25ae Merge pull request #3514 from jumpserver/pr@dev@online_session_active
perf: 在线用户根据websocket添加用户是否活跃状态
2023-11-09 18:15:08 +08:00
“huailei000”
c746cdc639 perf: 平台列表不显示更多操作 2023-11-09 18:08:29 +08:00
“huailei000”
3cf6b6f639 perf: 优化保存快捷命令时,命令不能为空 2023-11-09 18:08:02 +08:00
feng
a1bf8f9ab7 perf: 在线用户根据websocket添加用户是否活跃状态 2023-11-08 16:51:16 +08:00
“huailei000”
5449d74d53 perf: 优化select2组件远程搜索结果为空时是否开启远程搜索状态没有动态更新问题;创建工单搜索资产时不显示问题 2023-11-08 10:21:15 +08:00
jiangweidong
dc401f80b9 fix: 解决更新云同步任务时,优先级无法更新问题 2023-11-07 17:18:45 +08:00
wangruidong
379cd2386a perf: 账号备份增加sftp方式 2023-11-07 15:37:04 +08:00
wangruidong
4e7bdb9c69 perf: 对象存储增加sftp 2023-11-07 15:37:04 +08:00
feng626
41449fb538 Merge pull request #3485 from jumpserver/pr@dev@gather_account
feat: 账号收集添加资产账号信息变化通知
2023-11-07 13:00:45 +08:00
feng626
a75488217c Merge pull request #3495 from jumpserver/pr@dev@su_from
fix: 账号 切换自 翻译修复
2023-11-07 11:09:18 +08:00
feng
8e32792696 fix: 账号 切换自 翻译修复 2023-11-07 11:08:28 +08:00
feng626
b47caf0287 Merge pull request #3493 from jumpserver/pr@dev@account_change_push
fix: 改密 推送 详情里执行任务的权限不对
2023-11-06 18:05:39 +08:00
feng
715ae856f0 fix: 改密 推送 详情里执行任务的权限不对 2023-11-06 18:01:39 +08:00
“huailei000”
19ae27e6c2 perf: ztree父节点是选中状态,点击展开后子节点默认显示选中状态 2023-11-06 16:22:11 +08:00
halo
2132bacff5 feat: 工作台支持配置显示系统工具 2023-11-03 17:34:14 +08:00
老广
e57c5a20d0 Merge pull request #3487 from jumpserver/pr@dev@perf_add_ip_group_access
perf: 更新字段修改
2023-11-02 15:39:48 +08:00
老广
3d35f0aafe Merge pull request #3488 from jumpserver/pr@dev@perf_tag_input
perf: 优化 tab input
2023-11-02 15:38:52 +08:00
ibuler
68ac03db9e perf: 优化 tab input 2023-11-02 15:33:30 +08:00
wangruidong
7b5471a451 perf: 更新字段修改 2023-11-02 15:16:58 +08:00
ibuler
7f052ac85e perf: 优化 tag input 2023-11-02 11:07:20 +08:00
feng
64e75eb9ff feat: 账号收集添加资产账号信息变化通知 2023-11-02 10:50:18 +08:00
ibuler
8e75e5d5e3 perf: 优化 json field m2m 2023-11-02 10:36:00 +08:00
“huailei000”
1810e6833c perf: 公告支持markdown预览 2023-11-01 01:19:09 -05:00
wangruidong
8c7c012785 perf: 添加访问IP控制 2023-10-31 02:45:03 -05:00
feng626
378d82518a Merge pull request #3479 from jumpserver/pr@dev@change_secret_records
feat: 改密记录 推送记录可单独执行
2023-10-31 14:10:53 +08:00
wangruidong
728b04c8e3 perf: 修改语言位置 2023-10-30 21:54:40 -05:00
老广
b8611f095a Merge pull request #3478 from jumpserver/pr@dev@feat_apple_host_support_same_account
feat: 发布机支持使用同名账号连接
2023-10-31 10:19:30 +08:00
feng
f49a1184e2 feat: 改密记录 推送记录可单独执行 2023-10-30 18:42:14 +08:00
dependabot[bot]
77a4441018 build(deps): bump browserify-sign from 4.2.1 to 4.2.2
Bumps [browserify-sign](https://github.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.2.
- [Changelog](https://github.com/browserify/browserify-sign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.2)

---
updated-dependencies:
- dependency-name: browserify-sign
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-30 14:30:10 +08:00
ibuler
18f1f0de79 feat: 发布机支持使用同名账号连接 2023-10-30 11:37:22 +08:00
dependabot[bot]
d252c7dd08 build(deps): bump @babel/traverse from 7.20.1 to 7.23.2
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.20.1 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-30 10:38:38 +08:00
dependabot[bot]
e5547f8a4c build(deps): bump crypto-js from 4.1.1 to 4.2.0
Bumps [crypto-js](https://github.com/brix/crypto-js) from 4.1.1 to 4.2.0.
- [Commits](https://github.com/brix/crypto-js/compare/4.1.1...4.2.0)

---
updated-dependencies:
- dependency-name: crypto-js
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-30 10:37:50 +08:00
jiangweidong
63d6578991 fix: 云账号详情页面更新报错 2023-10-30 10:30:12 +08:00
ibuler
da00ae84a8 perf: 资产授权添加协议 2023-10-30 10:14:01 +08:00
ibuler
74a905ee85 perf: 资产授权添加协议 2023-10-30 10:14:01 +08:00
“huailei000”
a03e985df3 perf: table tooltip 修改鼠标移上图标显示 2023-10-25 16:53:58 +08:00
吴小白
2d6005a4e0 perf: 优化构建 2023-10-25 16:38:41 +08:00
“huailei000”
c4ca28d2d9 perf: 优化table组件内容溢出省略时,鼠标可以移入tooltip 2023-10-25 16:37:59 +08:00
wangruidong
3934c17f52 perf: 增加语言切换功能 2023-10-25 03:03:17 -05:00
“huailei000”
51717f8583 perf: 优化创建api key后关闭弹窗列表不刷新问题 2023-10-25 10:47:17 +08:00
“huailei000”
67e99702c1 perf: 远程应用名称不能包含() 2023-10-24 19:30:50 +08:00
feng626
27ce5a3785 Merge pull request #3462 from jumpserver/pr@dev@account_change_secret
fix: 修改账号改密执行权限
2023-10-24 10:51:35 +08:00
feng
49122bb213 fix: 修改账号改密执行权限 2023-10-24 10:49:41 +08:00
halo
263c4d3f89 fix: 修复权限树自动选择的问题 2023-10-23 04:29:49 -05:00
wangruidong
2ed203dc32 perf: 添加作业中心执行历史配置字段 2023-10-23 04:23:15 -05:00
“huailei000”
742a06ea2d perf: 优化LDAP用户列表导入全部用户失败后没有提示信息问题 2023-10-20 16:15:20 +08:00
“huailei000”
1ad4e2c62e perf: 调整账号列表页面默认激活的tab 2023-10-19 19:15:24 +08:00
jiangweidong
7e29a3e836 fix: 新创建的云同步任务策略为空的问题 2023-10-19 03:57:31 -05:00
“huailei000”
3a66e8323c perf:优化折线图显示不准确问题 2023-10-19 14:38:03 +08:00
feng626
4852e3d26a Merge pull request #3446 from jumpserver/pr@dev@profile
perf: 修改profile icon
2023-10-19 10:44:23 +08:00
feng
80509dc15f perf: 修改profile icon 2023-10-19 10:43:32 +08:00
“huailei000”
601bd4740c perf: 优化账号推送,推送参数不准确问题 2023-10-18 05:53:42 -05:00
老广
62be5885db Merge pull request #3444 from jumpserver/pr@dev@perf_date_expired
perf: 优化日期,去掉 nwaring
2023-10-18 05:53:02 -05:00
ibuler
62e49567cc perf: 优化日期,去掉 nwaring 2023-10-18 18:51:46 +08:00
feng626
f2eff11d66 Merge pull request #3443 from jumpserver/pr@dev@command_filter_acl
fix: 命令过滤,点击命令组跳转到了规则基本信息,而不是命令组那里
2023-10-18 18:46:28 +08:00
feng
ae61586f95 fix: 命令过滤,点击命令组跳转到了规则基本信息,而不是命令组那里 2023-10-18 18:43:46 +08:00
“huailei000”
a08fbc3b77 perf: 优化table组件在有查询条件的情况下列表会重复请求接口的问题 2023-10-18 04:31:22 -05:00
ibuler
53d130f1cf perf: 优化个人设置 2023-10-18 04:30:39 -05:00
ibuler
c05992ce50 perf: 修改许可证 2023-10-18 04:30:18 -05:00
feng626
52522b7095 Merge pull request #3441 from jumpserver/pr@dev@session_ticket
perf: 资产登录工单 去掉session信息
2023-10-18 17:07:34 +08:00
feng
af3f6c5900 perf: 资产登录工单 去掉session信息 2023-10-18 17:06:46 +08:00
ibuler
7b15ca4955 perf: 优化角色创建时权限没有提示 2023-10-17 02:23:35 -05:00
“huailei000”
031016330a perf: 优化查看账号密码控制台报错问题 2023-10-16 15:03:00 +08:00
老广
e274640d2e Merge pull request #3433 from jumpserver/pr@dev@refactor_confirm
perf: 优化确认操作
2023-10-13 17:27:27 +08:00
ibuler
bd3352424c perf: 优化用户确认 2023-10-13 17:24:45 +08:00
ibuler
f8ec2bce0c merge: with dev 2023-10-13 17:06:01 +08:00
ibuler
81f34f0154 perf: remove debug 2023-10-13 17:01:38 +08:00
ibuler
759e205bfb perf: 优化确认操作 2023-10-13 16:55:21 +08:00
halo
fc5029e88a feat: 增加DB2图标 2023-10-13 02:59:35 -05:00
Bai
6fa8052878 fix: 修复连接令牌过期按钮被禁用的问题 2023-10-13 01:36:27 -05:00
feng626
0dc62712d4 Merge pull request #3431 from jumpserver/pr@dev@ticket
perf: 工单配置社区版隐藏
2023-10-13 11:18:43 +08:00
feng
23b08590cf perf: 工单配置社区版隐藏 2023-10-13 11:05:23 +08:00
feng626
8d8ab483e1 Merge pull request #3430 from jumpserver/pr@dev@platform
perf: 资产批量更新平台字段,根据平台约束协议自动生效
2023-10-12 18:11:37 +08:00
feng
fb757686e3 perf: 资产批量更新平台字段,根据平台约束协议自动生效 2023-10-12 16:56:43 +08:00
“huailei000”
6a8161dcaf perf:创建api key,弹窗关闭后刷新列表 2023-10-12 15:51:09 +08:00
ibuler
ae2391f07f perf: 优化数据库必填 2023-10-12 14:50:30 +08:00
ibuler
50a9ce35ad perf: 重构用户确认 2023-10-12 13:57:41 +08:00
“huailei000”
2d2a4be3a2 perf: 优化账号收集、账号改密、账号备份多次点击执行次数不能正常跳转到执行列表问题 2023-10-11 21:58:25 -05:00
Bai
e055429ff2 perf: 优化用户详情 phone 显示 2023-10-11 04:48:54 -05:00
“huailei000”
8727bac560 perf: 登录资产复核增加跳转会话入口 2023-10-11 04:26:04 -05:00
ibuler
fd018dc5ac perf: 仅本地用户允许使用 passkey 2023-10-11 04:24:38 -05:00
“huailei000”
4c3673aef2 perf: 优化账号模版同步更新账号信息 2023-10-11 04:23:29 -05:00
feng626
4eb61373e0 Merge pull request #3423 from jumpserver/pr@dev@profile
perf: 左上角profile accesskey 改成个人设置
2023-10-11 14:21:34 +08:00
feng
8e8dd38e2e perf: 左上角profile accesskey 改成个人设置 2023-10-11 14:21:00 +08:00
jiangweidong
f8b7720e2c feat: 支持自定义短信认证(文件) 2023-10-10 22:19:57 -05:00
ibuler
50af6fe017 perf: 优化确认的 dialog 2023-10-10 18:24:14 +08:00
feng626
8bc617c4a7 Merge pull request #3421 from jumpserver/pr@dev@user_notice
perf: 用户首次登录 条款和条件 提示优化
2023-10-10 18:23:47 +08:00
feng
f75d69601b perf: 用户首次登录 条款和条件 提示优化 2023-10-10 18:22:35 +08:00
Aaron3S
f54d819ec8 feat: 快捷命令增加设置执行目录 2023-10-10 07:49:57 +05:00
Eric
c50db4089c perf: 隐藏设置 terminal_theme_name 2023-10-10 07:42:12 +05:00
feng626
a6d7cc1215 Merge pull request #3416 from jumpserver/pr@dev@asset_notice
feat: 登录资产消息提示
2023-10-07 17:46:39 +08:00
feng
f6a2fcbbea feat: 登录资产消息提示 2023-10-07 17:44:27 +08:00
feng
071822e665 fix: 系统设置 邮件设置 不能设置后缀 2023-09-27 13:17:52 +05:00
“huailei000”
063dc9f8e1 perf: 优化资产授权详情、账号改密详情中已经添加了的资产没有给出标记还能重复选择的问题 2023-09-27 13:16:39 +05:00
“huailei000”
66e90f189c perf: 列表搜索框退格键可以删除搜索条件 2023-09-26 15:11:56 +08:00
feng626
bdd1a86568 Merge pull request #3410 from jumpserver/pr@dev@user_login_reminder
feat: 用户登录提醒
2023-09-25 16:26:10 +08:00
feng
01d0c9a0c2 feat: 用户登录提醒 2023-09-25 16:24:56 +08:00
“huailei000”
d42bd25371 perf: 修复平台列表导出模版错误问题 2023-09-21 16:18:29 +08:00
“huailei000”
2d07b10961 perf: 调整仪表盘翻译 2023-09-21 15:30:17 +08:00
ibuler
77fb9ef528 perf: 优化 passkey 报错 2023-09-21 12:23:50 +08:00
“huailei000”
b5950b795b fix: 修复组织角色添加用户报错问题 2023-09-20 18:42:08 +08:00
fit2bot
097817e02c perf: 修改模版平台参数无法设置 (#3402)
* perf: 修改模版平台参数无法设置

* perf: remove debug

---------

Co-authored-by: ibuler <ibuler@qq.com>
2023-09-20 14:09:31 +08:00
“huailei000”
cf7a77ce2e perf: 优化账号推送参数设置弹窗不能打开问题 2023-09-20 11:32:48 +08:00
feng626
e72850bff8 Merge pull request #3400 from jumpserver/pr@dev@user_setting
perf: 用户个人设置加上密钥是否设置
2023-09-19 16:30:31 +08:00
feng
4669cbfc83 perf: 用户个人设置加上密钥是否设置 2023-09-19 16:29:58 +08:00
Bai
cf8ad2a581 perf: 优化 es host help_text 提示 # 不支持 # 字符 2023-09-19 15:31:38 +08:00
“huailei000”
b776d0157b perf: 优化工单详情信息显示 2023-09-19 15:28:39 +08:00
“huailei000”
9aaee957af perf: 资产授权列表-指定账号字段不计算到账号个数里 2023-09-19 14:24:35 +08:00
老广
0bc681ea08 Merge pull request #3396 from jumpserver/pr@dev@perf_account_template_auto_push
perf: 修复自动推送按钮在不是 password 和 ssh_key 时禁用
2023-09-19 11:30:04 +08:00
ibuler
ea89ce1796 perf: 修复自动推送按钮在不是 password 和 ssh_key 时禁用 2023-09-19 11:17:22 +08:00
老广
e8a37a9c5b Merge pull request #3395 from jumpserver/pr@dev@perf_change_accont_template_password_rules
perf: 修改密码规则
2023-09-19 11:00:42 +08:00
ibuler
1350573ee2 perf: 修改翻译 2023-09-19 10:58:47 +08:00
ibuler
ba83bb14f3 perf: 修改密码规则 2023-09-18 19:33:47 +08:00
“huailei000”
7ae5adf49c perf: 在资产列表的资产树选择完节点后,创建资产能自动选择节点 2023-09-18 14:31:26 +08:00
ibuler
85a1385b4b perf: 优化创建用户默认角色 2023-09-18 14:02:03 +08:00
feng626
4da8eb12dc Merge pull request #3392 from jumpserver/pr@dev@push_now
perf: windows 账号 密钥类型为ssh 隐藏自动推送
2023-09-17 18:26:43 +08:00
feng
bf1be51c39 perf: windows 账号 密钥类型为ssh 隐藏自动推送 2023-09-17 18:25:33 +08:00
feng
3bbe9eccc1 perf: 修改usersession api 2023-09-15 17:25:41 +08:00
Bai
65e84b9b41 fix: 修改资产授权默认过期时间为 70 years 2023-09-15 14:27:40 +08:00
ibuler
0619900fd7 perf: 修改短信 sms 2023-09-15 14:26:49 +08:00
feng626
9ecc759dac Merge pull request #3390 from jumpserver/pr@dev@user_session
perf: 在线用户列表默认显示 认证方式
2023-09-15 10:50:24 +08:00
feng
946787e876 perf: 在线用户列表默认显示 认证方式 2023-09-15 10:49:28 +08:00
feng
54c30fcc0d feat: 用户在线session 控制 2023-09-14 16:25:51 +08:00
Aaron3S
83d730cf0f feat: 支持 ansible raw 模块 2023-09-13 19:30:10 +08:00
“huailei000”
7f7173432d perf: 优化crontab,周日的值修改为0 2023-09-13 19:26:56 +08:00
Bai
c1e5f1c1ce fix: 修复角色详情用户Name显示问题 2023-09-12 15:53:35 +08:00
fit2bot
ca3a99a5cf perf: 修改 passkey (#3382)
* perf: 修改 passkey

* perf: 完成 passkey

* perf: 修改 passkey icon

---------

Co-authored-by: ibuler <ibuler@qq.com>
2023-09-11 18:34:44 +08:00
feng626
95831c3ff7 Merge pull request #3383 from jumpserver/pr@dev@tranlate
perf: 翻译
2023-09-11 15:31:49 +08:00
feng
b5abf3e6ad perf: 翻译 2023-09-11 15:31:05 +08:00
feng626
9e4f519fc5 Merge pull request #3381 from jumpserver/pr@dev@usersetting
feat: 个人设置
2023-09-11 14:37:52 +08:00
feng
b712ec4183 feat: 个人设置 2023-09-10 15:44:05 +08:00
jiangweidong
35936ad01e feat: 系统工具支持traceroute (#3379) 2023-09-06 10:31:21 +08:00
ibuler
de5aed0f58 perf: 修改 protocol 说明 2023-09-04 13:50:26 +08:00
老广
5cb8cec835 Merge pull request #3376 from jumpserver/pr@dev@perf_session_list
perf: 优化会话列表
2023-08-31 17:11:50 +08:00
feng626
d66b2a8a87 Merge pull request #3362 from jumpserver/pr@dev@unlockip
feat: 可查看全局被限制的ip 并且可以解锁
2023-08-30 17:31:37 +08:00
feng
d06637afd4 feat: 可查看全局被限制的ip 并且可以解锁 2023-08-30 17:30:53 +08:00
“huailei000”
b27d88859f perf: 优化系统设置-邮件设置提交后页面空白问题 2023-08-30 16:40:10 +08:00
feng626
61580e096b Merge pull request #3377 from jumpserver/pr@dev@mysql_ssl
feat: mysql 证书
2023-08-30 15:16:05 +08:00
feng
da5d67ccbe feat: mysql 证书 2023-08-30 15:14:01 +08:00
ibuler
85a88616b0 perf: 优化会话列表 2023-08-30 14:34:16 +08:00
老广
3d3dfcafd3 Merge pull request #3374 from jumpserver/pr@dev@perf_route_children
perf: 修改路由 children active menu
2023-08-29 19:20:17 +08:00
ibuler
d51e025f04 perf: 修改路由 children active menu 2023-08-29 19:17:57 +08:00
老广
e78d201bd5 Merge pull request #3373 from jumpserver/pr@dev@feat_settings_tool_ping_telnet_multi
feat: telnet、ping支持批量测试,增加IP格式提示
2023-08-29 19:06:20 +08:00
老广
e1836eb1c6 Merge pull request #3372 from jumpserver/pr@dev@perf_change_some_helptext
perf: 修改云同步说明文案
2023-08-29 18:23:37 +08:00
ibuler
085447255b perf: 修改一些提示文案 2023-08-29 18:22:39 +08:00
jiangweidong
e3da28221e feat: telnet、ping支持批量测试,增加IP格式提示 2023-08-29 17:50:26 +08:00
ibuler
b276bbad34 perf: 修改云同步说明文案 2023-08-29 17:39:53 +08:00
老广
f534b292ce Merge pull request #3371 from jumpserver/pr@dev@perf_cloud_sync
perf: 优化同步
2023-08-29 16:25:13 +08:00
ibuler
a02e5363e0 perf: 修改云同步 2023-08-29 16:18:39 +08:00
ibuler
a0dbb3d7b0 perf: 优化同步 2023-08-29 16:14:35 +08:00
老广
b6e955f4b7 Merge pull request #3367 from jumpserver/pr@dev@perf_cloud_sync_None_err
fix: 策略删除动作和规则时提示404
2023-08-29 11:26:38 +08:00
老广
902662681c Merge pull request #3370 from jumpserver/pr@dev@perf_applet_host_tip
perf: 修改发布机的说明
2023-08-29 11:25:07 +08:00
ibuler
0196d42ffc perf: 修改发布机的说明 2023-08-29 11:24:09 +08:00
“huailei000”
a0a7590769 perf: 优化资产授权用户、资产选择之后下拉菜单选项还能继续选择问题 2023-08-29 10:23:08 +08:00
老广
b18c045a1b Merge pull request #3369 from jumpserver/pr@dev@perf_template
perf: 修改账号模版
2023-08-28 15:44:10 +08:00
ibuler
e0411010d1 perf: 修改账号模版 2023-08-28 15:19:20 +08:00
jiangweidong
757c48ebc8 fix: 策略删除时提示404 2023-08-25 14:02:23 +08:00
老广
38ea835df5 Merge pull request #3361 from jumpserver/pr@dev@perf_change_icon
perf: 修改一些 icon
2023-08-25 10:26:09 +08:00
老广
869b94d365 Merge pull request #3363 from jumpserver/pr@dev@perf_announcement_markdown
perf: 公告支持markdown语法
2023-08-25 10:25:42 +08:00
老广
25a8f43345 Merge pull request #3364 from jumpserver/pr@dev@fix_dynamic_input_glint_quit_problem
fix: 解决自动化动态列表组件添加内容会频闪退闪的问题
2023-08-25 10:25:09 +08:00
jiangweidong
7f2cb97bd7 perf: 去掉多余的内容 2023-08-25 10:06:28 +08:00
jiangweidong
580c005bc6 fix: 解决自动化动态列表组件添加内容会频闪闪退的问题 2023-08-25 10:02:51 +08:00
“huailei000”
e22d46bb38 perf: 公告支持markdown语法 2023-08-23 19:16:36 +08:00
ibuler
8d7dd81ebd perf: 修改一些 icon 2023-08-21 15:13:00 +08:00
老广
d5aa439fee Merge pull request #3359 from jumpserver/pr@dev@add_tips
perf: 添加 DOMAINS 不设置 报错的提示
2023-08-18 15:02:55 +08:00
ibuler
095492e5a3 perf: 添加 DOMAINS 不设置 报错的提示 2023-08-18 07:02:09 +00:00
feng
a4e2cadedd perf: vault 为企业版 2023-08-17 16:18:25 +05:00
feng626
29b35ff2a9 Merge pull request #3354 from jumpserver/pr@dev@vault
perf: 优化功能设置顺序
2023-08-17 16:21:55 +08:00
feng
4b137f855e perf: 优化功能设置顺序 2023-08-17 16:18:58 +08:00
feng626
d836b46966 Merge pull request #3353 from jumpserver/pr@dev@label
fix: 标签跳转问题
2023-08-17 15:13:30 +08:00
feng
f9d7d68c77 fix: 标签跳转问题 2023-08-17 15:12:43 +08:00
Eric
cafc5879a0 perf: 发布机可支持添加网域 2023-08-17 14:27:09 +08:00
feng626
2ec88c3591 Merge pull request #3351 from jumpserver/pr@dev@vault
perf:  优化 vault 配置
2023-08-17 12:12:46 +08:00
feng
c37212a5ce perf: 优化 vault 配置 2023-08-17 11:42:55 +08:00
“huailei000”
f9c334b003 fix: 修复当列表有额外查询参数时,导致导出选项判断不准确问题 2023-08-16 17:06:37 +08:00
老广
d43d4137d7 Merge pull request #3347 from jumpserver/pr@dev@fix_accountadd
fix: 修复账号列表添加账号时会明文显示的问题
2023-08-16 10:51:07 +08:00
Bai
614565ba9c fix: 修复账号列表添加账号时会明文显示的问题 2023-08-16 10:44:46 +08:00
老广
575015616f Merge pull request #3337 from jumpserver/pr@dev@revert_build
revert: 还原构建
2023-08-15 18:39:13 +08:00
老广
26fac22b82 Merge pull request #3346 from jumpserver/pr@dev@perf_setting
perf: 再次修改设置
2023-08-15 17:02:25 +08:00
ibuler
40b7c62099 merge: with dev 2023-08-15 17:01:41 +08:00
ibuler
017486e961 perf: 再次修改设置 2023-08-15 16:59:28 +08:00
“huailei000”
7bb4567345 perf: 优化系统设置-基本设置控制台报错问题 2023-08-15 14:38:36 +08:00
老广
76c78df007 Merge pull request #3344 from jumpserver/pr@dev@perf_setting
perf: 修改系统设置布局
2023-08-15 13:53:15 +08:00
ibuler
ff6ac297b9 perf: 整理完成 2023-08-15 13:49:08 +08:00
ibuler
6b953506e4 perf: 修改系统设置布局 2023-08-14 19:41:47 +08:00
feng626
7ec130f033 Merge pull request #3343 from jumpserver/pr@dev@automation
perf: automation 任务详情添加id
2023-08-14 18:50:42 +08:00
feng
2f9e00de2e perf: automation 任务详情添加id 2023-08-14 18:49:27 +08:00
“huailei000”
e479dbfcb5 perf: 优化命令存储-主机helptext 2023-08-14 16:54:20 +08:00
feng626
1e5b6f970d Merge pull request #3340 from jumpserver/pr@dev@perm
perf: 角色权限选择工作台时自动关联connectiontoken权限
2023-08-14 16:37:28 +08:00
feng
e7222fb63c perf: 角色权限选择工作台时自动关联connectiontoken权限 2023-08-14 16:29:18 +08:00
feng626
f9617a92a8 Merge pull request #3339 from jumpserver/pr@dev@applet_host
perf: applethost 搜索框优化
2023-08-14 14:24:56 +08:00
feng
38e1b070cf perf: applethost 搜索框优化 2023-08-14 14:23:58 +08:00
feng626
f75d97305f Merge pull request #3338 from jumpserver/pr@dev@applethost
perf: 远程应用批量部署多选优化
2023-08-14 11:25:54 +08:00
feng
7df0cc78b1 perf: 远程应用批量部署多选优化 2023-08-14 11:24:24 +08:00
吴小白
a8a90930b9 revert: 还原构建 2023-08-14 11:23:18 +08:00
Eric
69dbf2f5da perf: 优化会话暂停的组件判断 2023-08-11 13:45:55 +05:00
feng626
fc8339d659 Merge pull request #3334 from jumpserver/pr@dev@vault
perf: 优化vault配置界面
2023-08-11 16:01:41 +08:00
feng
eada8d319d perf: 优化vault配置界面 2023-08-11 15:55:46 +08:00
“huailei000”
11940428a0 perf: 优化组件监控 2023-08-11 14:38:28 +08:00
老广
af2544e24b Merge pull request #3331 from jumpserver/pr@dev@perf_change_edtion
perf: 修改 applet 翻译
2023-08-10 17:32:36 +08:00
ibuler
4546952388 perf: 修改 applet 翻译 2023-08-10 17:30:50 +08:00
老广
a0c849f29d Merge pull request #3330 from jumpserver/pr@dev@applet_add_enterprise
perf: applet 添加 entperpise 标记
2023-08-10 15:32:31 +08:00
ibuler
fb9cd1614a perf: applet 添加 entperpise 标记 2023-08-10 14:40:46 +08:00
“huailei000”
c1543183a2 perf: 优化组件监控组件 2023-08-10 09:09:13 +05:00
Aaron3S
03dc3e993c feat: 增加批量sql支持 2023-08-09 19:43:46 +08:00
Aaron3S
8719ffee8e feat: 增加批量sql支持 2023-08-09 19:43:46 +08:00
Eric
cc60f2c1f5 perf: 优化代码 2023-08-09 19:43:25 +08:00
Eric
f042692dbf perf: 工单会话暂停控制 2023-08-09 19:43:25 +08:00
Eric
3d8dc619ad perf: 支持其他组件的暂停会话 2023-08-09 19:43:25 +08:00
Bai
b223c73a47 perf: 优化账号列表显示其他字段 2023-08-09 17:05:48 +08:00
Eric
53e313517d feat: 支持最大会话连接时长 2023-08-09 10:38:22 +08:00
老广
56bea8eaf4 Merge pull request #3324 from jumpserver/pr@dev@perf_ztree_chk_css
perf: 优化 ztree chk 样式
2023-08-08 17:13:09 +08:00
老广
992025e618 Merge pull request #3323 from jumpserver/pr@dev@perf_dashboard_data
perf: 优化首页 rank 的宽度
2023-08-08 17:12:48 +08:00
ibuler
205d301578 perf: 优化 ztree chk 样式 2023-08-08 17:08:49 +08:00
ibuler
4daccc9df3 perf: 优化首页 rank 的宽度 2023-08-08 16:37:05 +08:00
老广
ac802fff59 Merge pull request #3319 from jumpserver/pr@dev@perf_ticket_session
perf: 优化工单方式打开会话监控
2023-08-08 16:18:16 +08:00
老广
a6b0da60a3 Merge pull request #3320 from jumpserver/pr@dev@fix_msg_subscribe_for_xss
fix: 修复系统设置 > 消息订阅 > 修改订阅人 因为用户名导致的 xss
2023-08-08 16:17:49 +08:00
ibuler
45c860797a fix: 修复系统设置 > 消息订阅 > 修改订阅人 因为用户名导致的 xss 2023-08-08 11:07:34 +08:00
Eric
e636aa24c8 perf: 优化工单方式打开会话监控 2023-08-08 10:37:24 +08:00
老广
86b37eeeba Merge pull request #3318 from jumpserver/pr@dev@perf_org_delete
perf: System 组织不允许删除
2023-08-08 10:28:16 +08:00
ibuler
61cf9e7f14 perf: System 组织不允许删除 2023-08-08 10:27:26 +08:00
fit2bot
344e49e7ec perf: 同名账号定义一些策略 (#3311)
* perf: 同名账号定义一些策略

* perf: 修改虚拟账号

* perf: 修改更新后的 route

---------

Co-authored-by: ibuler <ibuler@qq.com>
2023-08-08 10:16:52 +08:00
老广
3ebdacafd0 Merge pull request #3316 from jumpserver/pr@dev@fix_strategy_cannot_export
fix: 云同步策略不支持导出
2023-08-08 09:57:06 +08:00
“huailei000”
e1ae467823 feat: 创建资产-选择模版添加搜索功能 2023-08-07 18:21:58 +08:00
jiangweidong
65c1ab450b fix: 云同步策略不支持导出 2023-08-07 17:55:54 +08:00
feng626
cbe3efdb18 Merge pull request #3309 from jumpserver/pr@dev@account_backup
feat: 账号备份密钥拆分
2023-08-07 15:50:36 +08:00
feng
e5eaa5bcfa feat: 账号备份密钥拆分 2023-08-07 15:47:25 +08:00
fit2bot
df687c0c06 perf: TagInput 组件增加替换显示密码功能;命令存储-主机不明文显示密码 (#3301)
Co-authored-by: “huailei000” <2280131253@qq.com>
Co-authored-by: huailei <31801270+huailei000@users.noreply.github.com>
2023-08-07 15:03:05 +08:00
feng626
1cc00517f5 Merge pull request #3314 from jumpserver/pr@dev@check_unused_users
feat: 检测不常用账号
2023-08-07 14:45:03 +08:00
jiangweidong
f3a6d6c02b perf: 优化批量审批翻译 2023-08-07 14:19:56 +08:00
feng
7bf33a9011 feat: 检测不常用账号 2023-08-07 11:35:06 +08:00
ibuler
7948a59b80 perf: 去掉 telnet 的全员设置 2023-08-07 10:37:33 +08:00
jiangweidong
af9f715357 feat: 云同步增加同步策略 (#3264)
* feat: 云同步逻辑更新

* feat: 云同步增加同步策略

* perf: 优化

* perf: 优化提交逻辑,平台和网域只允许设置一次

* perf: 策略抽离,有列表、详情、同步任务可便捷添加策略

* fix: 修改组件层级关系

* perf: 去掉不需要的

* perf: 优化变量名称、策略添加优先级
2023-08-07 10:33:04 +08:00
吴小白
202f8a357c perf: 更新 node 16.17.1 2023-08-07 10:20:13 +08:00
jiangweidong
3e75c781b3 feat: 系统工具改为异步,增加tcpdump工具 2023-08-07 10:19:48 +08:00
吴小白
3c3ed27eb2 Merge pull request #3310 from jumpserver/pr@dev@fix_build
fix: 修正构建错误
2023-08-04 17:59:54 +08:00
吴小白
fd0e23e35f perf: 更新 node 16.5 2023-08-04 17:55:04 +08:00
吴小白
c09a2df142 fix: 修正构建错误 2023-08-04 17:35:50 +08:00
“huailei000”
36737a6f25 perf: 命令记录列表增加账号字段 2023-08-03 16:19:21 +08:00
feng626
e4af9ccc1e Merge pull request #3304 from jumpserver/pr@dev@task_search
perf: 任务添加显示项 过滤项
2023-08-02 17:51:43 +08:00
feng
3c256c6fdc perf: 任务添加显示项 过滤项 2023-08-02 17:49:47 +08:00
“huailei000”
1e36f59b23 perf: 最近会话连接资产时增加login_account参数 2023-08-02 16:48:35 +08:00
feng626
aec6ee8376 Merge pull request #3302 from jumpserver/pr@dev@applet_host_deployment
feat: 批量部署发布机
2023-08-01 17:42:28 +08:00
feng
526b049495 feat: 批量部署发布机 2023-08-01 17:38:31 +08:00
Eric
5bef5a59a9 perf: 完善工单 session 变化 2023-08-01 17:26:15 +08:00
Eric
1e371a4e32 perf: 优化 session 的状态 2023-08-01 17:26:15 +08:00
Eric
0f6fd0ed70 feat: 支持暂停会话操作 2023-08-01 17:26:15 +08:00
feng626
fc1aefbb54 Merge pull request #3251 from jumpserver/pr@dev@vault
feat: vault 添加系统配置
2023-07-31 17:57:41 +08:00
feng
7561f1224d perf: merge dev 2023-07-31 17:56:20 +08:00
老广
0932132add Merge pull request #3299 from jumpserver/pr@dev@refactor_path
perf: 修改一些组件路径,原来的太难找了
2023-07-31 14:50:21 +08:00
ibuler
8492882633 perf: 修改一些组件路径,原来的太难找了 2023-07-31 14:45:27 +08:00
老广
11698255f6 Merge pull request #3297 from jumpserver/pr@dev@perf_tmp_passwd_icon
perf: 修改临时密码的 icon
2023-07-31 10:53:48 +08:00
ibuler
53eee6c857 perf: 修改临时密码的 icon 2023-07-31 10:34:40 +08:00
老广
f2bc4d6f22 Merge pull request #3296 from jumpserver/pr@dev@perf_applet_host_account_create
perf: 修改隐藏
2023-07-28 16:16:54 +08:00
ibuler
e50b16b13b perf: 修改隐藏 2023-07-28 16:16:03 +08:00
老广
232c30cadb Merge pull request #3295 from jumpserver/pr@dev@perf_applet_host_account_create
perf: 修改应用发布机账号生成
2023-07-28 11:07:38 +08:00
jiangweidong
3142da16ae feat: 支持批量审批工单 (#3273) 2023-07-28 10:49:11 +08:00
ibuler
741f8b847e Merge branch 'dev' into pr@dev@perf_applet_host_account_create 2023-07-28 10:44:05 +08:00
ibuler
b6d00a1784 perf: 修改应用发布机账号生成 2023-07-28 10:42:54 +08:00
jiangweidong
2f122f7fbe feat: 系统工具增加服务器时间及nmap工具 (#3289) 2023-07-28 10:42:28 +08:00
feng626
c3c46b759e Merge pull request #3294 from jumpserver/pr@dev@ldap
perf: 优化ldap交互
2023-07-27 19:14:55 +08:00
feng
a1c4013a69 perf: 优化ldap交互 2023-07-27 19:12:20 +08:00
老广
8aa9690a61 Merge pull request #3293 from jumpserver/pr@dev@perf_same_ztree
perf: 统一 ztree css
2023-07-27 18:37:29 +08:00
ibuler
30c0100a0b perf: 统一 ztree css 2023-07-27 18:18:52 +08:00
fit2bot
134dd17f3f perf: 添加应用市场链接 (#3292)
* perf: 添加应用市场链接

* perf: 修改版本号

* perf: 修改高度

---------

Co-authored-by: ibuler <ibuler@qq.com>
2023-07-27 16:52:58 +08:00
feng626
505642baec Merge pull request #3291 from jumpserver/pr@dev@asset_perm_account
perf: 修改AccountFormatter 请求方法为post
2023-07-27 14:04:14 +08:00
feng
c08964ee34 perf: 修改AccountFormatter 请求方法为post 2023-07-27 14:03:14 +08:00
老广
9df443667c Merge pull request #3290 from jumpserver/pr@dev@perf_protocols
perf: 修改 protocols 显示
2023-07-26 18:39:51 +08:00
ibuler
8fd8e9c1d4 perf: 修改 protocols 显示 2023-07-26 18:33:34 +08:00
feng
aa3ab5e138 perf: 配置信息修改 2023-07-24 19:43:46 +08:00
“huailei000”
a7df8706e5 perf: 工作台概况会话添加连接方式 2023-07-24 19:20:24 +08:00
“huailei000”
c200781322 perf: 修改github配置 2023-07-21 14:59:04 +08:00
老广
75c7778a10 Merge pull request #3261 from jumpserver/dependabot/npm_and_yarn/semver-5.7.2
build(deps): bump semver from 5.7.1 to 5.7.2
2023-07-21 10:28:11 +08:00
老广
c30e919573 Merge pull request #3271 from jumpserver/dependabot/npm_and_yarn/word-wrap-1.2.4
build(deps): bump word-wrap from 1.2.3 to 1.2.4
2023-07-21 10:27:45 +08:00
老广
ffff648e6d Merge pull request #3284 from jumpserver/pr@dev@perf_account_default_show
perf: 优化账号,默认实现是否激活
2023-07-21 10:27:19 +08:00
ibuler
981e676fa2 perf: 优化账号,默认实现是否激活 2023-07-20 20:22:36 +08:00
ibuler
664855d4b0 perf: 优化从 url 中获取端口 2023-07-20 17:37:58 +08:00
老广
c79637095a Merge pull request #3281 from jumpserver/pr@dev@remove_debug_msg
perf: 去掉 debug msg
2023-07-20 16:42:25 +08:00
ibuler
2a96abdd4a perf: 去掉 debug msg 2023-07-20 16:25:11 +08:00
老广
9541b97b23 Merge pull request #3280 from jumpserver/pr@dev@perf_add_debug
perf: 修改 debug msg
2023-07-20 16:11:00 +08:00
ibuler
52518a9ff3 perf: 修改 debug msg 2023-07-20 16:08:11 +08:00
“huailei000”
24a1c11288 perf: 优化账号详情推送弹窗点击关闭图标下次点击不能正常打开问题 2023-07-20 15:58:37 +08:00
老广
313aebaf50 Merge pull request #3278 from jumpserver/pr@dev@perf_add_debug_msg
perf: 添加 setTimeout
2023-07-20 15:21:57 +08:00
ibuler
5c6373e689 perf: 添加 setTimeout 2023-07-20 15:20:41 +08:00
老广
09fe3ea107 Merge pull request #3277 from jumpserver/pr@dev@perf_add_debug_msg
perf: 添加 debug msg
2023-07-20 15:06:10 +08:00
ibuler
ccf081b608 perf: 添加 debug msg 2023-07-20 15:04:44 +08:00
Bai
80ce3293a1 fix: 优化导入组件导入数据时全部使用 patch 方法; 解决一些表格数据库不全时引来的问题; 2023-07-19 19:58:40 +08:00
“huailei000”
4ed282ff2b perf: 优化连续批量更新用户报错问题 2023-07-19 19:50:18 +08:00
老广
dd8957cb69 Merge pull request #3274 from jumpserver/pr@dev@perf_protocol_setting_reset
perf: 优化 nestfield 重置不会变更的问题
2023-07-19 19:42:24 +08:00
ibuler
36c687b854 perf: 精简写法 2023-07-19 19:30:51 +08:00
ibuler
e22ecb6fe8 perf: 优化 nestfield 重置不会变更的问题 2023-07-19 18:46:20 +08:00
ibuler
b77440284f perf: 优化 Platform 中自动化 select 的宽度 2023-07-19 08:38:03 +05:00
老广
93d866328c Merge pull request #3270 from jumpserver/pr@dev@perf_domain_platform
perf: 优化网关更新时协议显示
2023-07-19 10:18:33 +08:00
dependabot[bot]
42c7b278c5 build(deps): bump word-wrap from 1.2.3 to 1.2.4
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-18 21:21:13 +00:00
“huailei000”
14ba501f2b perf: 优化网关更新时协议显示 2023-07-18 19:57:31 +08:00
ibuler
f072546083 perf: 优化账号模版表单,在弹窗中新窗口打开 2023-07-18 18:12:54 +08:00
“huailei000”
8f9ebcdfa9 perf: 界面设置上传组件控制上传类型 2023-07-18 18:10:58 +08:00
老广
7b76917768 Merge pull request #3267 from jumpserver/pr@dev@perf_promise
perf: 修改 资产详情的 card 和 账号模版密文类型
2023-07-18 10:34:10 +08:00
ibuler
e4d4bc84b6 perf: 修改 资产详情的 card 和 账号模版密文类型 2023-07-18 10:24:03 +08:00
老广
62cf19e70e Merge pull request #3263 from jumpserver/pr@dev@perf_connection_action
perf: 优化一些 rbac 权限位,着重 connection token 的
2023-07-17 10:24:59 +08:00
老广
2cb36f89f0 Merge pull request #3265 from jumpserver/pr@dev@perf_can_unbind_from_other_source
perf: 用户在认证方式和用户来源一致时,无法解绑此类认证
2023-07-17 10:24:16 +08:00
老广
9946ad75ad Merge pull request #3266 from jumpserver/pr@dev@perf_promise
perf: 修改平台 protocols 的下拉选择,可以更改
2023-07-17 10:21:16 +08:00
ibuler
abd8919225 perf: 修改平台 protocols 的下拉选择,可以更改 2023-07-17 10:11:54 +08:00
jiangweidong
6e5e760689 perf: 用户在认证方式和用户来源一致时,无法解绑此类认证 2023-07-14 18:21:45 +08:00
ibuler
1235895973 perf: 优化一些 rbac 权限位,着重 connection token 的 2023-07-13 16:01:03 +08:00
“huailei000”
59f9025e42 perf: 调整用户登录、连接方式文案 2023-07-13 10:57:45 +08:00
Bai
d06bda6d40 perf: 优化 is_active 到 Other 中 2023-07-12 20:33:21 +08:00
fangfang.dong
a8099089b2 feat: 系统设置 - 终端设置 - 端点规则: 新增字段is_active控制是否启用 2023-07-12 20:33:21 +08:00
dependabot[bot]
fd0c14e1c0 build(deps): bump semver from 5.7.1 to 5.7.2
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-11 12:33:01 +00:00
Aaron3S
9a04c81238 feat: 增加两个组件的名称 2023-07-11 19:16:18 +08:00
老广
f1b3e5038f Merge pull request #3259 from jumpserver/pr@dev@perf_protocol_setting
perf: 修改账号配置
2023-07-11 17:28:42 +08:00
ibuler
f6fe08607b perf: 修改账号配置 2023-07-11 17:27:16 +08:00
老广
6f2ca3e26a Merge pull request #3258 from jumpserver/pr@dev@add_gpt_add_default_addr
perf: 优化修改默认值
2023-07-11 14:48:50 +08:00
ibuler
3ed9ac0d9b perf: 优化修改默认值 2023-07-11 14:47:07 +08:00
老广
821ed14f40 Merge pull request #3257 from jumpserver/pr@dev@add_gpt_proxy
perf: gpt 添加 proxy
2023-07-11 14:40:21 +08:00
老广
6f0ee734e5 Merge pull request #3255 from jumpserver/pr@dev@feat_acl_command_warning
feat: 新增危险命令告警类型: Warning
2023-07-11 12:20:14 +08:00
老广
9a5b174eb1 Merge pull request #3242 from jumpserver/pr@dev@perf_component_jsoneditor
perf: json编辑器组件增加参数控制是否可拖拽;默认可以拖拽高度
2023-07-11 12:19:29 +08:00
老广
c27e1c97f2 Merge pull request #3241 from jumpserver/pr@dev@perf_component_weekcronselect
perf: 优化时间选择组件,鼠标指针超出容器时页面显示错乱问题
2023-07-11 12:17:27 +08:00
ibuler
1b4e01d19e perf: gpt 添加 proxy 2023-07-11 11:47:41 +08:00
老广
28c836b1f6 Merge pull request #3249 from jumpserver/pr@dev@feat_chatgpt_support
feat: 支持 chatgpt 资产
2023-07-11 09:58:04 +08:00
fangfang.dong
fc85eaf6b9 feat: 新增危险命令告警类型: Warning 2023-07-10 16:22:22 +08:00
老广
678d17a4e7 Merge pull request #3256 from jumpserver/pr@dev@perf_platform_protocol
perf: 优化 protocol setting
2023-07-07 19:02:27 +08:00
ibuler
ddf9780f9c perf: 优化 protocol setting 2023-07-07 18:51:20 +08:00
fangfang.dong
d47bd4acad feat: 新增危险命令告警类型: Warning 2023-07-07 15:22:25 +08:00
jiangweidong
09dabb5d3f feat: 云同步支持UCloud公有云 (#3253) 2023-07-04 18:24:50 +08:00
Eric_Lee
9eea051884 Merge pull request #3254 from jumpserver/revert-3250-pr@dev@perf_xrdp_rdp7
Revert "perf: 新增 xrdp 协议 rdp7 端口 3390"
2023-07-04 18:02:19 +08:00
老广
f6a3eb1349 Revert "perf: 新增 xrdp 协议 rdp7 端口 3390" 2023-07-04 17:36:21 +08:00
feng
433f3a34cb perf: 改密与推送保持一致 2023-07-04 17:34:51 +08:00
老广
aa790944f6 Merge pull request #3246 from jumpserver/pr@dev@feat_support_anonymous_account
feat: 支持授权匿名账号
2023-07-04 11:46:49 +08:00
ibuler
57167fc821 perf: 优化修改 chatgpt 支持 2023-07-04 09:53:28 +08:00
feng
fb70719cba feat: vault 添加系统配置 2023-07-04 00:55:45 +08:00
ibuler
5988892840 perf: 修改端口可以从 url 中获取 2023-07-03 19:44:53 +08:00
老广
af11e1bf0c Merge pull request #3244 from jumpserver/pr@dev@perf_permission_joblog
perf: 作业日志增加权限位控制
2023-07-03 16:36:03 +08:00
老广
09416b13d3 Merge pull request #3245 from jumpserver/pr@dev@perf_route_user_roles
perf: 点击角色列表组织默认切换到全局组织,在角色列表不能点击切换组织
2023-07-03 16:35:30 +08:00
老广
478745f534 Merge pull request #3250 from jumpserver/pr@dev@perf_xrdp_rdp7
perf: 新增 xrdp 协议 rdp7 端口 3390
2023-07-03 16:29:45 +08:00
ibuler
3de2bf73ea perf: 修改 i18n 2023-07-03 11:24:36 +08:00
Eric
3b877739a3 perf: endpoint 增加 rdp7_port 2023-06-30 18:41:49 +08:00
ibuler
06899d6932 perf: 修改 chatgpt 支持 2023-06-30 18:31:50 +08:00
Eric
6f3d21bb77 perf: 新增 xrdp 协议 rdp7 端口 3390 2023-06-30 18:12:00 +08:00
ibuler
b7da517ad7 feat: 支持 chatgpt 资产 2023-06-30 17:34:45 +08:00
“huailei000”
598b6b12e8 perf: 点击角色列表组织默认切换到全局组织,在角色列表不能点击切换组织 2023-06-30 15:59:31 +08:00
ibuler
400ceac737 perf: 修改协议设置 2023-06-30 15:55:20 +08:00
老广
34de6d8775 Merge pull request #3247 from jumpserver/pr@dev@perf_dev_env
perf: 调整 luna 的 proxy 的配置,支持 luna 录像播放
2023-06-30 15:53:11 +08:00
Eric
080542633a perf: 调整 luna 的 proxy 的配置,支持 luna 录像播放 2023-06-29 15:20:14 +08:00
ibuler
653f26137b perf: 修改翻译 2023-06-27 16:45:40 +08:00
ibuler
c86f0cc08d feat: 支持授权匿名账号 2023-06-27 16:41:27 +08:00
“huailei000”
541c6c5fe5 perf: json编辑器组件增加参数控制是否可拖拽;默认可以拖拽高度 2023-06-27 15:58:33 +08:00
“huailei000”
c36a210cd4 perf: 作业日志增加权限位控制 2023-06-25 15:56:41 +08:00
“huailei000”
b9f26df5e6 perf: 优化时间选择组件,鼠标指针超出容器时页面显示错乱问题 2023-06-21 18:28:32 +08:00
老广
dd0baa7b00 Merge pull request #3239 from jumpserver/pr@dev@perf_sql_display
perf: 修改 sql 显示
2023-06-21 14:35:26 +08:00
ibuler
524105278b perf: 修改 sql 显示 2023-06-21 14:08:11 +08:00
老广
2656546e3b Merge pull request #3236 from jumpserver/pr@dev@feat_assets_detail
feat: 控制台 - 资产管理 - 资产详情 新增标签增删功能
2023-06-21 13:37:34 +08:00
ibuler
ecaf1ceace pref: 添加 sql debug 2023-06-21 12:03:26 +08:00
“huailei000”
cf515a18de perf: 替换批量更新图标 2023-06-21 11:18:13 +08:00
Eric
a0ab7d3f32 perf: 修复 terminal 显示问题 2023-06-21 11:17:38 +08:00
fangfang.dong
467c3f7288 feat: 控制台 - 资产管理 - 资产详情 新增标签增删功能 2023-06-20 17:35:49 +08:00
老广
f68eaee8c8 Merge pull request #3231 from jumpserver/pr@dev@perf_org_change_global
perf: 优化手动切换到全局后,点击菜单回到上个非全局组织的问题
2023-06-20 16:01:31 +08:00
老广
163f661386 Merge pull request #3232 from jumpserver/pr@dev@applet_host_add_help_text
perf: 添加应用发布机说明,可以通过标签选择
2023-06-20 15:45:04 +08:00
老广
e54bc076d6 Merge pull request #3233 from jumpserver/pr@dev@perf_help_text
perf: 优化一些说明文案
2023-06-20 15:44:33 +08:00
ibuler
1fbf04ae51 perf: 优化一些说明文案 2023-06-20 15:21:41 +08:00
ibuler
f002c4aa3d perf: 添加应用发布机说明,可以通过标签选择 2023-06-20 14:19:43 +08:00
ibuler
f67bd39067 perf: 添加应用发布机说明,可以通过标签选择 2023-06-20 14:14:39 +08:00
ibuler
5c60624b2d perf: 优化手动切换到全局后,点击菜单回到上个非全局组织的问题 2023-06-19 18:48:21 +08:00
fangfang.dong
488684f293 fix: 修复创建用户时 先输入密码再选择来源为LDAP 密码项仍可输入未隐藏的BUG 2023-06-19 17:10:14 +08:00
Bai
5c511345bf fix: 修复 Endpoint Host 被禁用的问题 2023-06-19 16:37:40 +08:00
老广
be85a91ccd Merge pull request #3224 from jumpserver/pr@dev@change_attr_form
perf: 优化 attr form 字段选择
2023-06-15 18:31:43 +08:00
ibuler
94a02a2e7e perf: 修改冲突 2023-06-15 18:30:46 +08:00
ibuler
e60a33a2b1 perf: 修改 json attr form 表单 2023-06-15 18:28:16 +08:00
ibuler
8449dafd55 perf: 优化 attr form 字段选择 2023-06-15 17:59:23 +08:00
“huailei000”
a787737290 perf: 优化同步设置组织下拉框显示过长问题 2023-06-15 15:10:53 +08:00
老广
fa517c8325 Merge pull request #3220 from jumpserver/pr@dev@perf_attr_change_value
perf: 修改 change value
2023-06-15 14:42:36 +08:00
老广
0748b6ce0c Merge pull request #3221 from jumpserver/pr@dev@perf_org_change_on_create
perf: 修改 更新切换
2023-06-15 14:42:16 +08:00
ibuler
59ee3eff17 perf: 修改 更新切换 2023-06-15 14:31:31 +08:00
ibuler
5cc17de1e0 perf: 修改 change value 2023-06-15 14:28:04 +08:00
feng626
42aacd9df5 Merge pull request #3218 from jumpserver/pr@dev@translate
fix: 修改用户登录和连接方式的详情翻译
2023-06-15 12:45:55 +08:00
feng
bb27171b09 fix: 修改用户登录和连接方式的详情翻译 2023-06-15 12:44:55 +08:00
feng626
ccc163ef07 Merge pull request #3217 from jumpserver/pr@dev@user_change_password
fix: 修改密码时 判断管理员校验规则失败
2023-06-15 12:14:50 +08:00
feng
e63630fce7 fix: 修改密码时 判断管理员校验规则失败 2023-06-15 11:29:03 +08:00
“huailei000”
17748c56c9 perf: 优化自定义资产列表不显示操作问题 2023-06-15 11:03:36 +08:00
老广
d880e5cb8c Merge pull request #3215 from jumpserver/pr@dev@perf_user_json_attr_disabled
perf: 优化 json user field
2023-06-15 10:32:39 +08:00
ibuler
310fe8068a perf: 优化 json user field 2023-06-15 10:31:40 +08:00
Eric
8670c3988d perf: 优化翻译 2023-06-14 21:07:41 +08:00
Bai
b5144625ee perf: 优化 命令过滤 菜单位置 2023-06-14 20:15:13 +08:00
“huailei000”
58b6d75ccf perf: 优化ladp中组织名称过长显示的问题 2023-06-14 20:02:03 +08:00
“huailei000”
c9ec67cc50 perf: 优化平台列表tab页点击导出会导出全部数据问题 2023-06-14 19:02:16 +08:00
“huailei000”
e4d1533091 perf: 优化连接方式详情用户、资产跳转报错问题 2023-06-14 18:31:50 +08:00
老广
97440860d9 Merge pull request #3208 from jumpserver/pr@dev@perf_route_replace
perf: 修改切换组织时的 url 替换
2023-06-14 16:45:08 +08:00
ibuler
3dd31dc76e perf: 替换路由中的 uuid 2023-06-14 16:35:56 +08:00
feng626
1822d72a9b Merge pull request #3209 from jumpserver/pr@dev@perm_asset
perf: 授权切换组织报错
2023-06-14 16:21:35 +08:00
feng
3267839327 perf: 授权切换组织报错 2023-06-14 16:20:47 +08:00
ibuler
17d73f2b3c perf: 修改切换组织时的 url 替换 2023-06-14 16:10:42 +08:00
“huailei000”
ec49241272 perf: 删除资产授权规则详情账号页面的“删除”按钮;删除用户详情页面资产授权规则页的“删除”/“克隆”按钮 2023-06-14 14:37:43 +08:00
feng626
c2e10b8a34 Merge pull request #3205 from jumpserver/pr@dev@website
perf: 资产website script暂时不展示特殊信息
2023-06-14 11:19:59 +08:00
feng
29813e7ce1 perf: 资产website script暂时不展示特殊信息 2023-06-14 11:18:42 +08:00
Aaron3S
919d95b56b fix: 修复playbook文件树无法点击的问题 2023-06-14 11:07:12 +08:00
feng626
5f95fc0422 Merge pull request #3200 from jumpserver/pr@dev@user_perm
fix: 修改node asset 关联的权限位
2023-06-14 10:36:22 +08:00
feng
49a4000086 fix: 修改node asset 关联的权限位 2023-06-14 10:35:14 +08:00
老广
9c7818313e Merge pull request #3199 from jumpserver/pr@dev@perf_select2_componenet
perf: 优化 select2 组件
2023-06-13 19:38:00 +08:00
ibuler
705e236279 merge: with dev 2023-06-13 19:36:59 +08:00
ibuler
d4ecb33c78 perf: 去掉注释内容 2023-06-13 19:34:05 +08:00
ibuler
67e45ad284 perf: 优化修改select2 2023-06-13 19:32:39 +08:00
ibuler
8cb72c3d82 perf: 优化 select2 组件 2023-06-13 19:26:38 +08:00
“huailei000”
b65a007d37 perf: 资产详情添加账号后关闭弹窗 2023-06-13 15:40:33 +08:00
“huailei000”
e487ee24af perf: 资产登录用户匹配结果不显示操作 2023-06-13 14:42:54 +08:00
feng626
bfbc78e342 Merge pull request #3196 from jumpserver/pr@dev@asset_login_acl
fix: 修复创建资产登录规则-选择指定资产-指定账号下拉框显示的是组织下的所有账号问题
2023-06-13 14:36:15 +08:00
feng
54879a8174 fix: 修复创建资产登录规则-选择指定资产-指定账号下拉框显示的是组织下的所有账号问题 2023-06-13 14:34:52 +08:00
老广
36634a2ce5 Merge pull request #3195 from jumpserver/pr@dev@perf_select2_filter
perf: 优化 select 本地搜索
2023-06-13 13:27:41 +08:00
ibuler
07920a4917 perf: 优化 select 本地搜索 2023-06-13 13:24:20 +08:00
老广
25932587a5 Merge pull request #3194 from jumpserver/pr@dev@perf_json_attr_form_dialog
perf: json attr form 更改 name 和 match 方式后,不再清空 value
2023-06-13 12:42:22 +08:00
ibuler
d98534d039 perf: json attr form 更改 name 和 match 方式后,不再清空 value 2023-06-13 12:41:32 +08:00
feng626
6cb5d6ac46 Merge pull request #3193 from jumpserver/pr@dev@connect-method-acls
perf: 连接方式 添加详情页
2023-06-13 11:31:41 +08:00
feng
fab461c10d perf: 连接方式 添加详情页 2023-06-13 11:29:59 +08:00
“huailei000”
644980bc40 perf: 资产登录详情、命令过滤详情里的列表不显示操作功能 2023-06-13 11:19:49 +08:00
老广
a56e3843bf Merge pull request #3191 from jumpserver/pr@dev@perf_action_display
perf: 优化 action 显示
2023-06-13 10:09:29 +08:00
ibuler
5ca188c014 perf: 优化 action 显示 2023-06-13 09:51:46 +08:00
“huailei000”
510cf48def perf: 修改账号推送详情标题不正确问题 2023-06-12 18:29:36 +08:00
feng626
e64fdab7fc Merge pull request #3189 from jumpserver/pr@dev@usergroup
perf: 修改用户组添加全部用户api的权限位
2023-06-12 18:16:26 +08:00
feng
aaec496642 perf: 修改用户组添加全部用户api的权限位 2023-06-12 18:14:49 +08:00
“huailei000”
80c05c9b65 perf: 优化资产列表显示 2023-06-12 18:05:08 +08:00
feng626
c9159838f5 Merge pull request #3187 from jumpserver/pr@dev@accounts
fix: 工单账号组建默认值为空
2023-06-12 17:06:09 +08:00
feng
b8e005f4b1 fix: 工单账号组建默认值为空 2023-06-12 17:03:59 +08:00
feng626
6e73ded101 Merge pull request #3185 from jumpserver/pr@dev@perms_user
perf: 优化授权详情用户组切换组织一些资源不存在抛出html错误
2023-06-12 16:28:45 +08:00
feng
6926556fa3 perf: 优化授权详情用户组切换组织一些资源不存在抛出html错误 2023-06-12 16:24:01 +08:00
老广
24971c1112 Merge pull request #3183 from jumpserver/pr@dev@perf_json_attr_select_match
perf: 优化 Json attr field 选择
2023-06-12 16:14:03 +08:00
“huailei000”
763792f42e perf: 用户登录列表不显示用户字段 2023-06-12 16:12:28 +08:00
ibuler
0ff28d2626 perf: 优化 Json attr field 选择 2023-06-12 16:10:55 +08:00
老广
f607eeda42 Merge pull request #3182 from jumpserver/pr@dev@perf_acl_global_org
perf: 限制 connect method acl 和 user login acl 为 global org
2023-06-12 15:48:10 +08:00
ibuler
c0145f4ec4 perf: 限制 connect method acl 和 user login acl 为 global org 2023-06-12 15:43:54 +08:00
feng626
7c995c005b Merge pull request #3179 from jumpserver/pr@dev@user_add
perf: 用户组添加 user 显示优化
2023-06-09 18:53:42 +08:00
feng
21f4f003bb perf: 用户组添加 user 显示优化 2023-06-09 18:52:44 +08:00
老广
fbbac6cea5 Merge pull request #3178 from jumpserver/pr@dev@perf_account_add_error
perf: 优化 account 添加
2023-06-09 18:43:21 +08:00
ibuler
302400e350 perf: 优化 account 添加 2023-06-09 18:41:52 +08:00
“huailei000”
b2120678dd perf: 优化TagInput组件样式 2023-06-09 18:37:43 +08:00
feng626
03c7755703 Merge pull request #3176 from jumpserver/pr@dev@user_phone
perf: 用户手机添加默认值及账号模版详情切换至显示
2023-06-09 18:32:58 +08:00
feng
21373574de perf: 用户手机添加默认值及账号模版详情切换至显示 2023-06-09 18:31:54 +08:00
“huailei000”
342ee31c72 perf: 优化命令过滤指定账号-选择账号显示不全问题 2023-06-09 18:21:28 +08:00
feng626
7aa8afec44 Merge pull request #3174 from jumpserver/pr@dev@account
perf: 修改创建资产账号的顺序及账号模版批量添加隐藏su_from_username字段
2023-06-09 16:23:30 +08:00
feng
d6b54c9879 perf: 修改创建资产账号的顺序及账号模版批量添加隐藏su_from_username字段 2023-06-09 16:18:52 +08:00
老广
b1952a180f Merge pull request #3173 from jumpserver/pr@dev@perf_asset_json_filter_platform_m2m
perf: 资产根据属性筛选允许多选
2023-06-08 18:40:33 +08:00
ibuler
d3a7fb63b9 perf: 资产根据属性筛选允许多选 2023-06-08 18:34:43 +08:00
jiangweidong
53cfdbd3a1 feat: 支持文件上传下载备份 (#3118)
* feat: 支持文件上传下载备份

* perf: 修改下载sftp文件api接口

* perf: 优化FTP审计中文件下载提示

* perf: 优化文件上传下载审计文件的提示信息
2023-06-08 18:06:37 +08:00
ibuler
41cd77d75e fix: 修复一个正则表达式不被 safari 支持的问题 2023-06-08 15:48:53 +08:00
老广
15ac510c6f Merge pull request #3170 from jumpserver/pr@dev@perf_acls_connect_methods
perf: 用户登录 ACL 挪到控制中
2023-06-08 14:51:26 +08:00
老广
31e005eca7 Merge pull request #3171 from jumpserver/pr@dev@fix_btoa_error
perf: 修复 btoa 的报错,首先 encode 一下
2023-06-08 14:50:54 +08:00
ibuler
842ea3cbf3 perf: 修复 btoa 的报错,首先 encode 一下 2023-06-08 14:50:08 +08:00
ibuler
9f893af74b perf: ACL 中添加一些 help text 2023-06-08 14:37:12 +08:00
ibuler
5274dc4e6b perf: 修改翻译 2023-06-08 13:50:01 +08:00
ibuler
64441e4836 perf: 还是放到权限管理中 2023-06-08 11:28:03 +08:00
fangfang.dong
43997eeac1 feat: 系统设置 - 短信服务 - 多平台配置测试手机号: 增加区号显示与修改功能 2023-06-08 11:03:06 +08:00
ibuler
1b95af54c1 perf: acls 菜单 2023-06-08 10:58:18 +08:00
ibuler
e79f95a822 perf: 修改翻译 2023-06-07 17:55:53 +08:00
ibuler
fb85e168e7 perf: 用户登录 ACL 挪到控制中 2023-06-07 17:41:40 +08:00
feng626
521cafa2af Merge pull request #3168 from jumpserver/pr@dev@fix_ticket
fix: 修复工单选择账号无法按照对应资产节点过滤账号,以及自己申请的工单不能关闭
2023-06-05 15:58:39 +08:00
feng
bb9f905dd4 fix: 修复工单选择账号无法按照对应资产节点过滤账号,以及自己申请的工单不能关闭 2023-06-05 15:57:19 +08:00
nut
3eda4381bb Update Assets.vue 2023-06-02 18:27:23 +08:00
nut
080acf57f7 Update index.vue 2023-06-02 18:27:23 +08:00
nut
9c137e6763 Update ja.json 2023-06-02 18:27:23 +08:00
nut
75f5363602 Update en.json 2023-06-02 18:27:23 +08:00
nut
32f6d5dc4a Update zh.json 2023-06-02 18:27:23 +08:00
fangfangdong
21265aa983 feat: 资产平台详情添加资产列表页(只显示当前组织下的资产) 2023-06-02 18:27:23 +08:00
feng626
e11441e5a7 Merge pull request #3165 from jumpserver/pr@dev@user_list
perf: 用户列表不显示is_superuser
2023-05-31 19:24:44 +08:00
feng
e7624f47bf perf: 用户列表不显示is_superuser 2023-05-31 19:20:13 +08:00
ibuler
7276b19a92 perf: 优化 nest fields 支持是否隐藏 2023-05-31 16:52:25 +08:00
fangfangdong
32fa172197 feat: 系统设置-安全设置 支持配置 作业中心命令黑名单 2023-05-31 14:59:16 +08:00
老广
aa80a07bfc Merge pull request #3161 from jumpserver/pr@dev@perf_platform_automation
perf: 修改 platform automation 的显示
2023-05-30 19:37:41 +08:00
ibuler
fd97a6c4a2 perf: 修改 platform automation 的显示 2023-05-30 19:35:33 +08:00
fangfangdong
9b355a2942 style: 调整国际化文件中时间单位显示方式 2023-05-30 16:59:19 +08:00
ibuler
0431f7108a perf: 优化 perm action 中的 helptext 2023-05-29 19:47:57 +08:00
ibuler
3500ab89f5 perf: 登录资产的 ACL 支持 ip 控制 2023-05-29 19:47:57 +08:00
feng
fcdcf29efa feat: 用户组绑定所有用户 2023-05-29 16:27:00 +08:00
feng
49d74271c8 feat: 用户组一键添加全部用户 2023-05-29 16:27:00 +08:00
Bai
9a7eca3cf8 perf: 优化终端端点 Host 字段默认端点禁止修改 2023-05-26 17:21:19 +08:00
feng
dc686f2af7 fix: 修改k8s修改不了端口问题 2023-05-26 14:56:49 +08:00
feng626
172c6d69a1 Merge pull request #3148 from jumpserver/pr@dev@ldap_import_orgs
perf: ldap 能多组织同步用户
2023-05-25 17:37:18 +08:00
feng
3f2a846184 perf: 账号模版改密的helptext用到修改资产协议上了 2023-05-25 17:05:40 +08:00
ibuler
48ac797b59 perf: LDAP 测试 api 改为异步的 2023-05-25 16:54:54 +08:00
halo
92962bdc5a feat: 支持删除已收集的账号 2023-05-25 14:47:48 +08:00
Bai
ca4889a19a perf: 优化 API 调用最大超时时间为 2 min 2023-05-25 14:47:21 +08:00
ibuler
e4631a5309 perf: 优化 list field 组件为 tag input 2023-05-25 14:45:43 +08:00
jiangweidong
62eced61d2 feat: 支持自定义短信认证 2023-05-25 14:44:00 +08:00
feng626
7a8df3fb7b Merge pull request #3150 from jumpserver/pr@dev@unable_to_delete_all_users
perf: 删除全部用户错误提示
2023-05-25 11:38:07 +08:00
feng
91ef3cfaab perf: 删除全部用户错误提示 2023-05-25 11:22:13 +08:00
吴小白
c92843e973 perf: 更新 nginx 镜像版本 2023-05-25 10:16:48 +08:00
feng
47372df4d2 perf: ldap 能多组织同步用户 2023-05-24 19:10:39 +08:00
老广
143e913554 Merge pull request #3101 from jumpserver/pr@dev@perf_m2m_json_field
feat: ACL 中选择可以根据属性进行选择
2023-05-24 15:21:28 +08:00
ibuler
61b2b0fb23 merge: with dev 2023-05-24 15:18:12 +08:00
ibuler
54ac31920c perf: 修改默认值 2023-05-24 15:15:43 +08:00
ibuler
4eaeff0a18 perf: 去掉 console.log 2023-05-24 14:30:52 +08:00
ibuler
08c16aae72 perf: 拆分为多个文件 2023-05-23 15:28:20 +08:00
feng626
bf5e218edc Merge pull request #3142 from jumpserver/pr@dev@ndoe_tree
perf: 资产树显示节点详情的key
2023-05-22 15:50:09 +08:00
feng
e8dbd99f74 perf: 资产树显示节点详情的key 2023-05-22 15:49:15 +08:00
Bai
9ab2acfa5b fix: 修复系统管理员不能互相更新的问题 2023-05-19 18:08:30 +08:00
jiangweidong
d3cbb48e05 fix: 展示手机号码 2023-05-19 17:02:32 +08:00
jiangweidong
170dde2ba5 fix: 去掉手机区号和手机号之间的空格 2023-05-19 17:02:32 +08:00
Bai
021eb1fd5b perf: 优化资产平台详情显示ID字段;优化组件监控显示Video-Worker名称 2023-05-19 16:01:17 +08:00
ibuler
1b2c85d86d perf: 修改完成 m2m json field 2023-05-18 21:33:58 +08:00
Bai
2434e434b7 perf: 优化组织管理员不能更新系统管理员 2023-05-18 19:04:55 +08:00
ibuler
3a5aa7bf90 perf: 优化 Json field 2023-05-18 17:31:56 +08:00
feng626
dbe4c232c1 Merge pull request #3133 from jumpserver/pr@dev@account
fix: 修改创建资产时创建账号的顺序
2023-05-18 16:30:36 +08:00
feng
3294495577 fix: 修改创建资产时创建账号的顺序 2023-05-18 16:29:30 +08:00
ibuler
05e7e6dd06 perf: 优化组织切换可能引起的路由错误问题 2023-05-17 20:06:13 +08:00
ibuler
936de3c9fb perf: stash json 2023-05-17 18:50:16 +08:00
feng626
51dac8ca30 Merge pull request #3131 from jumpserver/pr@dev@ticket_session
fix: 工单命令复核 session 404
2023-05-17 18:40:07 +08:00
Aaron3S
89b3ea51f5 perf: 优化 ops 用户提示 2023-05-17 18:38:50 +08:00
Aaron3S
bdfc608534 fix: 修复快捷命令选中search节点错误的问题 2023-05-17 18:38:50 +08:00
feng
667514e90b fix: 工单命令复核 session 404 2023-05-17 18:36:33 +08:00
“huailei000”
83a116c094 perf: 优化更新平台时协议如果是http显示为http(s) 2023-05-16 19:55:55 +08:00
“huailei000”
211933349e fix: 修复更新命令存储不显示值 2023-05-16 18:08:02 +08:00
jiangweidong
ec51b243b1 perf: 修改用户来源判断逻辑和函数名对应上 2023-05-16 18:06:09 +08:00
jiangweidong
80eb072c1b perf: 修改变量名 2023-05-16 18:06:09 +08:00
jiangweidong
ce08fd57c6 perf: 优化变量名 2023-05-16 18:06:09 +08:00
jiangweidong
40ccab1a19 perf: 函数功能抽离 2023-05-16 18:06:09 +08:00
jiangweidong
8489d94643 perf: 非本地用户无法解绑飞书、企业微信、钉钉 2023-05-16 18:06:09 +08:00
Bai
72693af5fd perf: 优化忘记密码URL不自动增加 http:// 前缀 2023-05-16 11:27:26 +08:00
Bai
1fdcf9ff75 perf: 优化资产、网关列表页面显示 labels 信息不全的问题 2023-05-15 19:03:56 +08:00
“huailei000”
c4ad25bfe3 perf:账号查看-修改的密码进行加密 2023-05-15 15:51:48 +08:00
Aaron3S
d6a7d93398 fix: 修复ops 快捷命令搜索树不生效的问题 2023-05-15 10:44:58 +08:00
“huailei000”
43b2ad3104 perf: 创建资产增加翻译 2023-05-15 10:42:14 +08:00
“huailei000”
2209eea890 feat: 平台设置中增加公共的属性 2023-05-11 14:49:43 +08:00
Aaron3S
1a56546d2e fix: 添加遗漏翻译 2023-05-10 16:04:31 +08:00
Aaron3S
fa52226958 feat: 作业中心根据当前选择的资产提示用户名 2023-05-10 15:40:36 +08:00
老广
42ee6c0848 Merge pull request #3112 from jumpserver/pr@dev@feat_applet_accounts_reuse
feat: 发布机详情中增加生成账号 api
2023-05-10 15:37:09 +08:00
ibuler
49f63d638c feat: 发布机详情中增加生成账号 api 2023-05-10 15:00:13 +08:00
halo
7e6d78d223 perf: 优化远程应用上传弹窗 2023-05-10 14:22:15 +08:00
“huailei000”
a99a5a6312 perf: 进度条颜色根据系统颜色变化 2023-05-10 14:12:15 +08:00
feng626
e80b162eca Merge pull request #3111 from jumpserver/pr@dev@account_template
feat: 账号模版 切换自
2023-05-09 15:29:15 +08:00
feng
fabf60ac47 feat: 账号模版 切换至 2023-05-09 14:18:49 +08:00
“huailei000”
14b5eb5239 perf: 内置的平台详情协议设置弹窗不显示alert提示 2023-05-08 18:53:13 +08:00
“huailei000”
e365eaef8a feat: 账号列表-查看账号支持类型为password的账号修改密码 2023-05-08 18:46:27 +08:00
“huailei000”
d14cbce2d6 perf: 用户列表、资产列表、资产授权批量更新后自动关闭批量更新窗口;资产授权批量更新后自动刷新列表 2023-05-08 17:03:13 +08:00
feng626
f29f88b78b Merge pull request #3102 from jumpserver/pr@dev@feat_exportdialog_add_tip
feat: 导出弹窗增加导出提示配置;账号导出增加导出提示
2023-05-08 17:01:52 +08:00
“huailei000”
40ccff9685 fix: 修复创建端点规则不显示默认值问题;修复云同步-更新账号不显示IP网段问题 2023-05-08 17:01:07 +08:00
“huailei000”
b85de1de33 fix: 修复更新端点规则ip不显示问题 2023-05-08 14:45:07 +08:00
“huailei000”
640e758016 feat: 导出弹窗增加导出提示配置;账号导出增加导出提示 2023-05-08 14:09:10 +08:00
ibuler
09bd49941f perf: 修改 asset login acl 2023-05-08 10:49:09 +08:00
ibuler
edc74c8dfb perf: 修改翻译 2023-05-08 10:38:03 +08:00
jiangweidong
fcd7d17ef1 perf: 优化变量名 2023-05-08 09:59:20 +08:00
jiangweidong
574b7b54c5 perf: 重复逻辑抽离 2023-05-08 09:59:20 +08:00
jiangweidong
f486da0018 perf: 统一格式 2023-05-08 09:59:20 +08:00
jiangweidong
1598b4e4ad 时间组件快捷支持->半年、1年选项 2023-05-08 09:59:20 +08:00
ibuler
4556d3f4fe perf: 修改 m2m placeholder 2023-05-06 19:51:10 +08:00
ibuler
e521868cd2 Perf: 前端好像差不多了 2023-05-06 19:00:59 +08:00
ibuler
d3d6a0e890 perf: 优化 JSONManyToMany 字段 2023-05-05 19:10:13 +08:00
“huailei000”
fd4223e107 fix: 修复登录限制提交报错问题 2023-05-05 16:04:49 +08:00
“huailei000”
152301b0f3 fix: 修复安全设置-登录限制黑白名单参数不能显示的问题 2023-05-04 10:49:05 +08:00
ibuler
cb7a492b74 fix: 修复更新资产时,其它资产协议丢失的问题 2023-05-04 10:36:44 +08:00
老广
80dd8a23ec Merge pull request #3084 from jumpserver/pr@dev@fix_asset_gatheredinfo_undefined
fix: 修复资产硬件信息显示undefined问题
2023-04-28 11:12:55 +08:00
老广
41729ebe53 Merge pull request #3083 from jumpserver/pr@dev@perf_command_jump
perf: DetailFormatter组件增加配置参数openInNewPage控制是否新页卡打开;命令记录会话点击新页卡打开
2023-04-28 11:12:20 +08:00
“huailei000”
da3e6aff76 perf: DetailFormatter组件增加配置参数createInNewPage控制是否新页卡打开;命令记录会话点击新页卡打开 2023-04-28 11:06:12 +08:00
老广
7ff8114850 Merge pull request #3085 from jumpserver/pr@dev@perf_switch_icon
perf: 修改交换机图标
2023-04-28 10:40:03 +08:00
Bai
43eaaf3eba fix: 修复云账号LAN不能创建的问题 2023-04-27 19:32:58 +08:00
ibuler
26d8154db8 feat: 支持 json m2m field 2023-04-27 16:46:38 +08:00
feng626
673198af07 Merge pull request #3088 from jumpserver/pr@dev@perm_user
perf: 用户工作台 资产可显示标签
2023-04-25 14:47:56 +08:00
feng
b14630c33a perf: 用户工作台 资产可显示标签 2023-04-25 14:46:37 +08:00
feng626
187a0824fc Merge pull request #3087 from jumpserver/pr@dev@perm_user
perf: 用户工作台资产显示更多字段
2023-04-25 14:35:48 +08:00
feng
e180fbd2a5 perf: 用户工作台资产显示更多字段 2023-04-25 14:34:23 +08:00
feng626
90ebb0ff0b Merge pull request #3086 from jumpserver/pr@dev@account_template_change_secret
feat: 修改模版密文后 同步修改账号
2023-04-25 10:28:23 +08:00
feng
c922fa99fd perf: 修改同步密文url 2023-04-25 10:26:27 +08:00
“huailei000”
5e3917d61c perf: 修改更新密文显示字段 2023-04-24 19:13:34 +08:00
feng
57702bcef3 feat: 修改模版密文后 同步修改账号 2023-04-24 15:35:08 +08:00
“huailei000”
a055b4fe45 perf: 修改交换机图标 2023-04-24 15:04:13 +08:00
“huailei000”
7305bf772c fix: 修复资产硬件信息显示undefined问题 2023-04-24 11:28:22 +08:00
ibuler
3a4c579c6c perf: 优化协议的长度 2023-04-21 16:54:00 +08:00
老广
4ce651e319 Revert "perf: 自定义平台协议名称宽度自适应显示" 2023-04-21 16:53:38 +08:00
老广
c3adfe7031 Merge pull request #3076 from jumpserver/pr@dev@perf_platform_select_autowidth
perf: 自定义平台协议名称宽度自适应显示
2023-04-21 16:44:20 +08:00
“huailei000”
b7868e804d perf: 自定义平台协议名称宽度自适应显示 2023-04-21 16:40:51 +08:00
“huailei000”
3ea8239c77 fix: 修复云同步更新实例任务IP网段组不显示问题 2023-04-21 14:29:30 +08:00
老广
ddb7369994 Merge pull request #3071 from jumpserver/pr@dev@fix_custom_platform
perf: 优化平台列表 tab
2023-04-21 10:14:13 +08:00
ibuler
97b0260921 perf: 优化平台列表 tab 2023-04-20 22:51:16 +08:00
ibuler
213f896ab8 perf: 优化账号切换的 disable 属性 2023-04-20 19:47:30 +08:00
ibuler
9ef79e677c perf: 优化账号切换自显示
perf: 切换账号

perf: 修改缩进啊
2023-04-20 19:25:59 +08:00
feng626
8e03194a65 Merge pull request #3065 from jumpserver/pr@dev@active
fix: 账号详情激活
2023-04-20 19:22:29 +08:00
ibuler
4c6f9d6490 perf: 切换账号
perf: 修改缩进啊
2023-04-20 19:22:20 +08:00
feng
95e14227f0 fix: 账号详情激活 2023-04-20 19:19:47 +08:00
Bai
1bf5e9c3bc fix: 批量更新资产平台help-text 2023-04-20 18:38:25 +08:00
Bai
0b52dfd852 fix: 资产/系统平台更新页面去掉重置按钮 2023-04-20 18:22:15 +08:00
“huailei000”
7cc3a7f9a1 perf: 调整用户详情登录规则helptext 2023-04-20 17:46:59 +08:00
“huailei000”
74df2dc164 perf: 文件加密表单显示文字 2023-04-20 17:15:01 +08:00
“huailei000”
b40e6bb412 perf: 网关系统平台不能选择 2023-04-20 15:58:31 +08:00
“huailei000”
10c9a54896 perf: 批量添加账号报错后显示全部报错信息 2023-04-20 15:25:42 +08:00
Aaron3S
1f4d211c32 fix: 修复更新调用创建接口的问题 2023-04-20 14:17:20 +08:00
Aaron3S
b736a3aeb8 fix: 修复 playbook form 回显问题 2023-04-20 14:12:37 +08:00
老广
b3050e62b3 Merge pull request #3048 from jumpserver/pr@dev@fix_assetupdate_form_show
fix: 修复更新资产系统平台显示id问题
2023-04-20 13:32:13 +08:00
老广
675fd1ba9f Merge pull request #3050 from jumpserver/pr@dev@perf_applet_upload
perf: 优化 applet 上传
2023-04-20 13:30:21 +08:00
ibuler
b1579b1fbd perf: 优化 applet 上传 2023-04-20 13:28:07 +08:00
“huailei000”
84d4aebdeb fix: 修复账号详情更新切换自报错问题 2023-04-20 11:32:39 +08:00
“huailei000”
6b1c0590ce fix: 修复更新资产系统平台显示id问题 2023-04-20 11:19:22 +08:00
“huailei000”
b7fd52fa6e perf: 账号改密详情删除节点刷新列表 2023-04-19 14:09:43 +08:00
feng626
904dbd6d5e Merge pull request #3045 from jumpserver/pr@dev@ticket_command
fix: 工单命令复核不显示账号
2023-04-19 13:32:49 +08:00
feng
c6c693d112 fix: 工单命令复核不显示账号 2023-04-19 13:31:23 +08:00
feng626
3f946f9ada Merge pull request #3044 from jumpserver/pr@dev@cmd_group
fix: 命令过滤组 内容默认值修改
2023-04-19 11:41:36 +08:00
feng
35482abf7a fix: 命令过滤组 内容默认值修改 2023-04-19 11:40:54 +08:00
feng626
8b77b3a6df Merge pull request #3043 from jumpserver/pr@dev@web_asset_https
perf: web资产 协议显示https
2023-04-19 11:30:59 +08:00
feng
8240f56ecd perf: web资产 协议显示https 2023-04-19 11:30:02 +08:00
“huailei000”
77dd600393 perf: 平台tab增加icon 2023-04-19 11:12:26 +08:00
“huailei000”
1149195a23 perf: 创建、更新资产新页卡打开 2023-04-19 11:11:06 +08:00
feng626
800162ce27 Merge pull request #3042 from jumpserver/pr@dev@cloud_sync
fix: 云同步 rdp 端口
2023-04-19 11:03:02 +08:00
feng
9b473a8729 fix: 云同步 rdp 端口 2023-04-19 11:02:16 +08:00
jiangweidong
c553a09cb4 perf: 优化站内信查看已读逻辑 2023-04-19 11:01:56 +08:00
“huailei000”
2702257d71 fix: 修复创建资产登录没有默认值,更新资产登录没有数据问题 2023-04-19 10:53:18 +08:00
feng626
d41162a89b Merge pull request #3038 from jumpserver/pr@dev@platform_bulk_del
perf: 平台不能批量删除
2023-04-18 20:46:19 +08:00
feng
60999a7533 perf: 平台不能批量删除 2023-04-18 20:45:34 +08:00
老广
09f20e8149 Merge pull request #3033 from jumpserver/pr@dev@fix_useracl_form_initial
fix: 修复创建用户登录规则时ip不显示默认值问题
2023-04-18 19:15:16 +08:00
老广
bb631556ba Merge pull request #3036 from jumpserver/pr@dev@perf_import_error
perf: 优化错误显示
2023-04-18 19:13:37 +08:00
ibuler
32f708a014 perf: 优化错误显示 2023-04-18 19:08:17 +08:00
feng626
249cc67140 Merge pull request #3035 from jumpserver/pr@dev@ccount_template
perf: 账号模版按钮 优化
2023-04-18 18:14:17 +08:00
feng
301d3a79b7 perf: 账号模版按钮 优化 2023-04-18 18:12:46 +08:00
“huailei000”
d5d86afed7 fix: 修复创建用户登录规则时ip不显示默认值问题 2023-04-18 16:51:26 +08:00
“huailei000”
6e4ea15d82 fix: 修复更新平台设置弹窗不能打开问题 2023-04-18 16:26:03 +08:00
老广
79aa64c999 Merge pull request #3028 from jumpserver/pr@dev@perf_asset_detail_value
perf: 优化创建平台时表单的初始值
2023-04-18 16:03:43 +08:00
feng626
fa405f4cce Merge pull request #3031 from jumpserver/pr@dev@account_create
perf: 账号单独创建 会主动err
2023-04-18 15:01:39 +08:00
feng
3fa2cfd860 perf: 账号单独创建 会主动err 2023-04-18 14:59:49 +08:00
“huailei000”
6dad655704 perf: 优化平台表单字段显示 2023-04-18 14:41:46 +08:00
“huailei000”
2a6406a8fb perf: 优化账号详情字段显示 2023-04-18 14:02:55 +08:00
“huailei000”
eca3685899 perf: 优化创建平台时表单的初始值 2023-04-17 19:20:19 +08:00
feng626
afc4bea076 Merge pull request #3027 from jumpserver/pr@dev@account_exclude
perf: 账号详情去除params
2023-04-17 17:56:35 +08:00
feng
ace2568028 perf: 账号详情去除params 2023-04-17 17:55:45 +08:00
feng626
0382bab537 Merge pull request #3022 from jumpserver/pr@dev@perf_asset_platform
perf: 修改 platform 更新
2023-04-17 17:51:36 +08:00
feng626
5f535d8bae Merge pull request #3025 from jumpserver/pr@dev@cmd_filter_create_update
fix: 命令过滤无默认值
2023-04-17 16:32:42 +08:00
feng
48fe080009 fix: 命令过滤无默认值 2023-04-17 16:31:16 +08:00
feng626
e4cab9c2a4 Merge pull request #3024 from jumpserver/pr@dev@create_account
fix: 修复资产详情创建账号不刷新
2023-04-17 14:20:33 +08:00
feng
61a6b45051 fix: 修复资产详情创建账号不刷新 2023-04-17 14:19:29 +08:00
ibuler
03cda9a47a perf: 修改 platform 更新 2023-04-17 11:21:24 +08:00
halo
f00f679bd2 fix: 时长字段不显示 2023-04-17 11:11:40 +08:00
“huailei000”
2340efedf5 perf: 调整推送参数helpText 2023-04-14 19:01:01 +08:00
jiangweidong
3d0b722409 feat: 支持部分资源的自定义自动化任务(Ping/VerifyAccount/ChangeSecret) (#3016)
* perf: 自定义指令自动化任务

* feat: 增加自定义改密指令

* perf: 优化

* perf: 优化

* perf: 调换顺序

* perf: 添加国际化
2023-04-14 18:31:35 +08:00
“huailei000”
777d31e562 fix: 修复账号推送参数设置是否可点击逻辑判断;修改表单标题显示;调整自动推送helpText 2023-04-14 18:06:00 +08:00
“huailei000”
47d602302f feat: 账号增加推送设置 2023-04-14 16:23:43 +08:00
feng626
87002430f4 Merge pull request #3013 from jumpserver/pr@dev@fix_form_error
fix:  修复表单报错问题
2023-04-13 19:48:55 +08:00
“huailei000”
1bb0bce869 fix: 修复表单报错问题 2023-04-13 19:36:23 +08:00
ibuler
9e74a49644 perf: 修改支持 list field form 2023-04-13 19:11:00 +08:00
ibuler
29d40f4d54 perf: 修复平台协议删除问题 2023-04-13 19:10:27 +08:00
fit2bot
dd0314d235 perf: vuex增加获取form的方法;修改账号推送 (#2975)
Co-authored-by: “huailei000” <2280131253@qq.com>
Co-authored-by: huailei <31801270+huailei000@users.noreply.github.com>
2023-04-13 19:09:13 +08:00
“huailei000”
fd3260faff feat: 平台增加自动化设置组件 2023-04-13 19:01:54 +08:00
feng626
c05f0275de Merge pull request #3009 from jumpserver/pr@dev@account_template
fix: 账号模版创建账号
2023-04-12 17:58:35 +08:00
feng
ed13ab3fdd fix: 账号模版创建账号 2023-04-12 17:58:03 +08:00
老广
5938ddbb35 Merge pull request #3007 from jumpserver/pr@dev@perf_platform_detail
perf: 修改 平台详情
2023-04-11 19:30:30 +08:00
ibuler
99672fc645 perf: 修改 平台详情 2023-04-11 19:13:15 +08:00
老广
3a73cb900d Merge pull request #3005 from jumpserver/pr@dev@fix_applethost_jumpdetail_error
fix: 修复远程应用发布机跳转到详情报错
2023-04-11 17:04:50 +08:00
“huailei000”
87196e0cd1 fix: 修复远程应用发布机跳转到详情报错 2023-04-11 17:03:53 +08:00
feng626
7c171b3a76 Merge pull request #3004 from jumpserver/pr@dev@perf_asset_list
perf: 优化资产列表
2023-04-11 16:21:00 +08:00
feng626
3694a27752 Merge pull request #3003 from jumpserver/pr@dev@asset_update
perf: asset update 500
2023-04-10 18:12:00 +08:00
feng
b5b6b5227d perf: asset update 500 2023-04-10 18:10:03 +08:00
jiangweidong
5ac365a990 fix: 解决更新手机号不同步的问题 2023-04-10 18:00:07 +08:00
jiangweidong
2361b16c30 fix: 优化手机号区号更新为空及展示问题 2023-04-10 17:13:34 +08:00
fit2bot
023fb03490 perf: 优化创建、更新后所有列表默认排序方式 (#2992)
Co-authored-by: “huailei000” <2280131253@qq.com>
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2023-04-10 17:08:48 +08:00
ibuler
4a0d104a96 perf: 优化资产列表 2023-04-10 17:06:34 +08:00
ibuler
f85351b163 perf: 优化自定义资产显示 2023-04-10 10:58:22 +08:00
ibuler
140ad7cac4 perf: 修改前端页面 2023-04-10 10:58:22 +08:00
ibuler
d0b4b488b1 perf: 优化对自定义资产的支持 2023-04-10 10:58:22 +08:00
ibuler
da69186239 perf: 支持自定义资产 2023-04-10 10:58:22 +08:00
ibuler
2ea6058113 perf: 修改支持自定义资产 2023-04-10 10:58:22 +08:00
jiangweidong
8913a5ea87 perf: 手机区号增加中国台湾地区 2023-04-10 10:27:40 +08:00
jiangweidong
de5ff58d18 feat: 手机号码支持选择区号 2023-04-10 10:27:40 +08:00
feng626
b658e37fcb Merge pull request #2994 from jumpserver/pr@dev@saml2_auth_help_text
perf: 修改saml2 auth help text
2023-04-07 17:45:32 +08:00
feng
99efbd153f perf: 修改saml2 auth help text 2023-04-07 17:44:38 +08:00
feng626
09a0630c6b Merge pull request #2993 from jumpserver/pr@dev@bulk_create_account
perf: 批量添加账号 模版添加 样式修改
2023-04-07 16:16:33 +08:00
feng
cc7dd80a8a perf: 批量添加账号 模版添加 样式修改 2023-04-07 16:15:52 +08:00
老广
a1906fd925 Merge pull request #2973 from jumpserver/pr@dev@feat_windows_winrm
feat: Windows类型资产增加winrm协议
2023-04-07 11:10:07 +08:00
“huailei000”
f48632d7f7 perf: 优化下拉菜单样式 2023-04-07 10:30:33 +08:00
feng626
f3d31a3f0f Merge pull request #2989 from jumpserver/pr@dev@account_push_is_periodic
perf: 账号推送 社区版隐藏定时功能
2023-04-06 10:50:54 +08:00
feng
7aaec2ea43 perf: 账号推送 社区版隐藏定时功能 2023-04-06 10:49:22 +08:00
feng626
40b7dbc211 Merge pull request #2988 from jumpserver/pr@dev@user_secret_key
perf: has_secret_key
2023-04-04 18:03:22 +08:00
feng
1cec999bfd perf: has_secret_key 2023-04-04 17:59:37 +08:00
feng626
b33f598742 Merge pull request #2987 from jumpserver/pr@dev@account_update
perf: 账号创建更新优化
2023-04-04 16:05:10 +08:00
feng
19b2b55051 perf: 账号创建更新优化 2023-04-04 16:03:52 +08:00
fit2bot
b6235ec7dd perf: 优化更多批量操作菜单显示 (#2986)
Co-authored-by: “huailei000” <2280131253@qq.com>
Co-authored-by: huailei <31801270+huailei000@users.noreply.github.com>
2023-04-04 15:36:41 +08:00
feng626
108a39472a Merge pull request #2985 from jumpserver/pr@dev@bulk_account
perf: 批量创建账号添加模版
2023-04-04 15:33:47 +08:00
feng
63c97e18ad perf: 批量创建账号添加模版 2023-04-04 15:30:40 +08:00
feng626
19f46b098a Merge pull request #2983 from jumpserver/pr@dev@perf_account_error
fix: 修复批量添加账号报错不弹窗
2023-04-04 11:40:48 +08:00
ibuler
03ad5239e9 fix: 修复批量添加账号报错不弹窗 2023-04-04 11:39:03 +08:00
老广
720a076648 Merge pull request #2982 from jumpserver/pr@dev@perf_i18n
perf: 修改翻译
2023-04-04 10:57:25 +08:00
ibuler
80eef76604 fix: 修复添加账号报错 2023-04-04 10:56:09 +08:00
ibuler
feb9c65733 perf: 修改翻译 2023-04-04 10:49:03 +08:00
老广
7597112520 Merge pull request #2972 from jumpserver/pr@dev@perf_account_template
perf: 优化账号批量创建
2023-04-04 09:58:42 +08:00
老广
d54044760d Merge branch 'dev' into pr@dev@perf_account_template 2023-04-04 09:52:51 +08:00
Eric
b369d2d22a perf: 移除 code 字段显示 2023-04-03 18:37:35 +08:00
Eric
2e3db496c0 perf: 会话分享记录新增字段 2023-04-03 18:37:35 +08:00
feng626
97e2f23093 Merge pull request #2980 from jumpserver/pr@dev@session_list
fix: 会话记录翻译问题
2023-04-03 18:12:13 +08:00
feng
42b79c3e30 fix: 会话记录翻译问题 2023-04-03 18:10:55 +08:00
feng626
d2fe84a81e Merge pull request #2979 from jumpserver/pr@dev@perf_asset_helptext
perf: 添加asset helptext
2023-04-03 16:28:44 +08:00
feng
c3bb902e67 perf: 添加asset helptext 2023-04-03 16:27:42 +08:00
ibuler
dde98e470b perf: 修改结果避免返回的错误不对 2023-04-03 15:56:23 +08:00
“huailei000”
822fa9714c perf: 创建、更新资产默认按照更新时间排序 2023-04-03 15:53:27 +08:00
“huailei000”
90659afc36 perf: 优化快捷命令-选择命令列表显示 2023-04-03 15:51:23 +08:00
“huailei000”
7310cbe636 perf: 创建资产时添加账号列表列表增加显示字段 2023-04-03 15:50:43 +08:00
“huailei000”
0857007e15 perf: 命令过滤详情展示更多字段信息 2023-04-03 15:48:34 +08:00
ibuler
41b00cb293 perf: 修改账号创建 2023-04-03 15:02:11 +08:00
ibuler
89466a0995 perf: 优化显示结果 2023-03-31 19:30:35 +08:00
jiangweidong
800315fb7c perf: 工单详情界面不显示添加模版 2023-03-30 16:39:26 +08:00
jiangweidong
8b037e8bac perf: 修改默认值 2023-03-30 16:39:26 +08:00
jiangweidong
085f7e68ef fix: 加个return 2023-03-30 16:39:26 +08:00
jiangweidong
5c075a5a7d perf: 工单界面不显示模版添加 2023-03-30 16:39:26 +08:00
jiangweidong
01bc2b838a fix: 修改逻辑 2023-03-30 11:30:11 +08:00
jiangweidong
0d495e979d fix: 修改改端口逻辑 2023-03-30 10:35:11 +08:00
jiangweidong
6d66419fa6 perf: 优化一下 2023-03-29 17:04:56 +08:00
jiangweidong
96f629a1b7 Merge branch 'dev' of https://github.com/jumpserver/lina into pr@dev@feat_windows_winrm 2023-03-29 15:52:34 +08:00
jiangweidong
35df28ea90 feat: windows类型平台支持winrm协议 2023-03-29 15:52:27 +08:00
“huailei000”
ca98a8ee2e perf: 优化快捷命令-内置变量弹窗内容显示 2023-03-29 15:35:56 +08:00
ibuler
dc09364348 perf: 优化账号批量创建 2023-03-29 15:02:01 +08:00
feng
423601d840 perf: asset address required 2023-03-29 14:15:47 +08:00
feng
09ee34f0d3 perf: 修改lian翻译 2023-03-29 14:05:34 +08:00
“huailei000”
98fef43ece perf: 作业中心-执行历史增加作业名称字段显示 2023-03-29 10:32:21 +08:00
ibuler
41bb35f7c6 perf: 修改账号创建 2023-03-28 15:48:11 +08:00
jiangweidong
827339ad0e fix: 提示文字有误 2023-03-28 10:56:36 +08:00
“huailei000”
9dbdc559cd perf: 资产平台http协议页面显示为http(s) 2023-03-24 18:39:03 +08:00
老广
bce328a440 Merge pull request #2961 from jumpserver/pr@dev@perf_assetbulkupdate_dialog
perf: 优化多次批量更新资产修改项显示不全的问题
2023-03-24 18:11:42 +08:00
老广
84e9cf1ac3 Merge pull request #2959 from jumpserver/pr@dev@perf_passwordhistory_id
perf: 账号密码历史记录列表修改唯一的key字段
2023-03-24 18:09:21 +08:00
老广
5fd5834c8d Merge pull request #2956 from jumpserver/pr@dev@perf_detail_delete_fail_tip
perf:优化RelationCard组件删除之后默认错误处理
2023-03-24 18:08:35 +08:00
老广
73a783bb97 Merge pull request #2964 from jumpserver/pr@dev@device_for_su_from
perf: device 支持账号切换
2023-03-24 18:06:19 +08:00
ibuler
1a5dd023e8 perf: device 支持账号切换 2023-03-24 18:01:59 +08:00
“huailei000”
e4e011e183 fix: 修复资产树连续创建节点后连续修改节点名称,只有最后一个节点名称能修改成功的问题 2023-03-24 16:00:07 +08:00
feng
4b594290e9 feat: 收集账号 可选同步表 2023-03-24 15:26:11 +08:00
“huailei000”
c71a0f2a75 perf: 账号密码历史记录列表修改唯一的key字段 2023-03-23 17:04:56 +08:00
“huailei000”
fd6ab841bf perf: 优化多次批量更新资产修改项显示不全的问题 2023-03-23 16:36:21 +08:00
feng626
2cf27f08b9 Merge pull request #2960 from jumpserver/pr@dev@clear_secret
perf: clear secret
2023-03-23 16:05:27 +08:00
feng
97f531bbec perf: clear secret 2023-03-23 16:01:12 +08:00
feng626
ac7d5e216e Merge pull request #2958 from jumpserver/pr@dev@sync_accounts
feat: 账号收集批量同步账号
2023-03-23 15:24:36 +08:00
feng
ee8b373e77 feat: 账号收集批量同步账号 2023-03-23 15:22:02 +08:00
“huailei000”
ecd1da5fc4 perf: 优化翻译 2023-03-23 11:29:39 +08:00
“huailei000”
cdcf340a75 perf:优化RelationCard组件删除之后默认错误处理 2023-03-23 11:25:58 +08:00
feng626
d8941c14b8 Merge pull request #2955 from jumpserver/pr@dev@remove_secret
feat: account remove secret
2023-03-23 11:21:01 +08:00
feng
bb3ca059b0 feat: account remove secret 2023-03-23 11:18:40 +08:00
老广
d994a01a10 Merge pull request #2954 from jumpserver/pr@dev@perf_platform_create
perf: 优化平台创建
2023-03-22 19:41:55 +08:00
ibuler
bb432dff18 perf: 修改 platform 2023-03-22 19:40:59 +08:00
ibuler
9ce7311f6d perf: 优化平台创建 2023-03-22 19:27:29 +08:00
“huailei000”
a468e04bad perf: 抽离公共复制方法;账号详情增加复制功能 2023-03-22 16:37:46 +08:00
老广
a5dcc8be52 Merge pull request #2950 from jumpserver/pr@dev@perf_platform_protocols
perf: 平台更改协议
2023-03-22 16:26:27 +08:00
ibuler
f905ed0065 perf: 优化代码写法,缩进以及函数 2023-03-22 16:13:42 +08:00
ibuler
0f80375242 perf: 去掉 debug console 2023-03-22 16:07:54 +08:00
feng626
ed8c9df4c3 Merge pull request #2952 from jumpserver/pr@dev@user_asset_detail_add_id
perf: user asset detail add id
2023-03-22 16:02:21 +08:00
feng
7bc9f2f75b perf: user asset detail add id 2023-03-22 16:01:28 +08:00
ibuler
b0c635463a perf: 平台更改协议 2023-03-22 14:22:12 +08:00
老广
5ccf713ef3 Merge pull request #2949 from jumpserver/pr@dev@fix_web_helptext
fix: Web资产提示有误
2023-03-22 14:02:15 +08:00
jiangweidong
16e92e925c fix: Web资产提示有误 2023-03-22 14:01:08 +08:00
feng626
a2ec7d3ccd Merge pull request #2948 from jumpserver/pr@dev@bulk_create_account
perf: 批量创建账号
2023-03-22 11:09:26 +08:00
feng
ae3e072275 perf: 批量创建账号 2023-03-22 11:07:30 +08:00
ibuler
9e195dd1d9 perf: 优化平台协议 2023-03-21 17:54:32 +08:00
老广
8432299660 Merge pull request #2947 from jumpserver/pr@dev@perf_import_reloadtable
perf: 导入数据成功后自动刷新列表
2023-03-21 10:23:27 +08:00
“huailei000”
b9f41570bf perf: 导入数据成功后自动刷新列表 2023-03-21 09:58:12 +08:00
老广
5ead251ea7 Merge pull request #2690 from jumpserver/pr@dev@perf_task_route
perf: 迁移到了系统设置中
2023-03-20 10:37:22 +08:00
ibuler
fc10a77372 perf: 已启动系统任务到系统设置中 2023-03-20 10:35:46 +08:00
ibuler
41ef8c6e97 perf: 持久化存储 pre org,避免别的 tab 切换到系统设置中,当前组织失效 2023-03-17 16:49:10 +08:00
Bai
ef0658de24 fix: 修复报错信息显示, 特殊处理500错误 2023-03-17 16:45:41 +08:00
Bai
fedb068221 fix: 修复 LAN 账号保存 Platform 问题 2023-03-17 15:44:46 +08:00
老广
5a66c39313 Merge pull request #2940 from jumpserver/pr@dev@fix_perms
fix: 修复授权规则创建账号模版弹窗取消按钮不生效问题
2023-03-17 15:29:11 +08:00
Bai
ce6ca288b7 fix: 修复授权规则创建账号模版弹窗取消按钮不生效问题 2023-03-17 14:55:22 +08:00
feng626
65541a7001 Merge pull request #2939 from jumpserver/pr@dev@asset_bulk_update
perf: 资产全局导入提示及批量更新平台
2023-03-16 18:24:54 +08:00
feng
24a5c6be43 perf: 资产全局导入提示及批量更新平台 2023-03-16 18:22:51 +08:00
“huailei000”
020c7ec9fa perf: ldap导入用户列表-组织下拉框设置最大宽度 2023-03-16 16:44:15 +08:00
Bai
f12707d958 fix: 修复创建资产添加账号模版报错问题 2023-03-16 12:54:16 +08:00
“huailei000”
82c2e20fce perf: message 2023-03-16 11:22:11 +08:00
老广
11f0ee12cf Merge pull request #2929 from jumpserver/pr@dev@perf_add_default_security
perf: 添加默认的信息
2023-03-16 10:50:12 +08:00
ibuler
ca64c0b219 perf: 添加默认的信息 2023-03-16 10:49:14 +08:00
“huailei000”
e34c6fc843 perf: 导出优化判断 2023-03-15 19:42:47 +08:00
老广
51e139712d Merge pull request #2926 from jumpserver/pr@dev@perf_table_export_mfa
perf: 优化列表导出mfa验证弹窗逻辑
2023-03-15 19:30:34 +08:00
“huailei000”
9daf001312 perf: 优化列表导出mfa验证弹窗逻辑 2023-03-15 11:28:49 +00:00
Eric
27353ee0c2 perf: 监控页 Tinker 组件名称显示 2023-03-15 19:02:17 +08:00
feng626
b6643d89c0 Merge pull request #2923 from jumpserver/pr@dev@endpoint
perf: endpoint rules list
2023-03-15 18:23:50 +08:00
feng
9503586b8a perf: endpoint rules list 2023-03-15 18:20:38 +08:00
老广
317735c69a Merge pull request #2921 from jumpserver/pr@dev@user_password_expire
fix: 用户密码即将到期不提示
2023-03-15 17:37:54 +08:00
老广
5c26000f90 Merge pull request #2920 from jumpserver/pr@dev@perf_platform_add_asset_constrains
perf: 修改账号更改平台的约束
2023-03-15 16:49:43 +08:00
feng
139a47b858 fix: 用户密码即将到期不提示 2023-03-15 16:19:25 +08:00
ibuler
2f2e05101b perf: 修改账号更改平台的约束 2023-03-15 16:15:37 +08:00
Bai
36f7d9711d fix: 修复windows账号详情和更新账号时 su-from 字段的控制 2023-03-15 15:38:12 +08:00
Bai
4e89c8d53f fix: 优化账号模版可以根据 protocols 过滤 secret_type 字段 2023-03-15 14:48:37 +08:00
老广
5902475778 Merge pull request #2911 from jumpserver/pr@dev@fix_createasset_account_editsecret
fix: 修复创建资产-添加账号后编辑账号密文为空的问题
2023-03-14 20:05:47 +08:00
“huailei000”
abdba774cc perf: 全局添加刷新方法;账号详情增加更新操作 2023-03-14 20:04:49 +08:00
ibuler
c1d10322cb perf: 优化导入导出 2023-03-14 20:04:21 +08:00
ibuler
90fdf4982e perf: 优化账号导出 2023-03-14 20:04:21 +08:00
ibuler
f5e5d431c7 perf: 优化系统组织切换的问题 2023-03-14 20:03:58 +08:00
“huailei000”
9d9fb51a7d perf: 优化控制台增长数据显示 2023-03-14 20:03:06 +08:00
“huailei000”
997b5d8d19 fix: 修复创建资产-添加账号后编辑账号密文为空的问题 2023-03-14 15:37:12 +08:00
feng626
ea19dd8e0a Merge pull request #2908 from jumpserver/pr@dev@hardware
fix: 主机硬件信息 多次请求及warning
2023-03-13 23:20:25 +08:00
feng
983663a3fa fix: 主机硬件信息 多次请求及warning 2023-03-13 23:17:47 +08:00
feng626
3809519331 Merge pull request #2906 from jumpserver/pr@dev@ticket_push_account
perf: 工单推荐账号过滤组织
2023-03-13 18:43:55 +08:00
feng
fe7d20778d perf: 工单推荐账号过滤组织 2023-03-13 18:39:59 +08:00
老广
f6bf8b3193 Merge pull request #2902 from jumpserver/pr@dev@feat_component_uploadsecret
feat: 增加上传密钥组件
2023-03-13 18:36:29 +08:00
老广
5d5ee3cdaa Merge pull request #2905 from jumpserver/pr@dev@change_file_name
perf: 修改账号备份
2023-03-13 18:29:17 +08:00
ibuler
ed4da2ab50 perf: 修改账号备份 2023-03-13 18:26:39 +08:00
“huailei000”
9c8ed912bb perf: 账号列表默认按照更新日期排序 2023-03-13 17:28:21 +08:00
feng626
c58551adea Merge pull request #2903 from jumpserver/pr@dev@gateway
fix: 创建网关找不到平台
2023-03-13 16:50:48 +08:00
feng
f44618d915 fix: 创建网关找不到平台 2023-03-13 16:48:44 +08:00
“huailei000”
aeb26c748a feat: 增加上传密钥组件 2023-03-13 16:40:54 +08:00
ibuler
c75f0b1312 perf: 修改 import expore 2023-03-10 19:26:02 +08:00
ibuler
6138fe2e35 perf: 优化导入导出 2023-03-10 19:26:02 +08:00
feng
8d3f99392f perf: 硬件信息浮动延迟 2023-03-10 15:41:13 +08:00
Bai
2fd4792ed4 feat: 支持飞书国际版(lark) 2023-03-10 15:13:42 +08:00
“huailei000”
757a822e34 perf: 增加license显示信息 2023-03-10 11:04:24 +08:00
feng626
03ffe4a911 Merge pull request #2893 from jumpserver/pr@dev@hardware_show
fix: 修复硬件信息undefined
2023-03-09 17:16:43 +08:00
feng
3b83ecd85d fix: 修复硬件信息undefined 2023-03-09 17:15:35 +08:00
老广
80145dc114 Merge pull request #2892 from jumpserver/pr@dev@perf_asset_address_not_rule
perf: 资产-地址前端不做限制
2023-03-09 13:01:40 +08:00
老广
5612f432a5 Merge pull request #2884 from jumpserver/pr@dev@fix_taskperms
fix: 修改任务列表权限控制
2023-03-09 13:01:06 +08:00
“huailei000”
023fd55a70 perf: 资产-地址前端不做限制 2023-03-09 05:00:44 +00:00
老广
897f3881b2 Merge pull request #2889 from jumpserver/pr@dev@perf_asset_ip_rule
perf: 优化创建资产url正则校验
2023-03-09 12:48:31 +08:00
老广
730aa548d4 Merge pull request #2887 from jumpserver/pr@dev@perf_table_drag-border
perf: 优化table列表拖动效果
2023-03-09 12:48:02 +08:00
feng626
f1a3d775cc Merge pull request #2890 from jumpserver/pr@dev@host_hardware_info
perf: host hardware
2023-03-09 11:13:06 +08:00
feng
71d79ec6ef perf: host hardware 2023-03-09 11:12:10 +08:00
“huailei000”
4118a21d1d perf: 优化创建资产url正则校验 2023-03-09 02:20:41 +00:00
feng626
be87eec4e5 Merge pull request #2869 from jumpserver/pr@dev@accounts
perf: 优化 perms 中可以关联账号模版
2023-03-08 18:52:15 +08:00
feng
230433de05 perf: 授权添加账号模版 2023-03-08 18:31:20 +08:00
“huailei000”
e3fdf1cc41 perf: 优化table列表拖动效果 2023-03-08 18:04:18 +08:00
Bai
f1f4ba5dfa fix: 修改任务列表权限控制 2023-03-08 09:15:46 +00:00
“huailei000”
d9d5de5102 perf: 调整其它设置表单显示内容 2023-03-08 16:34:25 +08:00
吴小白
7bf74d8727 Merge pull request #2873 from jumpserver/pr@dev@perf_upload_gitconfig
perf: work flow
2023-03-08 15:49:14 +08:00
Eric
937bdcf5c7 perf: 增加 IGNORE_VERIFY_CERTS 字段 2023-03-08 14:47:29 +08:00
Bai
0a86240f90 fix: 修复组织管理员打开用户列表报错的问题 2023-03-08 14:40:49 +08:00
“huailei000”
ea3c28791d perf: work flow 2023-03-08 13:17:55 +08:00
老广
1ff829182b Merge pull request #2874 from jumpserver/pr@dev@perf_log_diff_performence
perf: 修改活动记录详情中的表现
2023-03-08 10:39:11 +08:00
ibuler
23fea1bf99 perf: 修改活动记录详情中的表现
perf: 优化操作日志 dialog
2023-03-08 10:36:07 +08:00
“huailei000”
496605c539 perf: 优化控制台仪表盘排名列表数据显示 2023-03-07 11:35:08 +08:00
ibuler
051784e52b perf: 优化 perms 中可以关联账号模版 2023-03-07 11:01:04 +08:00
“huailei000”
dcd6fa977d perf: 调整logo对齐 2023-03-06 15:21:43 +08:00
ibuler
411987fb69 perf: 修改授权中账号选择 2023-03-02 19:41:26 +08:00
“huailei000”
ac88b70b1c perf: table列表增加拖动效果 2023-03-02 18:51:46 +08:00
“huailei000”
36e053068e perf: 调整账号详情ssh指纹显示 2023-03-02 16:44:50 +08:00
“huailei000”
7944c4a414 perf: 优化移动端模式下菜单样式 2023-03-02 11:08:00 +08:00
Aaron3S
325c2b45e4 fix: 修改权限依赖 2023-03-01 18:40:17 +08:00
“huailei000”
477fd5164e perf: 优化资产列表控制台报错问题 2023-03-01 16:14:29 +08:00
Bai
acfdb04b09 fix: 修改RDS许可证配置 2023-03-01 15:21:27 +08:00
“huailei000”
50073f0512 perf: 优化通告组件链接样式 2023-03-01 13:55:42 +08:00
“huailei000”
6ab61215b5 perf: 优化选择平台弹窗样式 2023-02-28 19:09:39 +08:00
Aaron3S
b5d4bfd488 feat: 增加按钮提示 2023-02-28 18:46:47 +08:00
feng626
24297dbead Merge pull request #2845 from jumpserver/pr@dev@hardinfo
perf: 主机硬件信息
2023-02-28 18:46:06 +08:00
Aaron3S
ad0c17610e fix: 增加运行用户为空时的提示 2023-02-28 18:30:47 +08:00
feng
a54f6f3a89 perf: 主机硬件信息 2023-02-28 18:30:39 +08:00
老广
3212260d8e Merge pull request #2835 from jumpserver/pr@dev@perf_ztree_overflow
perf: 优化 ztree overflow
2023-02-28 18:19:57 +08:00
Aaron3S
8ec7cd3c3c fix: 修复作业无法执行的问题 2023-02-28 18:09:56 +08:00
“huailei000”
32c09c896c perf: 优化资产树滚动条样式 2023-02-28 18:00:28 +08:00
“huailei000”
71c1d77498 perf: 优化移动端下切换组织组件样式 2023-02-28 17:28:27 +08:00
ibuler
06fc05e6b3 perf: 优化 ztree overflow 2023-02-28 17:12:36 +08:00
老广
6b491ef696 Merge pull request #2831 from jumpserver/pr@dev@fix_version
fix: 修正版本获取不正确
2023-02-28 17:01:41 +08:00
“huailei000”
5e23ae7926 perf: 优化移动端样式 2023-02-28 16:45:58 +08:00
吴小白
f62c6b980e fix: 修正版本获取不正确 2023-02-28 16:36:48 +08:00
老广
d930e09065 Merge pull request #2830 from jumpserver/pr@dev@perf_ztree_height
perf: 优化 tree 高度
2023-02-28 16:30:05 +08:00
ibuler
10a6dae2cf perf: 优化 tree 高度 2023-02-28 08:09:21 +00:00
“huailei000”
c099a16a49 perf: about dialog 2023-02-28 14:00:57 +08:00
feng626
198e6a11d2 Merge pull request #2822 from jumpserver/pr@dev@push_account_community
perf: 推送账号 社区版定时任务关闭
2023-02-28 13:35:30 +08:00
ibuler
8c66e20961 perf: 优化 关于页面 2023-02-28 13:34:51 +08:00
feng
0393128be5 perf: 推送账号 社区版定时任务关闭 2023-02-28 11:41:39 +08:00
Aaron3S
69ff6be5f4 fix: 修复 term 组件上的按钮被覆盖的问题 2023-02-27 18:35:44 +08:00
老广
8685362344 Merge pull request #2815 from jumpserver/pr@dev@fix_platform_create
perf: 修改 assetMeta 调用
2023-02-27 17:56:36 +08:00
ibuler
7ea68e9a09 perf: 修改 assetMeta 调用 2023-02-27 09:46:30 +00:00
ibuler
ebede00841 fix: 修复平台创建 2023-02-27 17:45:39 +08:00
feng
73ce298d6a perf: lina 提示优化 2023-02-27 16:28:23 +08:00
Aaron3S
69cd561dc0 fix: 暂时隐藏参数设置字段 2023-02-27 16:11:28 +08:00
老广
7af1a55e2a Merge pull request #2804 from jumpserver/revert-2798-pr@dev@lina_point_out
Revert "perf: lina 提示优化"
2023-02-27 15:57:06 +08:00
feng626
c1272d9a9f Revert "perf: lina 提示优化" 2023-02-27 15:56:30 +08:00
ibuler
f3e28f7cd4 perf: 平台允许修改 2023-02-27 15:51:32 +08:00
ibuler
412a7ceba1 perf: 添加accounts 搜索建议 2023-02-27 15:20:37 +08:00
feng626
73bf34c422 Merge pull request #2798 from jumpserver/pr@dev@lina_point_out
perf: lina 提示优化
2023-02-27 13:29:11 +08:00
feng
3d2b139214 perf: lina 提示优化 2023-02-27 13:27:41 +08:00
ibuler
658f0ff587 perf: 优化 applet host 请求引起的 eror 2023-02-26 12:38:12 +08:00
“huailei000”
621893fdcc fix: 修复快捷命令操作按钮不能点击问题 2023-02-24 17:53:53 +08:00
feng626
a6e94b4173 Merge pull request #2792 from jumpserver/pr@dev@add_account_asset_required
perf: add account asset required
2023-02-24 16:27:32 +08:00
feng
f9098c3203 perf: add account asset required 2023-02-24 16:22:36 +08:00
“huailei000”
b1c0218ad9 perf: 优化账号列表查看详情MFA弹窗没有关闭问题 2023-02-24 14:47:56 +08:00
“huailei000”
4058843b1a perf: 平台详情增加disabled判断 2023-02-24 11:10:50 +08:00
ibuler
d144cd6809 perf: 迁移到了系统设置中 2023-02-17 18:38:55 +08:00
684 changed files with 33137 additions and 10498 deletions

View File

@@ -22,4 +22,5 @@ VUE_APP_LOGOUT_PATH = '/core/auth/logout/'
# Dev server for core proxy
VUE_APP_CORE_HOST = 'http://localhost:8080'
VUE_APP_CORE_WS = 'ws://localhost:8080'
VUE_APP_KAEL_HOST = 'http://localhost:8083'
VUE_APP_ENV = 'development'

View File

@@ -10,3 +10,4 @@ jobs:
- uses: jumpserver/action-generic-handler@master
env:
GITHUB_TOKEN: ${{ secrets.PRIVATE_TOKEN }}
I18N_TOKEN: ${{ secrets.I18N_TOKEN }}

View File

@@ -19,9 +19,7 @@ jobs:
id: get_version
run: |
TAG=$(basename ${GITHUB_REF})
VERSION=${TAG/v/}
echo "::set-output name=TAG::$TAG"
echo "::set-output name=VERSION::$VERSION"
- name: Create Release
id: create_release
uses: release-drafter/release-drafter@v5
@@ -31,16 +29,29 @@ jobs:
config-name: release-config.yml
version: ${{ steps.get_version.outputs.TAG }}
tag: ${{ steps.get_version.outputs.TAG }}
build-and-release:
needs: create-realese
name: Build and Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build it and upload
uses: jumpserver/action-build-upload-assets@node10
- uses: actions/setup-node@v2
with:
node-version: '16.20'
- name: Install dependencies
run: yarn install
- name: Build web
run: |
sed -i "s@version-dev@${{ steps.get_version.outputs.TAG }}@g" src/layout/components/NavHeader/About.vue
yarn build
- name: Create Upload Assets
run: |
rm -rf build/*
mv lina lina-${{ steps.get_version.outputs.TAG }}
tar -czf lina-${{ steps.get_version.outputs.TAG }}.tar.gz lina-${{ steps.get_version.outputs.TAG }}
echo $(md5sum lina-${{ steps.get_version.outputs.TAG }}.tar.gz | awk '{print $1}') > build/lina-${{ steps.get_version.outputs.TAG }}.tar.gz.md5
mv lina-${{ steps.get_version.outputs.TAG }}.tar.gz build/
- name: Release Upload Assets
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
draft: true
files: |
build/lina-${{ steps.get_version.outputs.TAG }}.tar.gz
build/lina-${{ steps.get_version.outputs.TAG }}.tar.gz.md5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-realese.outputs.upload_url }}

View File

@@ -1,13 +1,28 @@
FROM node:14.16 as stage-build
FROM node:16.20-bullseye-slim as stage-build
ARG TARGETARCH
ARG DEPENDENCIES=" \
g++ \
make \
python3"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=lina \
sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& rm -f /etc/apt/apt.conf.d/docker-clean \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update \
&& apt-get install -y --no-install-recommends ${DEPENDENCIES} \
&& echo "no" | dpkg-reconfigure dash \
&& rm -rf /var/lib/apt/lists/*
ARG NPM_REGISTRY="https://registry.npmmirror.com"
WORKDIR /data
RUN set -ex \
&& npm config set registry ${NPM_REGISTRY} \
&& yarn config set registry ${NPM_REGISTRY}
WORKDIR /data
ADD package.json yarn.lock /data
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn,sharing=locked,id=lina \
yarn install
@@ -16,9 +31,9 @@ ARG VERSION
ENV VERSION=$VERSION
ADD . /data
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn,sharing=locked,id=lina \
sed -i "s@<strong> version-dev </strong>@<strong> ${VERSION} </strong>@g" src/layout/components/NavHeader/About.vue \
sed -i "s@version-dev@${VERSION}@g" src/layout/components/NavHeader/About.vue \
&& yarn build
FROM nginx:alpine
FROM nginx:1.24-bullseye
COPY --from=stage-build /data/lina /opt/lina
COPY nginx.conf /etc/nginx/conf.d/default.conf

View File

@@ -20,26 +20,29 @@
"vue-i18n-report": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json'",
"vue-i18n-report-json": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -o /tmp/abc.json",
"vue-i18n-report-add-miss": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -a",
"diff-i18n": "python ./src/i18n/langs/i18n-util.py diff en ja",
"apply-i18n": "python ./src/i18n/langs/i18n-util.py apply en ja"
"diff-i18n": "python ./src/i18n/langs/i18n-util.py diff en ja zh_Hant",
"apply-i18n": "python ./src/i18n/langs/i18n-util.py apply en ja zh_Hant"
},
"dependencies": {
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
"@traptitech/markdown-it-katex": "^3.6.0",
"@ztree/ztree_v3": "3.5.44",
"axios": "0.21.1",
"axios": "0.28.0",
"axios-retry": "^3.1.9",
"cron-parser": "^4.0.0",
"crypto-js": "^4.1.1",
"css-color-function": "^1.3.3",
"decimal.js": "^10.4.3",
"deepmerge": "^4.2.2",
"echarts": "^4.7.0",
"dompurify": "^3.1.6",
"echarts": "4.7.0",
"element-ui": "2.13.2",
"eslint-plugin-html": "^6.0.0",
"highlight.js": "^11.9.0",
"install": "^0.13.0",
"jquery": "^3.6.1",
"js-cookie": "2.2.0",
"jsencrypt": "^3.2.1",
"krry-transfer": "^1.7.3",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"lodash": "^4.17.21",
@@ -54,6 +57,8 @@
"lodash.set": "^4.3.2",
"lodash.topairs": "^4.3.0",
"lodash.values": "^4.3.0",
"markdown-it": "^13.0.2",
"markdown-it-link-attributes": "^4.0.1",
"moment": "^2.29.4",
"moment-parseformat": "^4.0.0",
"normalize.css": "7.0.0",
@@ -79,7 +84,7 @@
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@babel/core": "7.0.0",
"@babel/core": "7.18.6",
"@babel/register": "7.0.0",
"@vue/cli-plugin-babel": "3.6.0",
"@vue/cli-plugin-eslint": "^3.9.1",
@@ -93,6 +98,7 @@
"chalk": "2.4.2",
"compression-webpack-plugin": "^6.1.1",
"connect": "3.6.6",
"deasync": "^0.1.29",
"element-theme-chalk": "^2.13.1",
"eslint": "^5.15.3",
"eslint-plugin-vue": "5.2.2",
@@ -109,6 +115,7 @@
"script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "0.7.2",
"serve-static": "^1.13.2",
"strip-ansi": "^7.1.0",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.2",
"vue-i18n-extract": "^1.1.1",

View File

@@ -27,6 +27,9 @@
if(pathname.indexOf('/ui') === -1) {
window.location.href = window.location.origin + '/ui/#' + pathname
}
if (pathname.startsWith('/ui/#/chat')) {
window.location.href = window.location.origin + pathname
}
}
</script>
<div id="app"></div>

View File

@@ -424,7 +424,7 @@ td .el-button.el-button--mini {
}
.el-dialog .el-dialog__body {
max-height: 90vh;
max-height: 80vh;
overflow: auto;
padding: 30px;
}
@@ -496,3 +496,11 @@ td .el-button.el-button--mini {
.el-alert.el-alert--error.is-light {
border-color: var(--color-danger-light);
}
#nprogress .bar {
background: light-5!important;
}
#nprogress .peg {
box-shadow: 0 0 10px light-5, 0 0 5px light-5!important;
}

View File

@@ -1,12 +1,19 @@
<template>
<div id="app">
<router-view />
<router-view v-if="isRouterAlive" />
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'App'
name: 'App',
computed: {
...mapState({
isRouterAlive: state => state.common.isRouterAlive
})
}
}
</script>

View File

@@ -42,8 +42,10 @@ export function getCommandFilterList(data) {
export function getCategoryTypes() {
return request({
url: '/api/v1/assets/categories/',
url: '/api/v1/assets/categories/?limit=1000',
method: 'get'
}).then(res => {
return res.results
})
}

View File

@@ -44,3 +44,30 @@ export function renameFile(playbookId, node) {
data: node
})
}
export function createJob(form) {
return request({
url: '/api/v1/ops/jobs/',
method: 'post',
data: form
})
}
export function StopJob(form) {
return request({
url: '/api/v1/ops/job-executions/stop/',
method: 'post',
data: form
})
}
export function JobUploadFile(form) {
return request({
url: '/api/v1/ops/jobs/upload/',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 60 * 60 * 1000,
data: form
})
}

View File

@@ -8,24 +8,11 @@ export function terminateSession(data) {
})
}
export function getSessionDetail(id) {
export function toggleLockSession(data) {
return request({
url: `/api/v1/terminal/sessions/${id}/`,
method: 'get'
})
}
export function getSessionCommands(id) {
return request({
url: `/api/v1/terminal/commands/?session_id=${id}`,
method: 'get'
})
}
export function getTerminalDetail(id) {
return request({
url: `/api/v1/terminal/terminals/${id}/`,
method: 'get'
url: '/api/v1/terminal/tasks/toggle-lock-session/',
method: 'post',
data: data
})
}

View File

@@ -25,12 +25,28 @@ export function importLicense(formData) {
data: formData
})
}
export function testLdapSetting(data) {
return request({
disableFlashErrorMsg: true,
url: '/api/v1/settings/ldap/testing/config/',
method: 'post',
data: data
export function testLdapSetting(data, refresh = true) {
let url = '/api/v1/settings/ldap/testing/config/'
if (refresh) {
url = url + '?refresh=1'
}
return new Promise((resolve, reject) => {
request({
disableFlashErrorMsg: true,
url: url,
method: 'post',
data: data
}).then(res => {
if (res.status !== 'running') {
resolve(res)
} else {
setTimeout(() => {
resolve(testLdapSetting(data, false))
}, 1000)
}
}).catch(error => {
reject(error)
})
})
}

View File

@@ -12,7 +12,6 @@ export function getProfile(token) {
return request({
url: '/api/v1/users/profile/',
method: 'get'
// params: { token }
})
}

BIN
src/assets/img/chat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="transparent" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bot "><path d="M12 8V4H8"></path><rect width="16" height="12" x="4" y="8" rx="2"></rect><path d="M2 14h2"></path><path d="M20 14h2"></path><path d="M15 13v2"></path><path d="M9 13v2"></path></svg>

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,256 +0,0 @@
<template>
<AutoDataForm
v-if="!loading"
ref="AutoDataForm"
v-bind="$data"
@submit="confirm"
/>
</template>
<script>
import AutoDataForm from '@/components/AutoDataForm'
import { UpdateToken } from '@/components/FormFields'
import Select2 from '@/components/FormFields/Select2'
import AssetSelect from '@/components/AssetSelect'
import { encryptPassword } from '@/utils/crypto'
import { RequiredChange } from '@/components/DataForm/rules'
export default {
name: 'AccountCreateForm',
components: {
AutoDataForm
},
props: {
asset: {
type: Object,
default: null
},
platform: {
type: Object,
default: null
},
account: {
type: Object,
default: null
},
// 默认组件密码加密
encryptPassword: {
type: Boolean,
default: true
}
},
data() {
return {
loading: true,
usernameChanged: false,
defaultPrivilegedAccounts: ['root', 'administrator'],
iPlatform: {
automation: {},
protocols: [
{
name: 'ssh',
secret_types: ['password', 'ssh_key', 'token', 'api_key']
}
]
},
url: '/api/v1/accounts/accounts/',
form: this.account || {},
encryptedFields: ['secret'],
fields: [
[this.$t('assets.Asset'), ['assets']],
[this.$t('common.Basic'), ['name', 'username', ...this.controlShowField()]],
[this.$t('assets.Secret'), [
'secret_type', 'secret', 'ssh_key', 'token',
'api_key', 'passphrase'
]],
[this.$t('common.Other'), ['push_now', 'is_active', 'comment']]
],
fieldsMeta: {
assets: {
component: AssetSelect,
label: this.$t('assets.Asset'),
el: {
multiple: false
},
hidden: () => {
return this.platform || this.asset
}
},
name: {
rules: [RequiredChange],
on: {
input: ([value], updateForm) => {
if (!this.usernameChanged) {
if (!this.account?.name) {
updateForm({ username: value })
}
const maybePrivileged = this.defaultPrivilegedAccounts.includes(value)
if (maybePrivileged) {
updateForm({ privileged: true })
}
}
}
}
},
username: {
el: {
disabled: !!this.account?.name
},
on: {
input: ([value], updateForm) => {
this.usernameChanged = true
},
change: ([value], updateForm) => {
const maybePrivileged = this.defaultPrivilegedAccounts.includes(value)
if (maybePrivileged) {
updateForm({ privileged: true })
}
}
}
},
su_from: {
component: Select2,
hidden: (formValue) => {
return !this.asset?.id
},
el: {
multiple: false,
clearable: true,
ajax: {
url: `/api/v1/accounts/accounts/su-from-accounts/?account=${this.account?.id || ''}&asset=${this.asset?.id || ''}`,
transformOption: (item) => {
return { label: `${item.name}(${item.username})`, value: item.id }
}
}
}
},
secret: {
label: this.$t('assets.Password'),
component: UpdateToken,
hidden: (formValue) => formValue.secret_type !== 'password'
},
ssh_key: {
label: this.$t('assets.PrivateKey'),
el: {
type: 'textarea',
rows: 4
},
hidden: (formValue) => formValue.secret_type !== 'ssh_key'
},
passphrase: {
label: this.$t('assets.Passphrase'),
component: UpdateToken,
hidden: (formValue) => formValue.secret_type !== 'ssh_key'
},
token: {
label: this.$t('assets.Token'),
el: {
type: 'textarea',
rows: 4
},
hidden: (formValue) => formValue.secret_type !== 'token'
},
api_key: {
id: 'api_key',
label: this.$t('assets.AccessKey'),
el: {
type: 'textarea',
rows: 4
},
hidden: (formValue) => formValue.secret_type !== 'api_key'
},
secret_type: {
type: 'radio-group',
options: []
},
push_now: {
hidden: () => {
const automation = this.iPlatform.automation || {}
return !automation.push_account_enabled || !automation.ansible_enabled || !this.$hasPerm('accounts.push_account')
}
}
},
hasSaveContinue: false
}
},
async mounted() {
try {
await this.getPlatform()
this.setSecretTypeOptions()
} finally {
this.loading = false
}
},
methods: {
async getPlatform() {
if (this.platform) {
this.iPlatform = this.platform
}
if (!this.asset || !this.asset.platform) {
return
}
const platformId = this.asset.platform.id
this.iPlatform = await this.$axios.get(`/api/v1/assets/platforms/${platformId}/`)
},
setSecretTypeOptions() {
const choices = [
{
label: this.$t('assets.Password'),
value: 'password'
},
{
label: this.$t('assets.SSHKey'),
value: 'ssh_key'
},
{
label: this.$t('assets.Token'),
value: 'token'
},
{
label: this.$t('assets.AccessKey'),
value: 'api_key'
}
]
const secretTypes = []
this.iPlatform.protocols?.forEach(p => {
secretTypes.push(...p['secret_types'])
})
if (!this.form.secret_type) {
this.form.secret_type = secretTypes[0]
}
this.fieldsMeta.secret_type.options = choices.filter(item => {
return secretTypes.indexOf(item.value) > -1
})
},
controlShowField() {
const privileged = ['privileged']
let suFrom = ['su_from']
const filterSuFrom = ['database', 'device', 'cloud', 'web']
const asset = this?.asset || {}
if (filterSuFrom.includes(asset?.category?.value)) {
suFrom = []
}
return [...privileged, ...suFrom]
},
confirm(form) {
const secretType = form.secret_type || ''
if (secretType !== 'password') {
form.secret = form[secretType]
delete form[secretType]
}
form.secret = this.encryptPassword ? encryptPassword(form.secret) : form.secret
if (!form.secret) {
delete form['secret']
}
if (this.account?.name) {
this.$emit('edit', form)
} else {
this.$emit('add', form)
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,191 @@
import { UpdateToken, UploadSecret } from '@/components/Form/FormFields'
import Select2 from '@/components/Form/FormFields/Select2.vue'
import AssetSelect from '@/components/Apps/AssetSelect/index.vue'
import { Required, RequiredChange } from '@/components/Form/DataForm/rules'
import AutomationParamsForm from '@/views/assets/Platform/AutomationParamsSetting.vue'
export const accountFieldsMeta = (vm) => {
const defaultPrivilegedAccounts = ['root', 'administrator']
return {
assets: {
rules: [Required],
component: AssetSelect,
label: vm.$t('assets.Asset'),
el: {
multiple: false
},
hidden: () => {
return vm.platform || vm.asset
}
},
template: {
component: Select2,
rules: [Required],
el: {
multiple: false,
ajax: {
url: '/api/v1/accounts/account-templates/',
transformOption: (item) => {
return { label: item.name, value: item.id }
}
}
},
hidden: () => {
return vm.platform || vm.asset || !vm.addTemplate
}
},
on_invalid: {
rules: [Required],
label: vm.$t('accounts.AccountPolicy'),
helpText: vm.$t('accounts.BulkCreateStrategy'),
hidden: () => {
return vm.platform || vm.asset
}
},
name: {
label: vm.$t('common.Name'),
rules: [RequiredChange],
on: {
input: ([value], updateForm) => {
if (!vm.usernameChanged) {
if (!vm.account?.name) {
updateForm({ username: value })
}
const maybePrivileged = defaultPrivilegedAccounts.includes(value)
if (maybePrivileged) {
updateForm({ privileged: true })
}
}
}
},
hidden: () => {
return vm.addTemplate
}
},
username: {
el: {
disabled: !!vm.account?.name
},
on: {
input: ([value], updateForm) => {
vm.usernameChanged = true
},
change: ([value], updateForm) => {
const maybePrivileged = defaultPrivilegedAccounts.includes(value)
if (maybePrivileged) {
updateForm({ privileged: true })
}
}
},
hidden: () => {
return vm.addTemplate
}
},
privileged: {
label: vm.$t('assets.Privileged'),
hidden: () => {
return vm.addTemplate
}
},
su_from: {
component: Select2,
hidden: (formValue) => {
return !vm.asset?.id || !vm.iPlatform.su_enabled
},
el: {
multiple: false,
clearable: true,
ajax: {
url: `/api/v1/accounts/accounts/su-from-accounts/?account=${vm.account?.id || ''}&asset=${vm.asset?.id || ''}`,
transformOption: (item) => {
return { label: `${item.name}(${item.username})`, value: item.id }
}
}
}
},
su_from_username: {
label: vm.$t('assets.UserSwitchFrom'),
hidden: (formValue) => {
return vm.platform || vm.asset || vm.addTemplate
}
},
password: {
label: vm.$t('assets.Password'),
component: UpdateToken,
hidden: (formValue) => {
return formValue.secret_type !== 'password' || vm.addTemplate
}
},
ssh_key: {
label: vm.$t('assets.PrivateKey'),
component: UploadSecret,
hidden: (formValue) => formValue.secret_type !== 'ssh_key' || vm.addTemplate
},
passphrase: {
label: vm.$t('assets.Passphrase'),
component: UpdateToken,
hidden: (formValue) => formValue.secret_type !== 'ssh_key' || vm.addTemplate
},
token: {
label: vm.$t('assets.Token'),
component: UploadSecret,
hidden: (formValue) => formValue.secret_type !== 'token' || vm.addTemplate
},
access_key: {
id: 'access_key',
label: vm.$t('assets.AccessKey'),
component: UploadSecret,
hidden: (formValue) => formValue.secret_type !== 'access_key' || vm.addTemplate
},
api_key: {
id: 'api_key',
label: vm.$t('assets.ApiKey'),
component: UploadSecret,
hidden: (formValue) => formValue.secret_type !== 'api_key' || vm.addTemplate
},
secret_type: {
type: 'radio-group',
options: [],
hidden: () => {
return vm.addTemplate
}
},
push_now: {
helpText: vm.$t('accounts.AccountPush.WindowsPushHelpText'),
hidden: (formValue) => {
const automation = vm.iPlatform.automation || {}
return !automation.push_account_enabled ||
!automation.ansible_enabled ||
!vm.$hasPerm('accounts.push_account') ||
(formValue.secret_type === 'ssh_key' && vm.iPlatform.type.value === 'windows') ||
vm.addTemplate
}
},
params: {
label: vm.$t('assets.PushParams'),
component: AutomationParamsForm,
el: {},
hidden: (formValue) => {
const automation = vm.iPlatform.automation || {}
vm.fieldsMeta.params.el.method = vm.iPlatform.automation.push_account_method
vm.fieldsMeta.params.el.pushAccountParams = vm.iPlatform.automation.push_account_params
return !formValue.push_now ||
!automation.push_account_enabled ||
!automation.ansible_enabled ||
(formValue.secret_type === 'ssh_key' &&
vm.iPlatform.type.value === 'windows') ||
!vm.$hasPerm('accounts.push_account') ||
vm.addTemplate
}
},
is_active: {
label: vm.$t('common.IsActive')
},
comment: {
label: vm.$t('common.Comment'),
hidden: () => {
return vm.addTemplate
}
}
}
}

View File

@@ -0,0 +1,153 @@
<template>
<AutoDataForm
v-if="!loading"
ref="AutoDataForm"
v-bind="$data"
@submit="confirm"
/>
</template>
<script>
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
import { encryptPassword } from '@/utils/crypto'
import { accountFieldsMeta } from '@/components/Apps/AccountCreateUpdateForm/const'
export default {
name: 'AccountCreateForm',
components: {
AutoDataForm
},
props: {
asset: {
type: Object,
default: null
},
platform: {
type: Object,
default: null
},
account: {
type: Object,
default: () => ({})
},
// 默认组件密码加密
encryptPassword: {
type: Boolean,
default: true
},
addTemplate: {
type: Boolean,
default: false
}
},
data() {
return {
loading: true,
usernameChanged: false,
iPlatform: {
automation: {},
su_enabled: false,
protocols: [
{
name: 'ssh',
secret_types: ['password', 'ssh_key', 'token', 'access_key', 'api_key']
}
]
},
url: '/api/v1/accounts/accounts/',
form: Object.assign({ 'on_invalid': 'error' }, this.account || {}),
encryptedFields: ['secret'],
fields: [
[this.$t('assets.Asset'), ['assets']],
[this.$t('accounts.AccountTemplate'), ['template']],
[this.$t('common.Basic'), ['name', 'username', 'privileged', 'su_from', 'su_from_username']],
[this.$t('assets.Secret'), [
'secret_type', 'password', 'ssh_key', 'token',
'access_key', 'passphrase', 'api_key'
]],
[this.$t('common.Other'), ['push_now', 'params', 'on_invalid', 'is_active', 'comment']]
],
fieldsMeta: accountFieldsMeta(this),
hasSaveContinue: false
}
},
async mounted() {
try {
await this.getPlatform()
this.setSecretTypeOptions()
this.getDefaultAssets()
} finally {
this.loading = false
}
},
methods: {
async getDefaultAssets() {
const assetId = this.$route.query.asset_id
if (assetId && !this.form.name) {
this.form.assets = [assetId]
}
},
async getPlatform() {
if (this.platform) {
this.iPlatform = this.platform
}
if (!this.asset || !this.asset.platform) {
return
}
const platformId = this.asset.platform.id
this.iPlatform = await this.$axios.get(`/api/v1/assets/platforms/${platformId}/`)
},
setSecretTypeOptions() {
const choices = [
{
label: this.$t('assets.Password'),
value: 'password'
},
{
label: this.$t('assets.SSHKey'),
value: 'ssh_key'
},
{
label: this.$t('assets.Token'),
value: 'token'
},
{
label: this.$t('assets.AccessKey'),
value: 'access_key'
},
{
label: this.$t('assets.ApiKey'),
value: 'api_key'
}
]
const secretTypes = []
this.iPlatform.protocols?.forEach(p => {
secretTypes.push(...p['secret_types'])
})
if (!this.form?.secret_type) {
this.form.secret_type = secretTypes[0]
}
this.fieldsMeta.secret_type.options = choices.filter(item => {
return secretTypes.indexOf(item.value) > -1
})
},
confirm(form) {
const secretType = form.secret_type || 'password'
form.secret = form[secretType]
form.secret = this.encryptPassword ? encryptPassword(form.secret) : form.secret
// 如果不删除会明文显示
delete form[secretType]
if (!form.secret) {
delete form['secret']
}
if (this.account?.name) {
this.$emit('edit', form)
} else {
this.$emit('add', form)
}
}
}
}
</script>

View File

@@ -0,0 +1,87 @@
<template>
<GenericUpdateFormDialog
v-if="visible"
:form-setting="formSetting"
:selected-rows="selectedRows"
:visible="visible"
v-on="$listeners"
/>
</template>
<script>
import { GenericUpdateFormDialog } from '@/layout/components'
import { accountFieldsMeta } from '@/components/Apps/AccountCreateUpdateForm/const'
import { encryptPassword } from '@/utils/crypto'
export default {
name: 'AccountBulkUpdateDialog',
components: {
GenericUpdateFormDialog
},
props: {
visible: {
type: Boolean,
default: false
},
selectedRows: {
type: Array,
default: () => ([])
}
},
data() {
return {
formSetting: {
url: '/api/v1/accounts/accounts/',
hasSaveContinue: false,
fields: [],
fieldsMeta: accountFieldsMeta(this),
cleanOtherFormValue: (formValue) => {
for (const value of formValue) {
Object.keys(value).forEach((item, index, arr) => {
if (['ssh_key', 'token', 'access_key', 'api_key', 'password'].includes(item)) {
value['secret'] = encryptPassword(value[item])
delete value[item]
}
})
}
return formValue
}
}
}
},
created() {
this.filterFieldsMeta()
},
methods: {
filterFieldsMeta() {
let fields = ['privileged']
const fieldsMeta = {}
const secretFields = ['password', 'ssh_key', 'passphrase', 'token', 'access_key', 'api_key']
const secret_type = this.selectedRows[0].secret_type?.value || 'password'
for (const field of secretFields) {
if (secret_type === 'ssh_key' && field === 'passphrase') {
fields.push('passphrase')
this.formSetting.fieldsMeta['passphrase'].hidden = () => false
continue
}
if (secret_type === field) {
fields.push(field)
this.formSetting.fieldsMeta[field].hidden = () => false
continue
}
delete this.formSetting.fieldsMeta[field]
}
fields = fields.concat(['is_active', 'comment'])
for (const field of fields) {
fieldsMeta[field] = this.formSetting.fieldsMeta[field]
}
this.formSetting.fields = fields
this.formSetting.fieldsMeta = fieldsMeta
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,19 +1,20 @@
<template>
<Dialog
:title="title"
:visible.sync="iVisible"
v-if="iVisible"
:close-on-click-modal="false"
:destroy-on-close="true"
:show-cancel="false"
:show-confirm="false"
:close-on-click-modal="false"
:title="title"
:visible.sync="iVisible"
v-bind="$attrs"
width="70%"
v-on="$listeners"
>
<AccountCreateUpdateForm
v-if="!loading"
ref="form"
:account="account"
:add-template="addTemplate"
:asset="asset"
@add="addAccount"
@edit="editAccount"
@@ -22,8 +23,8 @@
</template>
<script>
import Dialog from '@/components/Dialog'
import AccountCreateUpdateForm from '@/components/AccountCreateUpdateForm'
import Dialog from '@/components/Dialog/index.vue'
import AccountCreateUpdateForm from '@/components/Apps/AccountCreateUpdateForm/index.vue'
export default {
name: 'CreateAccountDialog',
@@ -36,6 +37,10 @@ export default {
type: Boolean,
default: false
},
addTemplate: {
type: Boolean,
default: false
},
asset: {
type: Object,
default: null
@@ -58,6 +63,9 @@ export default {
}
},
computed: {
protocols() {
return this.asset ? this.asset.protocol : []
},
iVisible: {
get() {
return this.visible
@@ -65,37 +73,40 @@ export default {
set(val) {
this.$emit('update:visible', val)
}
},
protocols() {
return this.asset ? this.asset.protocol : []
}
},
methods: {
addAccount(form) {
const formValue = Object.assign({}, form)
let assets = []
let data, url, iVisible
if (this.asset) {
assets = [this.asset.id]
data = {
asset: this.asset.id,
...formValue
}
iVisible = false
url = `/api/v1/accounts/accounts/`
} else {
assets = formValue.assets
iVisible = true
data = formValue
url = `/api/v1/accounts/accounts/bulk/`
if (data.assets.length === 0) {
this.$message.error(this.$tc('assets.PleaseSelectAsset'))
return
}
}
delete formValue.assets
if (assets.length === 0) {
this.$message.error(this.$tc('assets.PleaseSelectAsset'))
return
}
const data = []
for (const asset of assets) {
data.push({
...formValue,
asset
})
}
this.$axios.post(`/api/v1/accounts/accounts/`, data).then(() => {
this.iVisible = false
this.$emit('add', true)
this.$message.success(this.$tc('common.createSuccessMsg'))
}).catch(error => this.setFieldError(error))
this.$axios.post(url, data, {
disableFlashErrorMsg: iVisible
}).then((data) => {
this.handleResult(data, null)
this.iVisible = iVisible
if (!iVisible) {
this.$emit('add', true)
}
}).catch(error => {
this.iVisible = true
this.handleResult(null, error)
})
},
editAccount(form) {
const data = { ...form }
@@ -105,6 +116,30 @@ export default {
this.$message.success(this.$tc('common.updateSuccessMsg'))
}).catch(error => this.setFieldError(error))
},
handleResult(resp, error) {
let bulkCreate = !this.asset
if (error && !Array.isArray(error?.response?.data)) {
bulkCreate = false
}
if (resp && !Array.isArray(resp)) {
bulkCreate = false
}
if (!bulkCreate) {
if (!error) {
this.$message.success(this.$tc('common.createSuccessMsg'))
} else {
this.setFieldError(error)
}
} else {
let result
if (error) {
result = error.response.data
} else {
result = resp
}
this.$emit('bulk-create-done', result)
}
},
setFieldError(error) {
const response = error.response
const data = response.data

View File

@@ -20,22 +20,48 @@
:title="accountCreateUpdateTitle"
:visible.sync="showAddDialog"
@add="addAccountSuccess"
@bulk-create-done="showBulkCreateResult($event)"
/>
<AccountCreateUpdate
v-if="showAddTemplateDialog"
:account="account"
:add-template="true"
:asset="iAsset"
:title="accountCreateUpdateTitle"
:visible.sync="showAddTemplateDialog"
@add="addAccountSuccess"
@bulk-create-done="showBulkCreateResult($event)"
/>
<ResultDialog
v-if="showResultDialog"
:result="createAccountResults"
:visible.sync="showResultDialog"
/>
<AccountBulkUpdateDialog
v-if="updateSelectedDialogSetting.visible"
:visible.sync="updateSelectedDialogSetting.visible"
v-bind="updateSelectedDialogSetting"
@update="handleAccountBulkUpdate"
/>
</div>
</template>
<script>
import ListTable from '@/components/ListTable/index'
import { ActionsFormatter } from '@/components/TableFormatters'
import ViewSecret from './ViewSecret'
import UpdateSecretInfo from './UpdateSecretInfo'
import AccountCreateUpdate from './AccountCreateUpdate'
import ListTable from '@/components/Table/ListTable/index.vue'
import { ActionsFormatter } from '@/components/Table/TableFormatters'
import ViewSecret from './ViewSecret.vue'
import UpdateSecretInfo from './UpdateSecretInfo.vue'
import AccountCreateUpdate from './AccountCreateUpdate.vue'
import { connectivityMeta } from './const'
import { openTaskPage } from '@/utils/jms'
import ResultDialog from './BulkCreateResultDialog.vue'
import AccountBulkUpdateDialog from '@/components/Apps/AccountListTable/AccountBulkUpdateDialog.vue'
export default {
name: 'AccountListTable',
components: {
AccountBulkUpdateDialog,
ResultDialog,
ListTable,
UpdateSecretInfo,
ViewSecret,
@@ -80,14 +106,29 @@ export default {
type: Boolean,
default: true
},
hasDeleteAction: {
type: Boolean,
default: true
},
columnsMeta: {
type: Object,
default: () => {
}
},
columnsDefault: {
type: Array,
default: () => ([
'name', 'username', 'asset', 'privileged',
'secret_type', 'is_active', 'date_updated'
])
},
headerExtraActions: {
type: Array,
default: () => []
},
extraQuery: {
type: Object,
default: () => ({})
}
},
data() {
@@ -95,7 +136,10 @@ export default {
return {
showViewSecretDialog: false,
showUpdateSecretDialog: false,
showResultDialog: false,
showAddDialog: false,
showAddTemplateDialog: false,
createAccountResults: [],
accountCreateUpdateTitle: this.$t('assets.AddAccount'),
iAsset: this.asset,
account: {},
@@ -106,11 +150,12 @@ export default {
app: 'assets',
resource: 'account'
},
extraQuery: this.extraQuery,
columnsExclude: ['spec_info'],
columns: [
'name', 'username', 'asset', 'privileged',
'secret_type', 'source', 'actions'
],
columnsShow: {
min: ['name', 'username', 'actions'],
default: this.columnsDefault
},
columnsMeta: {
name: {
formatter: function(row) {
@@ -189,25 +234,27 @@ export default {
}
},
{
name: 'Delete',
title: this.$t('common.Delete'),
can: this.$hasPerm('accounts.delete_account'),
name: 'ClearSecret',
title: this.$t('common.ClearSecret'),
can: this.$hasPerm('accounts.change_account'),
type: 'primary',
callback: ({ row }) => {
this.$axios.delete(`/api/v1/accounts/accounts/${row.id}/`).then(() => {
this.$message.success(this.$tc('common.deleteSuccessMsg'))
this.$refs.ListTable.reloadTable()
this.$axios.patch(
`/api/v1/accounts/accounts/clear-secret/`,
{ account_ids: [row.id] }
).then(() => {
this.$message.success(this.$tc('common.ClearSuccessMsg'))
})
}
},
{
name: 'Test',
title: this.$t('common.Test'),
title: this.$t('accounts.Test'),
can: ({ row }) =>
!this.$store.getters.currentOrgIsRoot &&
this.$hasPerm('accounts.change_account') &&
row.asset['auto_info'].ansible_enabled &&
row.asset['auto_info'].ping_enabled,
row.asset['auto_config'].ansible_enabled &&
row.asset['auto_config'].ping_enabled,
callback: ({ row }) => {
this.$axios.post(
`/api/v1/accounts/accounts/tasks/`,
@@ -242,6 +289,7 @@ export default {
}
},
headerActions: {
hasLabelSearch: true,
hasLeftActions: this.hasLeftActions,
hasMoreActions: true,
hasCreate: false,
@@ -256,7 +304,8 @@ export default {
},
exportOptions: {
url: this.exportUrl,
mfaVerifyRequired: true
mfaVerifyRequired: true,
tips: this.$t('accounts.AccountExportTips')
},
importOptions: {
canImportCreate: this.$hasPerm('accounts.add_account'),
@@ -280,12 +329,85 @@ export default {
})
}
},
{
name: 'add-template',
title: this.$t('common.TemplateAdd'),
has: !(this.platform || this.asset),
can: () => {
return vm.$hasPerm('accounts.add_account') && !this.$store.getters.currentOrgIsRoot
},
callback: async() => {
await this.getAssetDetail()
setTimeout(() => {
vm.iAsset = this.asset
vm.account = {}
vm.accountCreateUpdateTitle = this.$t('assets.AddAccount')
vm.showAddTemplateDialog = true
})
}
},
...this.headerExtraActions
// {
// name: 'autocreate',
// title: this.$t('accounts.AutoCreate'),
// type: 'default'
// }
],
extraMoreActions: [
{
name: 'BulkVerify',
title: this.$t('accounts.BulkVerify'),
type: 'primary',
fa: 'fa-link',
can: ({ selectedRows }) => {
return selectedRows.length > 0 &&
['clickhouse', 'redis', 'website', 'chatgpt'].indexOf(selectedRows[0].asset.type.value) === -1 &&
!this.$store.getters.currentOrgIsRoot
},
callback: function({ selectedRows }) {
const ids = selectedRows.map(v => {
return v.id
})
this.$axios.post(
'/api/v1/accounts/accounts/tasks/',
{ action: 'verify', accounts: ids }).then(res => {
openTaskPage(res['task'])
}).catch(err => {
this.$message.error(this.$tc('common.bulkVerifyErrorMsg' + ' ' + err))
})
}.bind(this)
},
{
name: 'ClearSecrets',
title: this.$t('common.ClearSecret'),
type: 'primary',
fa: 'clean',
can: ({ selectedRows }) => {
return selectedRows.length > 0 && vm.$hasPerm('accounts.change_account')
},
callback: function({ selectedRows }) {
const ids = selectedRows.map(v => {
return v.id
})
this.$axios.patch(
'/api/v1/accounts/accounts/clear-secret/',
{ account_ids: ids }).then(() => {
this.$message.success(this.$tc('common.ClearSuccessMsg'))
}).catch(err => {
this.$message.error(this.$tc('common.bulkClearErrorMsg' + ' ' + err))
})
}.bind(this)
},
{
name: 'actionUpdateSelected',
title: this.$t('accounts.AccountBatchUpdate'),
fa: 'batch-update',
can: ({ selectedRows }) => {
return selectedRows.length > 0 &&
!this.$store.getters.currentOrgIsRoot &&
vm.$hasPerm('accounts.change_account') &&
selectedRows.every(i => i.secret_type.value === selectedRows[0].secret_type.value)
},
callback: ({ selectedRows }) => {
vm.updateSelectedDialogSetting.selectedRows = selectedRows
vm.updateSelectedDialogSetting.visible = true
}
}
],
canBulkDelete: vm.$hasPerm('accounts.delete_account'),
searchConfig: {
@@ -293,6 +415,10 @@ export default {
exclude: ['asset']
},
hasSearch: true
},
updateSelectedDialogSetting: {
visible: false,
selectedRows: []
}
}
},
@@ -312,6 +438,31 @@ export default {
actionColumn.formatterArgs.extraActions.push(item)
}
}
if (this.hasDeleteAction) {
this.tableConfig.columnsMeta.actions.formatterArgs.extraActions.push(
{
name: 'Delete',
title: this.$t('common.Delete'),
can: this.$hasPerm('accounts.delete_account'),
type: 'primary',
callback: ({ row }) => {
const msg = this.$t('accounts.AccountDeleteConfirmMsg')
this.$confirm(msg, this.$tc('common.Info'), {
type: 'warning',
confirmButtonClass: 'el-button--danger',
beforeClose: async(action, instance, done) => {
if (action !== 'confirm') return done()
this.$axios.delete(`/api/v1/accounts/accounts/${row.id}/`).then(() => {
done()
this.$refs.ListTable.reloadTable()
this.$message.success(this.$tc('common.deleteSuccessMsg'))
})
}
})
}
}
)
}
},
methods: {
onUpdateAuthDone(account) {
@@ -328,6 +479,17 @@ export default {
},
refresh() {
this.$refs.ListTable.reloadTable()
},
showBulkCreateResult(results) {
this.showResultDialog = false
this.createAccountResults = results
setTimeout(() => {
this.showResultDialog = true
}, 100)
},
handleAccountBulkUpdate() {
this.updateSelectedDialogSetting.visible = false
this.$refs.ListTable.reloadTable()
}
}
}

View File

@@ -0,0 +1,120 @@
<template>
<Dialog
:show-cancel="false"
:title="title"
v-bind="$attrs"
@confirm="closeDialog"
v-on="$listeners"
>
<el-alert style="margin-bottom: 10px" type="success">
<span v-for="item of summary" :key="item.key"><b>{{ item.label }}</b>: {{ item.value }} </span>
</el-alert>
<DataTable :config="config" />
</Dialog>
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import DataTable from '@/components/Table/DataTable/index.vue'
export default {
name: 'ResultDialog',
components: {
DataTable,
Dialog
},
props: {
result: {
type: Array,
default: () => []
}
},
data() {
const errorProp = this.$t('common.Error')
const stateMap = {
'created': this.$tc('common.Created'),
'updated': this.$tc('common.Updated'),
'skipped': this.$tc('common.Skipped')
}
const stateClsMap = {
'created': 'color-primary',
'updated': 'color-success',
'skipped': 'color-default'
}
return {
title: this.$t('accounts.AddAccountResult'),
config: {
columns: [
{
prop: 'asset',
label: this.$t('assets.Asset')
},
{
prop: 'state',
label: this.$t('common.Status'),
width: '200px',
formatter: (row) => {
if (row.error) {
return <span class='color-error'>{ errorProp }: { row.error }</span>
} else if (row.state) {
const colorCls = stateClsMap[row.state]
const state = stateMap[row.state]
return <span class={ colorCls }>{ state }</span>
}
}
}
],
totalData: this.result
}
}
},
computed: {
summary() {
const labels = {
total: this.$tc('common.Total'),
created: this.$tc('common.Created'),
updated: this.$tc('common.Updated'),
skipped: this.$tc('common.Skipped'),
error: this.$tc('common.Error')
}
const grouped = _.groupBy(this.result, 'state')
const groupedLength = _.mapValues(grouped, 'length')
groupedLength['total'] = this.result.length
return _.map(groupedLength, (value, key) => {
return {
label: labels[key],
value: value,
key: key
}
})
}
},
methods: {
closeDialog() {
this.$emit('update:visible', false)
}
}
}
</script>
<style scoped>
.color-error {
color: var(--color-danger);
}
.color-primary {
color: var(--color-primary);
}
.color-success {
color: var(--color-success);
}
.color-default {
}
::v-deep .el-data-table .el-table .el-table__row > td > div > span {
white-space: inherit;
}
</style>

View File

@@ -4,7 +4,7 @@
<script>
import { GenericListTableDialog } from '@/layout/components'
import { ShowKeyCopyFormatter } from '@/components/TableFormatters'
import { ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
export default {
components: {
@@ -27,8 +27,9 @@ export default {
visible: false,
width: '60%',
tableConfig: {
id: 'history_date',
url: `/api/v1/accounts/account-secrets/${this.account.id}/histories/`,
columns: ['secret', 'secret_type', 'history_date'],
columns: ['secret', 'version', 'history_date'],
columnsMeta: {
secret: {
label: this.$t('assets.Password'),

View File

@@ -0,0 +1,112 @@
<template>
<Dialog
:destroy-on-close="true"
:show-cancel="false"
:visible.sync="show"
:width="'50'"
v-bind="$attrs"
@confirm="accountConfirmHandle"
v-on="$listeners"
/>
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import { openTaskPage } from '@/utils/jms'
export default {
name: 'RemoveAccount',
components: {
Dialog
},
props: {
accounts: {
type: Array,
default: () => []
},
visible: {
type: Boolean,
default: false
}
},
data() {
return {
show: false,
mfaDialogVisible: true
}
},
computed: {},
mounted() {
const url = `/api/v1/accounts/accounts/tasks/`
this.$axios.post(
url, { disableFlashErrorMsg: true, action: 'remove' }
).then(resp => {
this.$axios.post(
`/api/v1/accounts/accounts/tasks/`,
{
action: 'remove',
gather_accounts: this.accounts.map(account => account.id)
}
).then(res => {
openTaskPage(res['task'])
})
})
},
methods: {
accountConfirmHandle() {
this.show = false
this.mfaDialogVisible = false
},
exit() {
this.$emit('update:visible', false)
}
}
}
</script>
<style lang="scss" scoped>
.item-textarea > > > .el-textarea__inner {
height: 110px;
}
.el-form-item {
border-bottom: 1px solid #EBEEF5;
padding: 5px 0;
margin-bottom: 0;
&:last-child {
border-bottom: none;
}
> > > .el-form-item__label {
padding-right: 20px;
line-height: 30px;
}
> > > .el-form-item__content {
line-height: 30px;
pre {
margin: 0;
}
}
}
ul {
margin: 0;
}
li {
display: block;
font-size: 13px;
margin-bottom: 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.title {
color: #303133;
font-weight: 500;
}
}
</style>

View File

@@ -29,8 +29,8 @@
</template>
<script>
import Dialog from '@/components/Dialog'
import { UpdateToken, UploadKey } from '@/components/FormFields'
import Dialog from '@/components/Dialog/index.vue'
import { UpdateToken, UploadKey } from '@/components/Form/FormFields'
import { encryptPassword } from '@/utils/crypto'
export default {

View File

@@ -1,10 +1,5 @@
<template>
<div>
<UserConfirmDialog
:url="url"
@UserConfirmCancel="exit"
@UserConfirmDone="getAuthInfo"
/>
<Dialog
:destroy-on-close="true"
:show-cancel="false"
@@ -12,7 +7,7 @@
:visible.sync="showSecret"
:width="'50'"
v-bind="$attrs"
@confirm="showSecret = false"
@confirm="accountConfirmHandle"
v-on="$listeners"
>
<el-form :model="secretInfo" class="password-form" label-position="right" label-width="100px">
@@ -27,7 +22,9 @@
:cell-value="secretInfo.secret"
:col="{ formatterArgs: {
name: account['name'],
secretType: secretType || ''
}}"
@input="onShowKeyCopyFormatterChange"
/>
</el-form-item>
<el-form-item v-if="secretType === 'ssh_key'" :label="$tc('assets.sshKeyFingerprint')">
@@ -61,17 +58,16 @@
</template>
<script>
import Dialog from '@/components/Dialog'
import PasswordHistoryDialog from './PasswordHistoryDialog'
import UserConfirmDialog from '@/components/UserConfirmDialog'
import { ShowKeyCopyFormatter } from '@/components/TableFormatters'
import Dialog from '@/components/Dialog/index.vue'
import PasswordHistoryDialog from './PasswordHistoryDialog.vue'
import { ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
import { encryptPassword } from '@/utils/crypto'
export default {
name: 'ShowSecretInfo',
components: {
Dialog,
PasswordHistoryDialog,
UserConfirmDialog,
ShowKeyCopyFormatter
},
props: {
@@ -87,6 +83,10 @@ export default {
type: String,
default: ''
},
type: {
type: String,
default: 'account'
},
title: {
type: String,
default: function() {
@@ -100,10 +100,12 @@ export default {
},
data() {
return {
modifiedSecret: '',
secretInfo: {},
versions: '-',
showSecret: false,
sshKeyFingerprint: '',
mfaDialogVisible: true,
sshKeyFingerprint: '-',
historyCount: 0,
showPasswordHistoryDialog: false
}
@@ -121,14 +123,32 @@ export default {
const url = `/api/v1/accounts/account-secrets/${this.account.id}/histories/?limit=1`
this.$axios.get(url, { disableFlashErrorMsg: true }).then(resp => {
this.versions = resp.count
this.showSecretDialog()
})
} else {
this.showSecretDialog()
}
},
methods: {
getAuthInfo() {
this.$axios.get(this.url, { disableFlashErrorMsg: true }).then(resp => {
this.secretInfo = resp
this.sshKeyFingerprint = resp?.spec_info
accountConfirmHandle() {
this.modifiedSecret && this.onChangeSecretSubmit()
this.showSecret = false
this.mfaDialogVisible = false
},
onChangeSecretSubmit() {
const params = {
name: this.secretInfo.name,
secret: encryptPassword(this.modifiedSecret)
}
const url = this.type === 'account' ? `/api/v1/accounts/accounts` : `/api/v1/accounts/account-templates`
this.$axios.patch(`${url}/${this.account.id}/`, params).then(() => {
this.$message.success(this.$tc('common.updateSuccessMsg'))
})
},
showSecretDialog() {
return this.$axios.get(this.url, { disableFlashErrorMsg: true }).then((res) => {
this.secretInfo = res
this.sshKeyFingerprint = res?.spec_info?.ssh_key_fingerprint || '-'
this.showSecret = true
})
},
@@ -137,6 +157,10 @@ export default {
},
showHistoryDialog() {
this.showPasswordHistoryDialog = true
},
onShowKeyCopyFormatterChange(value) {
if (value === this.secretInfo.secret) return
this.modifiedSecret = value
}
}
}

View File

@@ -1,5 +1,5 @@
import i18n from '@/i18n/i18n'
import { ChoicesFormatter } from '@/components/TableFormatters'
import { ChoicesFormatter } from '@/components/Table/TableFormatters'
export const connectivityMeta = {
label: i18n.t('assets.Connectivity'),
@@ -15,7 +15,7 @@ export const connectivityMeta = {
err: 'text-danger'
},
getText({ cellValue }) {
if (cellValue?.value === '-') {
if (cellValue?.value === '-' || cellValue?.value === 'unknown') {
return '-'
} else {
return cellValue?.label

View File

@@ -1,14 +1,14 @@
<template>
<IBox :fa="icon" :type="type" :title="title" v-bind="$attrs">
<IBox :fa="icon" :title="title" :type="type" v-bind="$attrs">
<table style="width: 100%">
<tr>
<td colspan="2">
<AssetSelect ref="assetSelect" :disabled="disabled" :can-select="canSelect" />
<AssetSelect ref="assetSelect" :can-select="canSelect" :disabled="disabled" />
</td>
</tr>
<tr>
<td colspan="2">
<el-button :type="type" size="small" :disabled="disabled" @click="addObjects">{{ $t('common.Add') }}</el-button>
<el-button :disabled="disabled" :type="type" size="small" @click="addObjects">{{ $t('common.Add') }}</el-button>
</td>
</tr>
</table>
@@ -16,8 +16,8 @@
</template>
<script>
import IBox from '@/components/IBox'
import AssetSelect from '@/components/AssetSelect'
import IBox from '@/components/IBox/index.vue'
import AssetSelect from '@/components/Apps/AssetSelect/index.vue'
export default {
name: 'AssetRelationCard',

View File

@@ -1,31 +1,35 @@
<template>
<Dialog
:close-on-click-modal="false"
:loading-status="!isLoaded"
:title="$tc('assets.Assets')"
custom-class="asset-select-dialog"
top="1vh"
top="2vh"
v-bind="$attrs"
width="80vw"
@close="handleClose"
width="1000px"
@cancel="handleCancel"
@close="handleClose"
@confirm="handleConfirm"
v-on="$listeners"
>
<AssetTreeTable
ref="ListPage"
v-bind="$attrs"
:header-actions="headerActions"
:table-config="tableConfig"
:url="baseUrl"
:node-url="baseNodeUrl"
:table-config="tableConfig"
:tree-url="`${baseNodeUrl}children/tree/`"
:url="baseUrl"
:tree-setting="treeSetting"
class="tree-table"
v-bind="$attrs"
@loaded="handleTableLoaded"
/>
</Dialog>
</template>
<script>
import AssetTreeTable from '@/components/AssetTreeTable'
import Dialog from '@/components/Dialog'
import AssetTreeTable from '@/components/Apps/AssetTreeTable/index.vue'
import Dialog from '@/components/Dialog/index.vue'
export default {
componentName: 'AssetSelectDialog',
@@ -52,11 +56,16 @@ export default {
disabled: {
type: [Boolean, Function],
default: false
},
treeSetting: {
type: Object,
default: () => ({})
}
},
data() {
const vm = this
return {
isLoaded: false,
dialogVisible: false,
rowSelected: _.cloneDeep(this.value) || [],
rowsAdd: [],
@@ -83,16 +92,6 @@ export default {
return row.platform.name
}
},
{
prop: 'protocols',
formatter: function(row) {
const data = row.protocols.map(p => {
return <el-tag size='mini'>{p.name}/{p.port} </el-tag>
})
return <span> {data} </span>
},
label: this.$t('assets.Protocols')
},
{
prop: 'actions',
has: false
@@ -114,6 +113,7 @@ export default {
headerActions: {
hasLeftActions: false,
hasRightActions: false,
hasLabelSearch: true,
searchConfig: {
getUrlQuery: false
}
@@ -146,6 +146,9 @@ export default {
if (selectValueIndex > -1) {
this.rowSelected.splice(selectValueIndex, 1)
}
},
handleTableLoaded() {
this.isLoaded = true
}
}
}
@@ -161,21 +164,15 @@ export default {
.tree-table {
.search {
.el-input__inner {
background-color: #f3f3f3;
}
.el-cascader {
background-color: #f3f3f3;
}
}
.left {
padding: 5px;
}
.treebox {
height: 70vh;
}
.right {
min-height: 500px;
overflow: auto;
}
.mini {
@@ -183,13 +180,13 @@ export default {
}
.transition-box {
padding: 5px;
padding: 10px 5px;
}
}
}
.page ::v-deep .treebox {
height: inherit !important;
.page ::v-deep .treebox .ztree {
}
.asset-select-dialog ::v-deep .el-icon-circle-check {

View File

@@ -11,12 +11,13 @@
<AssetSelectDialog
v-if="dialogVisible"
ref="dialog"
:base-node-url="baseNodeUrl"
:base-url="baseUrl"
:tree-setting="treeSetting"
:tree-url-query="treeUrlQuery"
:value="value"
:visible.sync="dialogVisible"
v-bind="$attrs"
:tree-url-query="treeUrlQuery"
:base-url="baseUrl"
:base-node-url="baseNodeUrl"
@cancel="handleCancel"
@confirm="handleConfirm"
v-on="$listeners"
@@ -25,9 +26,8 @@
</template>
<script>
import Select2 from '@/components/FormFields/Select2'
import Select2 from '@/components/Form/FormFields/Select2.vue'
import AssetSelectDialog from './dialog.vue'
import { b } from 'css-color-function/lib/adjusters'
export default {
componentName: 'AssetSelect',
@@ -48,6 +48,10 @@ export default {
value: {
type: Array,
default: () => []
},
treeSetting: {
type: Object,
default: () => ({})
}
},
data() {
@@ -76,7 +80,6 @@ export default {
}
},
methods: {
b,
handleFocus() {
this.$refs.select2.selectRef.blur()
this.dialogVisible = true
@@ -130,21 +133,12 @@ export default {
padding: 0 0 0 3px;
.tree-table {
.search {
.el-input__inner {
background-color: #f3f3f3;
}
.el-cascader {
background-color: #f3f3f3;
}
}
.left {
padding: 5px;
.treebox {
height: 70vh;
.ztree {
min-height: 500px;
height: inherit !important;
}
}
@@ -153,7 +147,7 @@ export default {
}
.transition-box {
padding: 5px;
padding: 10px 5px;
}
}
}

View File

@@ -1,10 +1,10 @@
<template>
<TreeTable
ref="TreeList"
component="TabTree"
:table-config="tableConfig"
:active-menu.sync="treeTableConfig.activeMenu"
:table-config="tableConfig"
:tree-tab-config="treeTableConfig"
component="TabTree"
v-bind="$attrs"
v-on="$listeners"
>
@@ -12,13 +12,13 @@
<slot name="table" />
</template>
<div slot="rMenu" slot-scope="{data}">
<slot name="rMenu" :data="data" />
<slot :data="data" name="rMenu" />
</div>
</TreeTable>
</template>
<script>
import TreeTable from '../TreeTable'
import TreeTable from '../../Table/TreeTable/index.vue'
import { setRouterQuery, setUrlParam } from '@/utils/common'
import $ from '@/utils/jquery-vendor'
@@ -31,6 +31,10 @@ export default {
type: String,
default: '/api/v1/assets/assets/'
},
typeUrl: {
type: String,
default: '/api/v1/assets/nodes/category/tree/'
},
nodeUrl: {
type: String,
default: '/api/v1/assets/nodes/'
@@ -60,6 +64,7 @@ export default {
const showAssets = this.treeSetting?.showAssets || this.showAssets
const treeUrlQuery = this.setTreeUrlQuery()
const assetTreeUrl = `${this.treeUrl}?assets=${showAssets ? '1' : '0'}&${treeUrlQuery}`
const vm = this
return {
treeTabConfig: {
@@ -81,7 +86,13 @@ export default {
nodeUrl: this.nodeUrl,
treeUrl: assetTreeUrl,
callback: {
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode),
beforeRefresh: () => {
const query = { ...this.$route.query, node_id: '', asset_id: '' }
setTimeout(() => {
vm.$router.replace({ query: query })
}, 100)
}
},
...this.treeSetting
}
@@ -94,9 +105,9 @@ export default {
showAssets: false,
showSearch: false,
customTreeHeaderName: this.$t('assets.BuiltinTree'),
url: '/api/v1/assets/nodes/category/tree/',
url: this.typeUrl,
nodeUrl: this.treeSetting?.nodeUrl || this.nodeUrl,
treeUrl: `/api/v1/assets/nodes/category/tree/?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
treeUrl: `${this.typeUrl}?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
callback: {
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
}

View File

@@ -0,0 +1,203 @@
<template>
<div>
<div>
<el-button
:disabled="isDisabled"
size="mini"
type="primary"
@click="onOpenDialog"
>
{{ $tc('common.Setting') }}
</el-button>
</div>
<Dialog
v-if="visible"
:destroy-on-close="true"
:show-cancel="false"
:show-confirm="false"
:title="title"
:visible.sync="visible"
v-bind="$attrs"
width="60%"
v-on="$listeners"
>
<AutoDataForm
ref="autoDataForm"
:form="form"
class="data-form"
v-bind="config"
@submit="onSubmit"
/>
</Dialog>
</div>
</template>
<script>
import Dialog from '../../Dialog'
import AutoDataForm from '../../Form/AutoDataForm'
export default {
componentName: 'AutomationParams',
components: {
Dialog,
AutoDataForm
},
props: {
value: {
type: Object,
default: () => ({})
},
title: {
type: String,
default: function() {
return this.$t('assets.PushParams')
}
},
assets: {
type: Array,
default: () => []
},
nodes: {
type: Array,
default: () => []
},
platforms: {
type: Array,
default: () => []
},
method: {
type: String,
default: ''
},
url: {
type: String,
default: `/api/v1/assets/platform-automation-methods/`
}
},
data() {
const vm = this
return {
remoteMeta: {},
visible: false,
isDisabled: true,
form: this.value,
config: {
url: this.url,
hasSaveContinue: false,
hasButtons: true,
method: 'get',
fields: [],
fieldsMeta: {}
},
onFieldChangeHandler: _.debounce(vm.handleFieldChange, 1000)
}
},
computed: {
refForm() {
return this.$refs.autoDataForm
}
},
watch: {
nodes: {
handler() {
this.onFieldChangeHandler()
},
deep: true
},
assets: {
handler() {
this.onFieldChangeHandler()
},
deep: true
},
platforms: {
handler(newVal) {
this.onFieldChangeHandler()
},
deep: true,
immediate: true
}
},
async mounted() {
await this.getUrlMeta()
},
methods: {
async getUrlMeta() {
const data = await this.$store.dispatch('common/getUrlMeta', { url: this.url })
this.remoteMeta = data.actions[this.config.method.toUpperCase()] || {}
},
async getFilterPlatforms() {
return await this.$axios.post(
'/api/v1/assets/platforms/filter-nodes-assets/',
{
'node_ids': this.nodes,
'asset_ids': this.assets,
'platform_ids': this.platforms.map(i => i.id || i.pk || i)
}
)
},
async handleFieldChange() {
const platforms = await this.getFilterPlatforms()
let pushAccountMethods = platforms.map(i => i.automation[this.method])
pushAccountMethods = _.uniq(pushAccountMethods)
// 检测是否有可设置的推送方式
const hasCanSettingPushMethods = _.intersection(pushAccountMethods, Object.keys(this.remoteMeta))
this.setFormConfig(hasCanSettingPushMethods)
this.isDisabled = hasCanSettingPushMethods.length <= 0
},
setFormConfig(methods) {
const newForm = {}
const fields = []
const fieldsMeta = {}
this.config.fields = []
// Todo: 未来改成后端处理,生成 serializer, 这里就不用判断类型了
const typeMapper = {
'string': 'input',
'boolean': 'switch'
}
for (const method of methods) {
const filterField = this.remoteMeta[method] || {}
// 修改资产、节点时不点击设置按钮也需要获取form表单值暴露出去
if (this.form.hasOwnProperty(method)) {
newForm[method] = this.form[method]
}
fields.push([filterField.label, [method]])
fieldsMeta[method] = {
fields: [],
fieldsMeta: {}
}
if (Object.keys(filterField?.children || {}).length > 0) {
for (const [k, v] of Object.entries(filterField.children)) {
const item = {
...v,
type: typeMapper[v.type] || 'input'
}
delete item.default
fieldsMeta[method].fields.push(k)
fieldsMeta[method].fieldsMeta[k] = item
}
}
}
this.form = newForm
this.config.fields = fields
this.config.fieldsMeta = fieldsMeta
},
onOpenDialog() {
this.visible = true
},
onSubmit(form) {
this.form = form
this.$emit('input', form)
setTimeout(() => {
this.visible = false
}, 100)
this.$log.debug('Auto push form:', form)
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,97 @@
<template>
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</template>
<script>
import ListTable from '@/components/Table/ListTable/index.vue'
export default {
name: 'BlockedIPList',
components: {
ListTable
},
props: {
object: {
type: Object,
required: false,
default: () => ({})
}
},
data() {
const vm = this
return {
tableConfig: {
url: '/api/v1/settings/security/block-ip/',
columns: [
'ip', 'actions'
],
columnsMeta: {
ip: {
label: this.$t('assets.ip')
},
actions: {
formatterArgs: {
hasDelete: false,
hasUpdate: false,
hasClone: false,
extraActions: [
{
name: 'UnlockIP',
title: this.$t('setting.Unblock'),
can: this.$hasPerm('settings.change_security'),
type: 'primary',
callback: ({ row }) => {
this.$axios.post(
'/api/v1/settings/security/unlock-ip/',
{ ips: [row.ip] }
).then(() => {
vm.$message.success(this.$tc('common.UnlockSuccessMsg'))
vm.$refs.ListTable.reloadTable()
})
}
}
]
}
}
}
},
headerActions: {
hasExport: false,
hasImport: false,
hasCreate: false,
hasSearch: false,
hasRefresh: true,
hasBulkDelete: false,
hasBulkUpdate: false,
hasLeftActions: true,
hasRightActions: true,
extraMoreActions: [
{
name: 'UnlockSelected',
title: this.$t('setting.BulkUnblock'),
type: 'primary',
can: ({ selectedRows }) => {
return selectedRows.length > 0
},
callback: function({ selectedRows }) {
vm.$axios.post(
'/api/v1/settings/security/unlock-ip/',
{
ips: selectedRows.map(v => { return v.ip })
}
).then(res => {
vm.$message.success(vm.$tc('common.UnlockSuccessMsg'))
vm.$refs.ListTable.reloadTable()
})
}
}
]
}
}
}
}
</script>
<style lang='less' scoped>
</style>

View File

@@ -0,0 +1,86 @@
<template>
<div>
<div>
<el-button
size="mini"
type="primary"
@click="onOpenDialog"
>
{{ $tc('common.View') }}
<span>({{ $tc('setting.LockedIP', ipCounts ) }})</span>
</el-button>
</div>
<Dialog
:visible.sync="visible"
:title="title"
width="40%"
:show-cancel="false"
:show-confirm="false"
:destroy-on-close="true"
v-bind="$attrs"
v-on="$listeners"
>
<BlockedIPList />
</Dialog>
</div>
</template>
<script>
import { Dialog } from '@/components'
import BlockedIPList from '@/components/Apps/BlockedIPs/BlockedIPList'
export default {
componentName: 'BlockedIPs',
components: {
BlockedIPList,
Dialog
},
props: {
value: {
type: Object,
default: () => ({})
},
title: {
type: String,
default: function() {
return this.$t('setting.BlockedIPS')
}
},
url: {
type: String,
default: `/api/v1/assets/platform-automation-methods/`
}
},
data() {
return {
remoteMeta: {},
visible: false,
form: this.value,
ipCounts: 0,
config: {
url: this.url,
hasSaveContinue: false,
hasButtons: true,
fields: [],
fieldsMeta: {}
}
}
},
created() {
this.getLockedIp()
},
methods: {
getLockedIp() {
this.$axios.get('/api/v1/settings/security/block-ip/').then(res => {
this.ipCounts = res.count
})
},
onOpenDialog() {
this.visible = true
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,136 @@
<template>
<div>
<Dialog
:destroy-on-close="true"
:show-cancel="false"
:title="title"
:visible.sync="showSecret"
:width="'50'"
v-bind="$attrs"
@confirm="accountConfirmHandle"
v-on="$listeners"
>
<el-form :model="secretInfo" class="password-form" label-position="right" label-width="100px">
<el-form-item :label="$tc('accounts.AccountChangeSecret.OldSecret')">
<ShowKeyCopyFormatter
:cell-value="secretInfo.old_secret"
:col="{ formatterArgs: {
name: 'old_secret'
}}"
/>
</el-form-item>
<el-form-item :label="$tc('accounts.AccountChangeSecret.NewSecret')">
<ShowKeyCopyFormatter
:cell-value="secretInfo.new_secret"
:col="{ formatterArgs: {
name: 'new_secret'
}}"
/>
</el-form-item>
</el-form>
</Dialog>
</div>
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import { ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
export default {
name: 'RecordViewSecret',
components: {
Dialog,
ShowKeyCopyFormatter
},
props: {
visible: {
type: Boolean,
default: false
},
url: {
type: String,
default: ''
},
title: {
type: String,
default: function() {
return this.$tc('common.ViewSecret')
}
}
},
data() {
return {
secretInfo: {},
showSecret: false,
mfaDialogVisible: true
}
},
computed: {
},
mounted() {
this.showSecretDialog()
},
methods: {
accountConfirmHandle() {
this.showSecret = false
this.mfaDialogVisible = false
},
showSecretDialog() {
return this.$axios.get(this.url, { disableFlashErrorMsg: true }).then((res) => {
this.secretInfo = res
this.showSecret = true
})
},
exit() {
this.$emit('update:visible', false)
}
}
}
</script>
<style lang="scss" scoped>
.item-textarea >>> .el-textarea__inner {
height: 110px;
}
.el-form-item {
border-bottom: 1px solid #EBEEF5;
padding: 5px 0;
margin-bottom: 0;
&:last-child {
border-bottom: none;
}
>>> .el-form-item__label {
padding-right: 20px;
line-height: 30px;
}
>>> .el-form-item__content {
line-height: 30px;
pre {
margin: 0;
}
}
}
ul {
margin: 0;
}
li {
display: block;
font-size: 13px;
margin-bottom: 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.title {
color: #303133;
font-weight: 500;
}
}
</style>

View File

@@ -0,0 +1,163 @@
<template>
<div class="container">
<div class="chat-action">
<Select2
v-model="select.value"
:disabled="isLoading || isSelectDisabled"
v-bind="select"
@change="onSelectChange"
/>
</div>
<div class="chat-input">
<el-input
v-model="inputValue"
:disabled="isLoading"
:placeholder="$tc('common.InputMessage')"
type="textarea"
@compositionend="isIM = false"
@compositionstart="isIM = true"
@keypress.native="onKeyEnter"
/>
<div class="input-action">
<span class="right">
<i :class="{'active': inputValue }" class="fa fa-send" @click="onSendHandle" />
</span>
</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import Select2 from '../../../../Form/FormFields/Select2.vue'
import { useChat } from '../../useChat.js'
const { setLoading } = useChat()
export default {
components: { Select2 },
props: {
},
data() {
return {
isIM: false,
inputValue: '',
select: {
url: '/api/v1/settings/chatai-prompts/',
value: '',
multiple: false,
placeholder: this.$t('common.Prompt'),
ajax: {
transformOption: (item) => {
return { label: item.name, value: item.content }
}
}
}
}
},
computed: {
...mapState({
isLoading: state => state.chat.loading
}),
isSelectDisabled() {
return !!this.select.value
}
},
methods: {
onKeyEnter(event) {
if (event.key === 'Enter') {
if ((!this.isIM && !event.shiftKey) || (this.isIM && event.ctrlKey)) {
event.preventDefault()
this.onSendHandle()
}
}
},
onSendHandle() {
if (!this.inputValue) return
setLoading(true)
this.$emit('send', this.inputValue)
this.inputValue = ''
},
onSelectChange(value) {
this.$emit('select-prompt', value)
}
}
}
</script>
<style lang="scss" scoped>
.container {
display: flex;
height: 100%;
flex-direction: column;
.chat-action {
width: 100%;
margin: 6px 0;
&>>> .el-select {
width: 50%;
.el-input__inner {
height: 28px;
line-height: 28px;
border-radius: 14px;
border-color: transparent;
background-color: #f7f7f8;
font-size: 13px;
color: rgba(0, 0, 0, 0.45);
&:hover {
background-color: #ededed;
}
}
.el-input__icon {
line-height: 0px;
}
}
}
.chat-input {
flex: 1;
display: flex;
flex-direction: column;
border: 1px solid #DCDFE6;
border-radius: 12px;
&:has(.el-textarea__inner:focus) {
border: 1px solid var(--color-primary);
}
&>>> .el-textarea {
height: 100%;
.el-textarea__inner {
height: 100%;
padding: 8px 10px;
border: none;
border-top-left-radius: 12px;
border-top-right-radius: 12px;
resize: none;
&::-webkit-scrollbar {
width: 12px;
}
}
}
.el-textarea.is-disabled + .input-action {
background-color: #F5F7FA;
cursor: no-drop;
i {
cursor: no-drop;
}
}
.input-action {
overflow: hidden;
padding: 0 16px 15px;
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
.right {
float: right;
.active {
color: var(--color-primary);
}
i {
cursor: pointer;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,186 @@
<template>
<div :class="{'user-role': isUserRole}" class="chat-item">
<div class="avatar">
<el-avatar :src="isUserRole ? userUrl : chatUrl" class="header-avatar" />
</div>
<div class="content">
<div class="operational">
<span class="date">
{{ $moment(item.message.create_time).format('YYYY-MM-DD HH:mm:ss') }}
</span>
</div>
<div class="message">
<div class="message-content">
<span v-if="isSystemError" class="error">
{{ item.message.content }}
</span>
<span v-else class="chat-text">
<MessageText :message="item.message" />
</span>
</div>
<div class="action">
<el-tooltip
v-if="isSystemError && isLoading"
:content="$tc('common.Reconnect')"
effect="dark"
placement="top"
>
<svg-icon icon-class="refresh" @click="onRefresh" />
</el-tooltip>
<el-dropdown v-else size="small" @command="handleCommand">
<span class="el-dropdown-link">
<i class="fa fa-ellipsis-v" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="i in dropdownOptions" :key="i.action" :command="i.action">
{{ i.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</div>
</div>
</template>
<script>
import MessageText from './MessageText.vue'
import { mapState } from 'vuex'
import { copy } from '@/utils/common'
import { useChat } from '../../useChat.js'
import { reconnect } from '@/utils/socket'
const { setLoading, removeLoadingMessageInChat } = useChat()
export default {
components: {
MessageText
},
props: {
item: {
type: Object,
default: () => {}
}
},
data() {
return {
chatUrl: require('@/assets/img/chat.png'),
userUrl: '/api/v1/settings/logo/',
dropdownOptions: [
{
action: 'copy',
label: this.$t('common.Copy')
}
]
}
},
computed: {
...mapState({
isLoading: state => state.chat.loading
}),
isUserRole() {
return this.item.message?.role === 'user'
},
isSystemError() {
return this.item.type === 'error' && this.item.message?.role === 'assistant'
}
},
methods: {
onRefresh() {
reconnect()
removeLoadingMessageInChat()
setLoading(false)
},
handleCommand(value) {
if (value === 'copy') {
copy(this.item.message.content)
}
}
}
}
</script>
<style lang="scss" scoped>
.chat-item {
display: flex;
padding: 16px 14px 0;
&:last-child {
padding-bottom: 16px;
}
.avatar {
width: 22px;
height: 22px;
margin-top: 2px;
.header-avatar {
width: 100%;
height: 100%;
&>>> img {
background-color: #e5e5e7;
}
}
}
.content {
margin-left: 6px;
overflow: hidden;
.operational {
display: flex;
justify-content: space-between;
overflow: hidden;
.copy {
float: right;
cursor: pointer;
}
}
.message {
display: -webkit-box;
.message-content {
flex: 1;
padding: 6px 10px;
border-radius: 2px 12px 12px;
background-color: #f0f1f5;
}
.action {
.svg-icon {
transform: translateY(50%);
margin-left: 3px;
cursor: pointer;
}
.el-dropdown {
height: 32px;
line-height: 37px;
font-size: 13px;
.el-dropdown-link {
i {
padding: 4px 5px;
font-size: 15px;
color: #8d9091;
&:hover {
color: #7b8085
}
}
}
}
}
.error {
color: red;
}
}
}
}
.user-role {
flex-direction: row-reverse;
.content {
margin-right: 10px;
.operational {
flex-direction: row-reverse;
}
.message {
flex-direction: row-reverse;
.message-content {
background-color: var(--menu-hover);
border-radius: 12px 2px 12px 12px;
}
}
}
}
</style>

View File

@@ -0,0 +1,178 @@
<template>
<div>
<div ref="textRef" class="leading-relaxed break-words">
<span v-if="message.content === 'loading'" class="loading-box">
<span />
<span />
<span />
</span>
<div v-else class="inline-block markdown-body" v-html="text" />
</div>
</div>
</template>
<script>
import MarkdownIt from 'markdown-it'
import mdKatex from '@traptitech/markdown-it-katex'
import mila from 'markdown-it-link-attributes'
import hljs from 'highlight.js'
import 'highlight.js/styles/atom-one-dark.css'
import { copy } from '@/utils/common'
/* eslint-disable vue/no-v-html */
export default {
props: {
message: {
type: Object,
default: () => {}
}
},
data() {
return {
markdown: null
}
},
computed: {
text() {
const value = this.message?.content || ''
if (value && this.markdown) {
return this.markdown?.render(value)
}
return this.$xss.process(value)
}
},
mounted() {
this.init()
},
updated() {
this.addCopyEvents()
},
destroyed() {
this.removeCopyEvents()
},
methods: {
init() {
const vm = this
this.markdown = new MarkdownIt({
html: false,
linkify: true,
highlight(code, language) {
const validLang = !!(language && hljs.getLanguage(language))
if (validLang) {
const lang = language || ''
return vm.highlightBlock(hljs.highlight(lang, code, true).value, lang)
}
return vm.highlightBlock(hljs.highlightAuto(code).value, '')
}
})
this.markdown.use(mila, { attrs: { target: '_blank', rel: 'noopener', class: 'link-style' }})
this.markdown.use(mdKatex, { blockClass: 'katexmath-block rounded-md', errorColor: ' #cc0000' })
},
highlightBlock(str, lang) {
return `<pre class="code-block-wrapper"><div class="code-block-header"><span class="code-block-header__lang">${lang}</span><span class="code-block-header__copy">${'Copy'}</span></div><code class="hljs code-block-body ${lang}">${str}</code></pre>`
},
addCopyEvents() {
const copyBtn = document.querySelectorAll('.code-block-header__copy')
copyBtn.forEach((btn) => {
btn.addEventListener('click', () => {
const code = btn.parentElement?.nextElementSibling?.textContent
if (code) {
copy(code)
}
})
})
},
removeCopyEvents() {
if (this.$refs.textRef) {
const copyBtn = this.$refs.textRef.querySelectorAll('.code-block-header__copy')
copyBtn.forEach((btn) => {
btn.removeEventListener('click', () => {})
})
}
}
}
}
</script>
<style lang="scss" scoped>
.markdown-body {
font-size: 13px;
&>>> p {
margin-bottom: 0 !important;
}
background: inherit;
&>>> pre {
padding: 0 0 6px 0;
.hljs.code-block-body {
border-radius: 4px;
}
}
&>>> .code-block-wrapper {
background: #1F2329;
padding: 2px 6px;
margin: 5px 0;
.code-block-body {
padding: 5px 10px 0;
};
.code-block-header {
margin-bottom: 4px;
overflow: hidden;
background: #353946;
color: #c2d1e1;
.code-block-header__copy {
float: right;
cursor: pointer;
&:hover {
color: #6e747b;
}
}
}
.hljs.code-block-body.javascript {
.hljs-comment {
display: block;
}
}
}
}
>>> .link-style {
color: #487bf4;
&:hover {
color: #275ee3;
}
}
.loading-box{
margin-left: 6px;
}
.loading-box span{
display: inline-block;
width: 5px;
height: 5px;
margin-right: 5px;
border-radius: 50%;
vertical-align: middle;
background: #676A6c;
animation: load 1.2s ease infinite;
}
.loading-box span:last-child{
margin-right: 0;
}
@keyframes load{
0%{
opacity: 1;
}
100%{
opacity: 0;
}
}
.loading-box span:nth-child(1){
animation-delay: 0.23s;
}
.loading-box span:nth-child(2){
animation-delay: 0.36s;
}
.loading-box span:nth-child(3){
animation-delay: 0.49s;
}
</style>

View File

@@ -0,0 +1,271 @@
<template>
<div class="chat-content">
<div id="scrollRef" class="chat-list">
<div v-if="showIntroduction" class="introduction">
<div v-for="(item, index) in introduction" :key="index" class="introduction-item" @click="sendIntroduction(item)">
<div class="head">
<i v-if="item.icon" :class="item.icon" />
<span class="title">{{ item.title }}</span>
</div>
<div class="content">
{{ item.content }}
</div>
</div>
</div>
<ChatMessage v-for="(item, index) in activeChat.chats" :key="index" :item="item" />
</div>
<div class="input-box">
<el-button
v-if="isLoading && socket && socket.readyState === 1"
class="stop"
icon="fa fa-stop-circle-o"
round
size="small"
@click="onStopHandle"
>{{ $tc('common.Stop') }}</el-button>
<ChatInput ref="chatInput" @send="onSendHandle" @select-prompt="onSelectPromptHandle" />
</div>
</div>
</template>
<script>
import ChatInput from './ChatInput.vue'
import ChatMessage from './ChatMessage.vue'
import { mapState } from 'vuex'
import { closeWebSocket, createWebSocket, onSend, ws } from '@/utils/socket'
import { getInputFocus, useChat } from '../../useChat.js'
const {
setLoading,
clearChats,
addChatMessageById,
addMessageToActiveChat,
newChatAndAddMessageById,
removeLoadingMessageInChat,
updateChaMessageContentById,
addTemporaryLoadingToChat
} = useChat()
export default {
components: {
ChatInput,
ChatMessage
},
props: {
},
data() {
return {
socket: {},
prompt: '',
currentConversationId: '',
showIntroduction: false,
introduction: [
{
title: this.$t('common.introduction.ConceptTitle'),
content: this.$t('common.introduction.ConceptContent')
},
{
title: this.$t('common.introduction.IdeaTitle'),
content: this.$t('common.introduction.IdeaContent')
}
]
}
},
computed: {
...mapState({
isLoading: state => state.chat.loading,
activeChat: state => state.chat.activeChat
})
},
destroyed() {
closeWebSocket()
},
methods: {
init() {
this.initWebSocket()
this.initChatMessage()
},
initWebSocket() {
const { NODE_ENV, VUE_APP_KAEL_HOST } = process.env || {}
const api = '/kael/chat/system/'
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
const path = `${protocol}://${window.location.host}${api}`
const index = VUE_APP_KAEL_HOST?.indexOf('://')
const localPath = protocol + VUE_APP_KAEL_HOST?.substring(index, VUE_APP_KAEL_HOST?.length) + api
const url = NODE_ENV === 'development' ? localPath : path
createWebSocket(url, this.onWebSocketMessage)
},
initChatMessage() {
this.prompt = ''
this.showIntroduction = true
this.currentConversationId = ''
this.$refs.chatInput.select.value = ''
const chat = {
message: {
content: this.$t('common.ChatHello'),
role: 'assistant',
create_time: new Date()
}
}
newChatAndAddMessageById(chat)
setLoading(false)
},
onWebSocketMessage(data) {
if (data.type === 'message') {
this.onChatMessage(data)
}
if (data.type === 'error') {
this.onSystemMessage(data)
}
},
onChatMessage(data) {
if (data.conversation_id) {
setLoading(true)
removeLoadingMessageInChat()
this.currentConversationId = data.conversation_id
updateChaMessageContentById(data.message.id, data)
}
if (data.message?.type === 'finish') {
setLoading(false)
getInputFocus()
}
},
onSystemMessage(data) {
data.message = {
content: data.system_message,
role: 'assistant',
create_time: new Date()
}
removeLoadingMessageInChat()
addMessageToActiveChat(data)
this.socketReadyStateSuccess = false
setLoading(true)
},
onSendHandle(value) {
this.showIntroduction = false
this.socket = ws || {}
if (ws?.readyState === 1) {
this.socketReadyStateSuccess = true
const chat = {
message: {
content: value,
role: 'user',
create_time: new Date()
}
}
const message = {
content: value,
prompt: this.prompt,
conversation_id: this.currentConversationId || ''
}
addChatMessageById(chat)
onSend(message)
addTemporaryLoadingToChat()
} else {
const chat = {
message: {
content: this.$t('common.ConnectionDropped'),
role: 'assistant',
create_time: new Date()
},
type: 'error'
}
addChatMessageById(chat)
this.socketReadyStateSuccess = false
setLoading(true)
}
},
onSelectPromptHandle(value) {
this.prompt = value
this.currentConversationId = ''
this.showIntroduction = false
this.onSendHandle(value)
},
onNewChat() {
clearChats()
this.initChatMessage()
},
onStopHandle() {
this.$axios.post(
'/kael/interrupt_current_ask/',
{ id: this.currentConversationId || '' }
).finally(() => {
removeLoadingMessageInChat()
setLoading(false)
})
},
sendIntroduction(item) {
this.showIntroduction = false
this.onSendHandle(item.content)
}
}
}
</script>
<style lang="scss" scoped>
.chat-content {
display: flex;
flex-direction: column;
overflow: hidden;
height: 100%;
.introduction {
padding: 16px 14px 0;
.introduction-item {
padding: 12px 14px;
border-radius: 8px;
margin-top: 16px;
background-color: var(--menu-hover);
cursor: pointer;
&:hover {
box-shadow: 0 0 2px 2px #00000014;
}
&:first-child {
margin-top: 0;
}
.head {
margin-bottom: 2px;
.title {
font-weight: 500;
color: #373739;
}
}
.content {
display: inline-block;
color: #a7a7ab;
word-wrap: break-word;
}
}
}
.chat-list {
flex: 1;
position: relative;
padding: 0 15px 25px;
overflow-y: auto;
user-select: text;
&::-webkit-scrollbar {
width: 12px;
}
}
.input-box {
position: relative;
height: 160px;
padding: 0 15px;
margin-bottom: 15px;
border-top: 1px solid #ececec;
}
.stop {
position: absolute;
top: -37px;
left: 50%;
z-index: 11;
transform: translateX(-50%);
>>> i {
margin-right: 4px;
}
}
}
</style>

View File

@@ -0,0 +1,81 @@
<template>
<div class="container">
<div class="close-sidebar">
<i v-if="hasClose" class="el-icon-close" @click="onClose" />
</div>
<el-tabs v-model="active" :tab-position="'right'" @tab-click="handleClick">
<el-tab-pane v-for="(item) in submenu" :key="item.name" :name="item.name">
<span slot="label">
<el-tooltip effect="dark" placement="left" :content="item.label">
<svg-icon :icon-class="item.icon" />
</el-tooltip>
</span>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
export default {
props: {
active: {
type: String,
default: 'chat'
},
hasClose: {
type: Boolean,
default: true
},
submenu: {
type: Array,
default: () => []
}
},
data() {
return {
}
},
methods: {
handleClick(tab, event) {
this.$emit('tab-click', tab)
},
onClose() {
this.$parent.onClose()
}
}
}
</script>
<style lang="scss" scoped>
.container {
width: 100%;
height: 100%;
background-color: #f0f1f5;
.close-sidebar {
height: 48px;
padding: 12px 0;
text-align: center;
font-size: 14px;
cursor: pointer;
i {
font-size: 16px;
font-weight: 600;
padding: 4px;
border-radius: 2px;
&:hover {
color: var(--color-primary);
background: var(--menu-hover);
}
}
}
}
>>> .el-tabs {
.el-tabs__item {
padding: 0 13px;
font-size: 15px;
:hover {
color: #7b8085;
}
}
}
</style>

View File

@@ -0,0 +1,145 @@
<template>
<div class="chat">
<div class="container">
<div class="header">
<div class="left">
<img :src="robotUrl" alt="">
<span class="title">{{ title }}</span>
</div>
<span class="new" @click="onNewChat">
<i class="el-icon-plus" />
<span>{{ $tc('common.NewChat') }}</span>
</span>
</div>
<div class="content">
<keep-alive>
<component :is="active" ref="component" />
</keep-alive>
</div>
</div>
<div class="sidebar">
<Sidebar v-bind="$attrs" :active.sync="active" :submenu="submenu" />
</div>
</div>
</template>
<script>
import Sidebar from './components/Sidebar/index.vue'
import Chat from './components/ChitChat/index.vue'
import { getInputFocus } from './useChat.js'
import { ws } from '@/utils/socket'
export default {
components: {
Chat,
Sidebar
},
props: {
title: {
type: String,
default: function() {
return this.$t('setting.ChatAI')
}
},
drawerPanelVisible: {
type: Boolean,
default: () => false
}
},
data() {
return {
active: 'chat',
robotUrl: require('../../../assets/img/robot-assistant.png'),
submenu: [
{
name: 'chat',
label: this.$t('common.Chat'),
icon: 'chat'
}
]
}
},
watch: {
drawerPanelVisible(value) {
if (value && !ws) {
this.initWebSocket()
}
}
},
methods: {
initWebSocket() {
this.$refs.component?.init()
},
onClose() {
this.$parent.show = false
},
onNewChat() {
this.active = 'chat'
this.$nextTick(() => {
this.$refs.component?.onNewChat()
getInputFocus()
})
}
}
}
</script>
<style lang="scss" scoped>
.chat {
display: flex;
width: 100%;
height: 100%;
.container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
.header {
display: flex;
justify-content: space-between;
height: 48px;
line-height: 48px;
padding: 0 16px;
overflow: hidden;
border-bottom: 1px solid #ececec;
.left {
img {
width: 22px;
height: 22px;
vertical-align: sub;
}
.title {
display: inline-block;
font-size: 18px;
color: black;
}
}
.new {
display: inline-block;
height: 28px;
line-height: 28px;
border-radius: 16px;
padding: 0 10px;
transform: translateY(32%);
color: var(--color-primary);
background-color: #f7f7f8;
cursor: pointer;
font-size: 13px;
&:hover {
background-color: #ededed;
}
}
}
.content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
.sidebar {
height: 100%;
width: 42px;
}
}
</style>

View File

@@ -0,0 +1,80 @@
import store from '@/store'
import { pageScroll } from '@/utils/common'
export const getInputFocus = () => {
const dom = document.querySelector('.chat-input .el-textarea__inner')
setTimeout(() => dom?.focus(), 200)
}
export function useChat() {
const chatStore = {}
const setLoading = (loading) => {
store.commit('chat/setLoading', loading)
}
const onNewChat = (name) => {
const data = {
name: name || `new chat`,
id: 1,
conversation_id: '',
chats: []
}
store.commit('chat/addChatToStore', data)
}
const clearChats = () => {
store.commit('chat/clearChats')
}
const addMessageToActiveChat = (chat) => {
store.commit('chat/addMessageToActiveChat', chat)
}
const removeLoadingMessageInChat = () => {
store.commit('chat/removeLoadingMessageInChat')
}
const addChatMessageById = (chat) => {
store.commit('chat/addMessageToActiveChat', chat)
if (chat?.conversation_id) {
store.commit('chat/setActiveChatConversationId', chat.conversation_id)
}
pageScroll('scrollRef')
}
const addTemporaryLoadingToChat = () => {
const temporaryChat = {
message: {
content: 'loading',
role: 'assistant',
create_time: new Date()
}
}
addChatMessageById(temporaryChat)
}
const newChatAndAddMessageById = (chat) => {
onNewChat(chat.message.content)
addChatMessageById(chat)
}
const updateChaMessageContentById = (id, data) => {
store.commit('chat/updateChaMessageContentById', { id, data })
pageScroll('scrollRef')
}
return {
chatStore,
setLoading,
onNewChat,
clearChats,
getInputFocus,
addMessageToActiveChat,
newChatAndAddMessageById,
removeLoadingMessageInChat,
addChatMessageById,
addTemporaryLoadingToChat,
updateChaMessageContentById
}
}

View File

@@ -0,0 +1,209 @@
<template>
<div ref="drawer" :class="{show: show}" class="drawer">
<div :style="{'background-color': modal ? 'rgba(0, 0, 0, .3)' : 'transparent'}" class="modal" />
<div :style="{'width': width}" class="drawer-panel">
<div v-show="!show" ref="dragBox" class="handle-button">
<i v-if="icon.startsWith('fa') || icon.startsWith('el')" :class="show ? 'el-icon-close': icon" />
<img v-else :src="icon" alt="">
</div>
<div class="drawer-panel-item">
<slot :drawer-panel-visible="show" />
</div>
</div>
</div>
</template>
<script>
export default {
name: 'DrawerPanel',
props: {
icon: {
type: String,
default: 'el-icon-setting'
},
width: {
type: String,
default: '440px'
},
modal: {
type: Boolean,
default: true
},
clickNotClose: {
type: Boolean,
default: false
}
},
data() {
return {
show: false
}
},
watch: {
show(value) {
if (value && !this.clickNotClose) {
this.addEventClick()
}
this.$emit('toggle', this.show)
}
},
mounted() {
this.init()
this.insertToBody()
},
beforeDestroy() {
const element = this.$refs.drawer
element.remove()
window.removeEventListener('click', this.closeSidebar)
},
methods: {
init() {
this.$nextTick(() => {
const dragBox = this.$refs.dragBox
const clientOffset = {}
dragBox.addEventListener('mousedown', (event) => {
const offsetX = dragBox.getBoundingClientRect().left
const offsetY = dragBox.getBoundingClientRect().top
const innerX = event.clientX - offsetX
const innerY = event.clientY - offsetY
clientOffset.clientX = event.clientX
clientOffset.clientY = event.clientY
document.onmousemove = function(event) {
dragBox.style.left = event.clientX - innerX + 'px'
dragBox.style.top = event.clientY - innerY + 'px'
const dragDivTop = window.innerHeight - dragBox.getBoundingClientRect().height
const dragDivLeft = window.innerWidth - dragBox.getBoundingClientRect().width
dragBox.style.left = dragDivLeft + 'px'
dragBox.style.left = '-48px'
if (dragBox.getBoundingClientRect().top <= 0) {
dragBox.style.top = '0px'
}
if (dragBox.getBoundingClientRect().top >= dragDivTop) {
dragBox.style.top = dragDivTop + 'px'
}
event.preventDefault()
event.stopPropagation()
}
document.onmouseup = function() {
document.onmousemove = null
document.onmouseup = null
}
}, false)
dragBox.addEventListener('mouseup', (event) => {
const clientX = event.clientX
const clientY = event.clientY
if (this.isDifferenceWithinThreshold(clientX, clientOffset.clientX) && this.isDifferenceWithinThreshold(clientY, clientOffset.clientY)) {
this.show = !this.show
}
})
})
},
isDifferenceWithinThreshold(num1, num2, threshold = 5) {
const difference = Math.abs(num1 - num2)
return difference <= threshold
},
addEventClick() {
window.addEventListener('click', this.closeSidebar)
},
closeSidebar(evt) {
const parent = evt.target.closest('.drawer-panel')
if (!parent && evt.target.className === 'modal') {
this.show = false
}
},
insertToBody() {
const element = this.$refs.drawer
const body = document.querySelector('body')
body.insertBefore(element, body.firstChild)
}
}
}
</script>
<style lang="scss" scoped>
.modal {
position: fixed;
top: 0;
left: 0;
opacity: 0;
transition: opacity .3s cubic-bezier(.7, .3, .1, 1);
background: rgba(0, 0, 0, .3);
z-index: -1;
}
.drawer-panel {
position: fixed;
top: 0;
right: 0;
width: 100%;
min-width: 260px;
height: 100vh;
user-select: none;
transition: transform .25s cubic-bezier(.7, .3, .1, 1);
box-shadow: 0 0 8px 4px #00000014;
transform: translate(100%);
background: #FFFFFF;
z-index: 1200;
}
.drawer-panel-item {
height: 100%;
}
.drawer-panel-item::-webkit-scrollbar-track {
box-shadow: none;
background-color: transparent;
}
.show {
transition: all .3s cubic-bezier(.7, .3, .1, 1);
}
.show .modal {
z-index: 1003;
opacity: 1;
width: 100%;
height: 100%;
}
.show .drawer-panel {
transform: translate(0);
}
.handle-button {
position: absolute;
bottom: 20%;
left: -48px;
width: 48px;
height: 45px;
line-height: 45px;
box-sizing: border-box;
text-align: center;
font-size: 24px;
border-radius: 20px 0 0 20px;
z-index: 0;
pointer-events: auto;
color: #fff;
background-color: #FFFFFF;
box-shadow: 0 0 8px 4px #00000014;
cursor: pointer;
&:hover {
left: -50px !important;
width: 50px !important;
transform: scale(1.06);
}
i {
font-size: 20px;
line-height: 45px;
}
img {
width: 22px;
height: 22px;
transform: translateY(10%);
margin-left: 3px;
}
}
</style>

View File

@@ -33,7 +33,7 @@
</template>
<script>
import Dialog from '@/components/Dialog'
import Dialog from '@/components/Dialog/index.vue'
import { openTaskPage } from '@/utils/jms'
export default {

View File

@@ -3,8 +3,10 @@
</template>
<script type="text/jsx">
import TreeTable from '../TreeTable'
import { DetailFormatter } from '@/components/TableFormatters'
import TreeTable from '../../Table/TreeTable/index.vue'
import { DetailFormatter } from '@/components/Table/TableFormatters'
import { AccountInfoFormatter } from '@/components/Table/TableFormatters'
import { connectivityMeta } from '@/components/Apps/AccountListTable/const'
export default {
name: 'GrantedAssets',
@@ -57,9 +59,11 @@ export default {
tableConfig: {
url: this.tableUrl,
hasTree: true,
columnsExtra: ['view_account'],
columnsExclude: ['spec_info'],
columnShow: {
min: ['name', 'address', 'accounts']
columnsShow: {
min: ['name', 'address', 'accounts'],
default: ['name', 'address', 'platform', 'view_account', 'connectivity']
},
columnsMeta: {
name: {
@@ -70,7 +74,13 @@ export default {
},
actions: {
has: false
}
},
view_account: {
label: this.$t('assets.Account'),
formatter: AccountInfoFormatter,
width: '100px'
},
connectivity: connectivityMeta
}
},
headerActions: {

View File

@@ -0,0 +1,71 @@
<template>
<el-row :gutter="24">
<el-col :md="20" :sm="22">
<ListTable v-bind="config" />
</el-col>
</el-row>
</template>
<script>
import ListTable from '@/components/Table/ListTable/index.vue'
import { toM2MJsonParams } from '@/utils/jms'
export default {
name: 'AssetJsonTab',
components: {
ListTable
},
props: {
object: {
type: Object,
default: () => {}
}
},
data() {
const [key, value] = toM2MJsonParams(this.object.assets)
return {
config: {
headerActions: {
hasLeftActions: false,
hasImport: false,
hasExport: false
},
tableConfig: {
url: `/api/v1/assets/assets/?${key}=${value}`,
columns: ['name', 'address', 'platform',
'type', 'is_active'
],
columnsMeta: {
name: {
label: this.$t('assets.Asset'),
formatter: (row) => {
const to = {
name: 'AssetDetail',
params: { id: row.id }
}
if (this.$hasPerm('assets.view_asset')) {
return <router-link to={to} class='text-link'>{row.name}</router-link>
} else {
return <span>{row.name}</span>
}
}
},
actions: {
has: false
}
}
}
}
}
},
computed: {
iUrl() {
return `/api/v1/users/users/`
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,91 @@
<template>
<el-row :gutter="24">
<el-col :md="20" :sm="22">
<ListTable v-bind="config" />
</el-col>
</el-row>
</template>
<script>
import ListTable from '@/components/Table/ListTable/index.vue'
import { toM2MJsonParams } from '@/utils/jms'
export default {
name: 'User',
components: {
ListTable
},
props: {
object: {
type: Object,
default: () => {}
}
},
data() {
const [key, value] = toM2MJsonParams(this.object.users)
return {
config: {
headerActions: {
hasLeftActions: false,
hasImport: false,
hasExport: false
},
tableConfig: {
url: `/api/v1/users/users/?${key}=${value}`,
columns: [
'name', 'username', 'groups', 'system_roles',
'org_roles', 'source', 'is_valid'
],
columnsMeta: {
name: {
label: this.$t('common.Name'),
formatter: (row) => {
const to = {
name: 'UserDetail',
params: { id: row.id }
}
if (this.$hasPerm('users.view_user')) {
return <router-link to={to} class='text-link'>{row.name}</router-link>
} else {
return <span>{row.name}</span>
}
}
},
system_roles: {
label: this.$t('users.SystemRoles'),
formatter: (row) => {
return row['system_roles'].map(item => item['display_name']).join(', ') || '-'
},
filters: [],
columnKey: 'system_roles'
},
org_roles: {
label: this.$t('users.OrgRoles'),
formatter: (row) => {
return row['org_roles'].map(item => item['display_name']).join(', ') || '-'
},
filters: [],
columnKey: 'org_roles',
has: () => {
return this.$store.getters.hasValidLicense && !this.currentOrgIsRoot
}
},
actions: {
has: false
}
}
}
}
}
},
computed: {
iUrl() {
return `/api/v1/users/users/`
}
}
}
</script>
<style scoped>
</style>

View File

@@ -13,7 +13,11 @@
placement="bottom"
>
{{ activity.content }}
<el-link v-if="activity.detail_url" type="primary" @click.native="onClick(activity.r_type, activity.detail_url)">
<el-link
v-if="activity['detail_url']"
type="primary"
@click.native="onClick(activity)"
>
{{ $tc('common.Detail') }}
</el-link>
</el-timeline-item>
@@ -26,8 +30,8 @@
</template>
<script>
import IBox from '@/components/IBox'
import DiffDetail from '@/components/Dialog/DiffDetail'
import IBox from '@/components/IBox/index.vue'
import DiffDetail from '@/components/Dialog/DiffDetail.vue'
import { openTaskPage } from '@/utils/jms'
export default {
@@ -66,7 +70,9 @@ export default {
}
})
},
onClick(type, taskUrl) {
onClick(activity) {
const type = activity['r_type']
const taskUrl = activity['detail_url']
if (type === 'O') {
this.$axios.get(taskUrl).then(
res => {

View File

@@ -0,0 +1,234 @@
<template>
<Dialog
:close-on-click-modal="false"
:destroy-on-close="true"
:show-cancel="false"
:show-confirm="false"
:title="title"
:visible.sync="visible"
class="dialog-content"
v-bind="$attrs"
width="600px"
@confirm="visible = false"
v-on="$listeners"
>
<div v-if="confirmTypeRequired === 'relogin'">
<el-row :gutter="24" style="margin: 0 auto;">
<el-col :md="24" :sm="24">
<el-alert
:title="$tc('auth.ReLoginTitle')"
center
style="margin-bottom: 20px;"
type="error"
/>
</el-col>
</el-row>
<el-row :gutter="24" style="margin: 0 auto;">
<el-col :md="24" :sm="24">
<el-button class="confirm-btn" size="mini" type="primary" @click="logout">
{{ this.$t('auth.ReLogin') }}
</el-button>
</el-col>
</el-row>
</div>
<div v-else>
<el-row :gutter="24" style="margin: 0 auto;">
<el-col :md="24" :sm="24" :span="24" class="add">
<el-select
v-model="subTypeSelected"
style="width: 100%; margin-bottom: 20px;"
@change="handleSubTypeChange"
>
<el-option
v-for="item of subTypeChoices"
:key="item.name"
:disabled="item.disabled"
:label="item.display_name"
:value="item.name"
/>
</el-select>
</el-col>
</el-row>
<el-row :gutter="24" style="margin: 0 auto;">
<el-col :md="24" :sm="24" style="display: flex; margin-bottom: 20px;">
<el-input
v-model="secretValue"
:placeholder="inputPlaceholder"
:show-password="showPassword"
@keyup.enter.native="handleConfirm"
/>
<span v-if="subTypeSelected === 'sms'" style="margin: -1px 0 0 20px;">
<el-button
:disabled="smsBtnDisabled"
size="mini"
style="line-height:20px; float: right;"
type="primary"
@click="sendSMSCode"
>
{{ smsBtnText }}
</el-button>
</span>
</el-col>
</el-row>
<el-row :gutter="24" style="margin: 10px auto;">
<el-col :md="24" :sm="24">
<el-button class="confirm-btn" size="mini" type="primary" @click="handleConfirm">
{{ this.$t('common.Confirm') }}
</el-button>
</el-col>
</el-row>
</div>
</Dialog>
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import { encryptPassword } from '@/utils/crypto'
export default {
name: 'UserConfirmDialog',
components: {
Dialog
},
props: {
url: {
type: String,
default: ''
},
handler: {
type: Function,
default: null
}
},
data() {
return {
title: this.$t('common.CurrentUserVerify'),
smsWidth: 0,
subTypeSelected: '',
inputPlaceholder: '',
smsBtnText: this.$t('common.SendVerificationCode'),
smsBtnDisabled: false,
confirmTypeRequired: '',
subTypeChoices: [],
secretValue: '',
visible: false,
callback: null,
cancel: null,
processing: false
}
},
computed: {
showPassword() {
return this.confirmTypeRequired === 'password'
}
},
mounted() {
this.$eventBus.$on('showConfirmDialog', this.performConfirm)
},
beforeDestroy() {
this.$eventBus.$off('showConfirmDialog', this.performConfirm)
},
methods: {
handleSubTypeChange(val) {
this.inputPlaceholder = this.subTypeChoices.filter(item => item.name === val)[0]?.placeholder
this.smsWidth = val === 'sms' ? 6 : 0
},
performConfirm: _.throttle(function({ response, callback, cancel }) {
if (this.processing || this.visible) {
return
}
this.processing = true
this.callback = callback
this.cancel = cancel
this.$log.debug('perform confirm action')
const confirmType = response.data?.code
const confirmUrl = '/api/v1/authentication/confirm/'
this.$axios.get(confirmUrl, { params: { confirm_type: confirmType }}).then((data) => {
this.confirmTypeRequired = data.confirm_type
if (this.confirmTypeRequired === 'relogin') {
this.$axios.post(confirmUrl, { 'confirm_type': 'relogin', 'secret_key': 'x' }).then(() => {
this.callback()
this.visible = false
}).catch(() => {
this.title = this.$t('auth.NeedReLogin')
this.visible = true
})
return
}
this.subTypeChoices = data.content
const defaultSubType = this.subTypeChoices.filter(item => !item.disabled)[0]
this.subTypeSelected = defaultSubType.name
this.inputPlaceholder = defaultSubType.placeholder
this.visible = true
}).catch((err) => {
const data = err.response?.data
const msg = data?.error || data?.detail || data?.msg || this.$t('common.GetConfirmTypeFailed')
this.$message.error(msg)
this.cancel(err)
}).finally(() => {
this.processing = false
})
}, 300),
logout() {
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
},
sendSMSCode() {
this.$axios.post(`/api/v1/authentication/mfa/select/`, { type: 'sms' }).then(res => {
this.$message.success(this.$tc('common.VerificationCodeSent'))
let time = 60
const interval = setInterval(() => {
const originText = this.smsBtnText
this.smsBtnText = this.$t('common.Pending') + `: ${time}`
this.smsBtnDisabled = true
time -= 1
if (time === 0) {
this.smsBtnText = originText
this.smsBtnDisabled = false
clearInterval(interval)
}
}, 1000)
})
},
handleConfirm() {
if (this.confirmTypeRequired === 'relogin') {
return this.logout()
}
if (this.subTypeSelected === 'otp' && this.secretValue.length !== 6) {
return this.$message.error(this.$tc('common.MFAErrorMsg'))
}
const data = {
confirm_type: this.confirmTypeRequired,
mfa_type: this.confirmTypeRequired === 'mfa' ? this.subTypeSelected : '',
secret_key: this.confirmTypeRequired === 'password' ? encryptPassword(this.secretValue) : this.secretValue
}
this.$axios.post(`/api/v1/authentication/confirm/`, data).then(res => {
this.callback()
this.secretValue = ''
this.visible = false
})
}
}
}
</script>
<style lang="scss" scoped>
.dialog-content >>> .el-dialog__footer {
padding: 0;
}
.dialog-content >>> .el-dialog {
padding: 8px;
.el-dialog__body {
padding-top: 30px;
padding-bottom: 30px;
}
}
.confirm-btn {
width: 100%;
line-height: 20px;
}
</style>

View File

@@ -17,6 +17,9 @@ export default {
default: null
}
},
data() {
return {}
},
computed: {
displayValue() {
if ([null, undefined, ''].includes(this.value)) {
@@ -64,8 +67,19 @@ export default {
}
},
render(h) {
let formatterData = ''
if (typeof this.formatter === 'function') {
return this.formatter(this.item, this.value)
const data = this.formatter(this.item, this.value)
if (data instanceof Promise) {
data.then(res => {
formatterData = res
})
} else {
formatterData = data
}
return (
<span>{formatterData}</span>
)
}
if (this.value instanceof Array) {
const newArr = this.value || []

View File

@@ -1,10 +1,10 @@
<template>
<DetailCard v-if="!loading" :items="items" v-bind="$attrs" />
<DetailCard v-if="!loading && hasObject && items.length > 0" :items="items" v-bind="$attrs" />
</template>
<script>
import DetailCard from './index'
import { toSafeLocalDateStr } from '@/utils/common'
import DetailCard from './index.vue'
import { copy, toSafeLocalDateStr } from '@/utils/common'
export default {
name: 'AutoDetailCard',
@@ -33,6 +33,10 @@ export default {
formatters: {
type: Object,
default: () => ({})
},
nested: {
type: String,
default: null
}
},
data() {
@@ -41,19 +45,49 @@ export default {
loading: true
}
},
computed: {
iObject() {
if (this.nested) {
return this.object[this.nested] || {}
} else {
return this.object
}
},
hasObject() {
return Object.keys(this.iObject).length > 0
}
},
async mounted() {
await this.optionAndGenFields()
this.loading = false
},
methods: {
defaultFormatter(fields) {
const formatter = {}
for (const name of fields) {
formatter[name] = function(item, val) {
if (val === '-') {
return <span>{'-'}</span>
}
return (<span style={{ cursor: 'pointer' }} onClick={() => copy(val)}>
{val}
</span>)
}
}
return formatter
},
async optionAndGenFields() {
const data = await this.$store.dispatch('common/getUrlMeta', { url: this.url })
const remoteMeta = data.actions['GET'] || {}
let remoteMeta = data.actions['GET'] || {}
if (this.nested) {
remoteMeta = remoteMeta[this.nested]?.children || {}
}
let fields = this.fields
fields = fields || Object.keys(remoteMeta)
const defaultExcludes = ['org_id']
const excludes = (this.excludes || []).concat(defaultExcludes)
fields = fields.filter(item => !excludes.includes(item))
const defaultFormatter = this.defaultFormatter(fields)
for (const name of fields) {
if (typeof name === 'object') {
this.items.push(name)
@@ -67,21 +101,33 @@ export default {
continue
}
let value = this.object[name]
let value = this.iObject[name]
const label = fieldMeta.label
if (Array.isArray(value)) {
if (typeof value[0] === 'object') {
value.forEach(item => {
const fieldName = `${name}.${item.name}`
if (excludes.includes(fieldName)) {
return
}
this.items.push({
key: item.label,
value: item.value
const firstValue = value[0]
if (firstValue.hasOwnProperty('name')) {
value.forEach(item => {
const fieldName = `${name}.${item.name}`
if (excludes.includes(fieldName)) {
return
}
this.items.push({
key: item.label,
value: item.value
})
})
})
} else {
value.forEach((item, index) => {
const v = Object.entries(item).map(([key, value]) => `${key}:${value}`).join(', ')
const data = { value: v }
if (index === 0) {
data['key'] = label
}
this.items.push(data)
})
}
} else if (typeof value[0] === 'string') {
value.forEach((item, index) => {
let data = {}
@@ -107,9 +153,9 @@ export default {
} else if (fieldMeta.type === 'labeled_choice') {
value = value?.['label']
} else if (fieldMeta.type === 'related_field' || fieldMeta.type === 'nested object') {
value = value['name']
value = value?.['name']
} else if (fieldMeta.type === 'm2m_related_field') {
value = value.map(item => item['name']).join(', ')
value = value?.map(item => item['name']).join(', ')
} else if (fieldMeta.type === 'boolean') {
value = value ? this.$t('common.Yes') : this.$t('common.No')
}
@@ -125,7 +171,7 @@ export default {
const item = {
key: label,
value: value,
formatter: this.formatters[name]
formatter: this.formatters[name] || defaultFormatter[name]
}
this.items.push(item)
}

View File

@@ -1,8 +1,8 @@
<template>
<IBox :title="title" :fa="fa">
<IBox :fa="fa" :title="title">
<el-form class="content" label-position="left" label-width="25%">
<el-form-item v-for="item in items" :key="item.key" :label="item.key">
<ItemValue class="item-value" :value="item.value" v-bind="item" />
<el-form-item v-for="item in iItems" :key="item.key" :label="item.key">
<ItemValue :value="item.value" class="item-value" v-bind="item" />
</el-form-item>
</el-form>
<slot />
@@ -10,8 +10,8 @@
</template>
<script>
import IBox from '../IBox'
import ItemValue from './ItemValue'
import IBox from '../../IBox/index.vue'
import ItemValue from './ItemValue.vue'
export default {
name: 'DetailCard',
@@ -35,6 +35,13 @@ export default {
type: String,
default: 'left'
}
},
data() {
return {
iItems: this.items.filter(item => {
return !item.hasOwnProperty('has') || item.has === true
})
}
}
}
</script>

View File

@@ -1,6 +1,6 @@
<template>
<IBox :type="type" :title="title" v-bind="$attrs">
<table style="width: 100%;table-layout:fixed;" class="CardTable">
<IBox :title="title" :type="type" v-bind="$attrs">
<table class="CardTable" style="width: 100%;table-layout:fixed;">
<tr>
<td colspan="2">
<Select2 ref="select2" v-model="select2.value" :disabled="iDisabled" v-bind="select2" />
@@ -9,7 +9,7 @@
<slot />
<tr>
<td colspan="2">
<el-button :type="type" size="small" :loading="submitLoading" :disabled="iDisabled" @click="addObjects">
<el-button :disabled="iDisabled" :loading="submitLoading" :type="type" size="small" @click="addObjects">
{{ $t('common.Add') }}
</el-button>
</td>
@@ -17,12 +17,12 @@
<template v-if="showHasObjects">
<tr v-for="obj of iHasObjects" :key="obj.value" class="item">
<td style="width: 100%;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">
<el-tooltip style="margin: 4px;" effect="dark" :content="obj.label" placement="left">
<el-tooltip :content="obj.label" effect="dark" placement="left" style="margin: 4px;">
<b>{{ obj.label }}</b>
</el-tooltip>
</td>
<td>
<el-button size="mini" :disabled="iDisabled" type="danger" style="float: right" @click="removeObject(obj)">
<el-button :disabled="iDisabled" size="mini" style="float: right" type="danger" @click="removeObject(obj)">
<i class="fa fa-minus" />
</el-button>
</td>
@@ -30,7 +30,7 @@
</template>
<tr v-if="params.hasMore && showHasMore" class="item">
<td colspan="2">
<el-button :type="type" :disabled="iDisabled" size="small" style="width: 100%" @click="loadMore">
<el-button :disabled="iDisabled" :type="type" size="small" style="width: 100%" @click="loadMore">
<i class="fa fa-arrow-down" />
{{ $t('common.More') }}
</el-button>
@@ -41,10 +41,11 @@
</template>
<script>
import Select2 from '../FormFields/Select2'
import IBox from '../IBox'
import Select2 from '@/components/Form/FormFields/Select2.vue'
import IBox from '../../IBox/index.vue'
import { createSourceIdCache } from '@/api/common'
import { mapGetters } from 'vuex'
export default {
name: 'RelationCard',
components: {
@@ -102,6 +103,10 @@ export default {
type: Function,
default: (obj, that) => {}
},
allowCreate: {
type: Boolean,
default: false
},
onDeleteSuccess: {
type: Function,
default(obj, that) {
@@ -117,6 +122,22 @@ export default {
that.$message.success(that.$t('common.RemoveSuccessMsg'))
}
},
onDeleteFail: {
type: Function,
default(error, that) {
let msg = ''
const data = error.response.data
for (const item of Object.keys(data)) {
const value = data[item]
if (value instanceof Array) {
msg = value.join(',')
} else {
msg = value
}
}
that.$message.error(msg)
}
},
performAdd: {
type: Function,
default: (objects, that) => {}
@@ -129,6 +150,10 @@ export default {
that.$refs.select2.clearSelected()
that.$message.success(that.$t('common.AddSuccessMsg'))
}
},
getHasObjects: {
type: Function,
default: null // (objectIds) => {}
}
},
data() {
@@ -146,7 +171,8 @@ export default {
options: this.objects,
value: this.value,
disabled: this.disabled,
disabledValues: []
disabledValues: [],
allowCreate: this.allowCreate
}
}
},
@@ -241,14 +267,22 @@ export default {
return
}
this.select2.disabledValues = this.hasObjectsId
const resp = await createSourceIdCache(this.hasObjectsId)
this.params.spm = resp.spm
await this.loadHasObjects()
if (this.getHasObjects) {
this.getHasObjects(this.hasObjectsId).then((data) => {
this.iHasObjects = data
})
} else {
const resp = await createSourceIdCache(this.hasObjectsId)
this.params.spm = resp.spm
await this.loadHasObjects()
}
},
removeObject(obj) {
this.performDelete(obj, this).then(
() => this.onDeleteSuccess(obj, this)
)
).catch(error => {
this.onDeleteFail(error, this)
})
},
addObjects() {
const objects = this.$refs.select2.$refs.select.selected.map(item => ({ label: item.label, value: item.value }))

View File

@@ -28,7 +28,10 @@
:command="[option, action]"
v-bind="option"
>
<i v-if="option.fa" :class="'fa ' + option.fa" />
<span v-if="option.fa">
<i v-if="option.fa.startsWith('fa-')" :class="'fa ' + option.fa" />
<svg-icon v-else :icon-class="option.fa" style="font-size: 14px; margin-right: 2px; margin-left: -2px;" />
</span>
{{ option.title }}
</el-dropdown-item>
</template>
@@ -158,7 +161,7 @@ export default {
}
</script>
<style scoped>
<style lang="scss" scoped>
.layout {
display: flex;
justify-content: center;
@@ -186,4 +189,22 @@ export default {
.el-button-ungroup .action-item:first-child {
margin-left: 0;
}
::v-deep .more-batch-processing {
&.el-dropdown-menu__item--divided {
margin-top: 0;
border-top: none;
color: #909399;
cursor: auto;
font-size: 12px;
line-height: 30px;
border-bottom: 1px solid #E4E7ED;
&:before {
height: 0;
}
}
&.el-dropdown-menu__item:not(.is-disabled):hover {
color: #909399;
background-color: #FFFFFF;
}
}
</style>

View File

@@ -13,8 +13,7 @@
<div v-else>
<el-table
:data="diff"
height="500"
style="width: 100%"
class="diffTable"
>
<el-table-column
:label="$tc('audits.ChangeField')"
@@ -66,7 +65,7 @@ export default {
},
data() {
return {
diff: '',
diff: [],
detailVisible: false
}
},
@@ -84,7 +83,7 @@ export default {
</script>
<style scoped>
<style lang='scss' scoped>
.el-tag {
width: 100%;
white-space: normal;
@@ -94,4 +93,13 @@ export default {
.el-table::before {
background-color: inherit;
}
.diffTable {
width: 100%;
max-height: 80vh;
& >>> td {
padding: 5px 0 !important;
}
}
</style>

View File

@@ -1,19 +1,21 @@
<template>
<el-dialog
:append-to-body="true"
:modal-append-to-body="true"
:title="title"
:top="top"
:width="iWidth"
class="dialog"
:append-to-body="true"
:modal-append-to-body="true"
v-bind="$attrs"
v-on="$listeners"
>
<slot />
<div v-loading="loadingStatus">
<slot />
</div>
<div slot="footer" class="dialog-footer">
<slot name="footer">
<el-button v-if="showCancel" @click="onCancel">{{ cancelTitle }}</el-button>
<el-button v-if="showConfirm" type="primary" :loading="loadingStatus" @click="onConfirm">
<el-button v-if="showCancel && showButtons" @click="onCancel">{{ cancelTitle }}</el-button>
<el-button v-if="showConfirm && showButtons" :disabled="loadingStatus" type="primary" @click="onConfirm">
{{ confirmTitle }}
</el-button>
</slot>
@@ -29,16 +31,6 @@ export default {
type: String,
default: 'Title'
},
showCancel: {
type: Boolean,
default: true
},
cancelTitle: {
type: String,
default() {
return this.$t('common.Cancel')
}
},
top: {
type: String,
default: '3vh'
@@ -51,29 +43,46 @@ export default {
type: Boolean,
default: true
},
loadingStatus: {
type: Boolean,
default: false
},
confirmTitle: {
type: String,
default() {
return this.$t('common.Confirm')
}
},
showCancel: {
type: Boolean,
default: true
},
cancelTitle: {
type: String,
default() {
return this.$t('common.Cancel')
}
},
showButtons: {
type: Boolean,
default: true
},
loadingStatus: {
type: Boolean,
default: false
},
maxWidth: {
type: String,
default: '1200px'
}
},
data() {
return {}
return {
}
},
computed: {
iWidth() {
return this.$store.getters.isMobile ? '80%' : this.width
return this.$store.getters.isMobile ? '1000px' : this.width
}
},
mounted() {
},
methods: {
onCancel() {
this.$emit('cancel')
@@ -88,7 +97,11 @@ export default {
<style lang="scss" scoped>
.dialog >>> .el-dialog {
border-radius: 0.3em;
max-width: 1500px;
max-width: min(100vw, 1500px);
.el-icon-circle-check {
display: none;
}
&__header {
box-sizing: border-box;
@@ -99,6 +112,10 @@ export default {
&__body {
padding: 20px 30px;
&:has(.el-table) {
background: #f3f3f4;
}
}
&__footer {
@@ -107,9 +124,14 @@ export default {
justify-content: flex-end;
}
}
@media (max-width: 900px) {
.dialog >>> .el-dialog {
max-width: calc(100% - 30px);
}
}
.dialog-footer >>> button.el-button {
font-size: 13px;
padding: 10px 20px;
}
</style>

View File

@@ -1,16 +1,19 @@
<template>
<DataForm
v-if="!loading"
:disabled="disabled"
:fields="iFields"
:form="value"
style="margin-left: -26%;margin-right: -6%"
v-bind="kwargs"
@change="updateValue($event)"
@input="updateValue($event)"
v-on="$listeners"
/>
</template>
<script>
import DataForm from '@/components/DataForm'
import DataForm from '@/components/Form/DataForm/index.vue'
export default {
name: 'NestedField',
@@ -37,6 +40,8 @@ export default {
},
data() {
return {
loading: false,
formJson: JSON.stringify(this.value),
kwargs: {
hasReset: false,
hasSaveContinue: false,
@@ -65,7 +70,26 @@ export default {
return fields
}
},
watch: {
value: {
handler(val) {
const valJson = JSON.stringify(val)
// value
if (valJson !== this.formJson) {
this.loading = true
setTimeout(() => {
this.loading = false
}, 10)
}
},
deep: true
}
},
methods: {
updateValue(val) {
this.formJson = JSON.stringify(val)
this.$emit('input', val)
},
objectToString(obj) {
let data = ''
// eslint-disable-next-line prefer-const

View File

@@ -23,9 +23,9 @@
</template>
<script>
import DataForm from '../DataForm'
import FormGroupHeader from '@/components/FormGroupHeader'
import { FormFieldGenerator } from '@/components/AutoDataForm/utils'
import DataForm from '../DataForm/index.vue'
import FormGroupHeader from '@/components/Form/FormGroupHeader/index.vue'
import { FormFieldGenerator } from '@/components/Form/AutoDataForm/utils'
export default {
name: 'AutoDataForm',
@@ -67,6 +67,9 @@ export default {
}
},
computed: {
dataForm() {
return this.$refs.dataForm
},
iForm() {
const iForm = {}
Object.entries(this.form).forEach(([key, value]) => {

View File

@@ -1,12 +1,12 @@
import Vue from 'vue'
import Select2 from '@/components/FormFields/Select2'
import ObjectSelect2 from '@/components/FormFields/NestedObjectSelect2'
import NestedField from '@/components/AutoDataForm/components/NestedField'
import Switcher from '@/components/FormFields/Switcher'
import rules from '@/components/DataForm/rules'
import BasicTree from '@/components/FormFields/BasicTree'
import JsonEditor from '@/components/FormFields/JsonEditor'
import ObjectSelect2 from '@/components/Form/FormFields/NestedObjectSelect2.vue'
import NestedField from '@/components/Form/AutoDataForm/components/NestedField.vue'
import Switcher from '@/components/Form/FormFields/Switcher.vue'
import rules from '@/components/Form/DataForm/rules'
import BasicTree from '@/components/Form/FormFields/BasicTree.vue'
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
import { assignIfNot } from '@/utils/common'
import TagInput from '@/components/Form/FormFields/TagInput.vue'
export class FormFieldGenerator {
constructor(emit) {
@@ -44,13 +44,14 @@ export class FormFieldGenerator {
break
case 'field':
type = ''
field.component = Select2
field.component = ObjectSelect2
if (fieldRemoteMeta.required) {
field.el.clearable = false
}
if (fieldRemoteMeta.child && fieldRemoteMeta.child.type === 'nested object') {
field.component = ObjectSelect2
}
field.el.label = field.label
// if (fieldRemoteMeta.child && fieldRemoteMeta.child.type === 'nested object') {
// field.component = ObjectSelect2
// }
break
case 'string':
type = 'input'
@@ -66,11 +67,16 @@ export class FormFieldGenerator {
type = ''
field.component = Switcher
break
case 'list':
type = 'input'
field.component = TagInput
break
case 'object_related_field':
field.component = ObjectSelect2
break
case 'm2m_related_field':
field.component = ObjectSelect2
field.el.label = field.label
break
case 'nested object':
type = 'nestedField'
@@ -80,6 +86,10 @@ export class FormFieldGenerator {
field.el = { ...field.el, ...fieldMeta }
field.el.fields = this.generateNestFields(field, fieldMeta, fieldRemoteMeta)
field.el.errors = {}
field.hidden = () => {
const hidden = fieldMeta['hiddenFields'] || (() => field.el.fields.length === 0)
return hidden(fieldMeta, fieldRemoteMeta, field.el.fields)
}
break
default:
type = 'input'
@@ -98,9 +108,12 @@ export class FormFieldGenerator {
generateNestFields(field, fieldMeta, fieldRemoteMeta) {
const fields = []
const nestedFields = fieldMeta.fields || []
let nestedFields = fieldMeta.fields || []
const nestedFieldsMeta = fieldMeta.fieldsMeta || {}
const nestedFieldsRemoteMeta = fieldRemoteMeta.children || {}
if (nestedFields === '__all__') {
nestedFields = Object.keys(nestedFieldsRemoteMeta)
}
for (const name of nestedFields) {
const f = this.generateField(name, nestedFieldsMeta, nestedFieldsRemoteMeta)
fields.push(f)
@@ -167,7 +180,7 @@ export class FormFieldGenerator {
id: groupTitle,
title: groupTitle,
fields: _fields,
name: _fields[0].id
name: _fields[0]?.id
}
this.groups.push(group)
return _fields
@@ -175,6 +188,9 @@ export class FormFieldGenerator {
generateFields(_fields, fieldsMeta, remoteFieldsMeta) {
let fields = []
if (_fields === '__all__') {
_fields = Object.keys(remoteFieldsMeta)
}
for (let field of _fields) {
if (field instanceof Array) {
const items = this.generateFieldGroup(field, fieldsMeta, remoteFieldsMeta)

View File

@@ -2,7 +2,7 @@
<template>
<div>
<el-tabs type="border-card">
<el-tab-pane v-if="shouldHide('min')" :label="$tc('common.CronTab.min')">
<el-tab-pane v-if="shouldHide('min')" :label="$tc('common.CronTab.min')" class="crontab-panel">
<CrontabMin
ref="cronmin"
:check="checkNumber"
@@ -59,38 +59,38 @@
<td>
<el-input
v-model.trim="contabValueObj.min"
min="0"
max="5"
size="small"
min="0"
onkeyup="value=value.replace(/[^\0-9\-\*\,]/g,'')"
size="mini"
/>
</td>
<td>
<el-input
v-model.trim="contabValueObj.hour"
size="small"
onkeyup="value=value.replace(/[^\0-9\-\*\,]/g,'')"
size="mini"
/>
</td>
<td>
<el-input
v-model.trim="contabValueObj.day"
size="small"
onkeyup="value=value.replace(/[^\0-9\\-\*\,]/g,'')"
size="mini"
/>
</td>
<td>
<el-input
v-model.trim="contabValueObj.month"
size="small"
onkeyup="value=value.replace(/[^\0-9\-\*\,]/g,'')"
size="mini"
/>
</td>
<td>
<el-input
v-model.trim="contabValueObj.week"
size="small"
onkeyup="value=value.replace(/[^\0-9\-\*\,]/g,'')"
size="mini"
/>
</td>
</tbody>
@@ -100,7 +100,7 @@
<div style="font-size: 13px;">{{ contabValueString }}</div>
</div>
</div>
<CrontabResult :ex="contabValueString" />
<CrontabResult :ex="contabValueString" @crontabDiffChange="crontabDiffChangeHandle" />
<div class="pop_btn">
<el-button
@@ -130,7 +130,7 @@ import CrontabWeek from './components/Crontab-Week.vue'
import CrontabResult from './components/Crontab-Result.vue'
export default {
name: 'Vcrontab',
name: 'VCrontab',
components: {
CrontabMin,
CrontabHour,
@@ -167,7 +167,8 @@ export default {
week: '*'
// year: "",
},
newContabValueString: ''
newContabValueString: '',
crontabDiff: 0
}
},
computed: {
@@ -364,6 +365,12 @@ export default {
},
//
submitFill() {
const crontabDiffMin = this.crontabDiff / 1000 / 60
if (crontabDiffMin > 0 && crontabDiffMin < 10) {
const msg = this.$tc('common.crontabDiffError')
this.$message.error(msg)
return
}
this.$emit('fill', this.contabValueString)
this.hidePopup()
},
@@ -381,13 +388,16 @@ export default {
for (const j in this.contabValueObj) {
this.changeRadio(j, this.contabValueObj[j])
}
},
crontabDiffChangeHandle(diff) {
this.crontabDiff = diff
}
}
}
</script>
<style scoped>
<style lang='scss' scoped>
.pop_btn {
text-align: center;
float: right;
margin-top: 20px;
}
@@ -453,6 +463,12 @@ export default {
overflow-y: auto;
}
.crontab-panel {
> > > .el-input-number {
margin: 0 5px
}
}
.el-form-item--mini.el-form-item,
.el-form-item--small.el-form-item {
margin-bottom: 10px;

View File

@@ -10,15 +10,15 @@
<el-form-item>
<el-radio v-model="radioValue" :label="3">
{{ this.$t('common.CronTab.from') }}
<el-input-number v-model="cycle01" :min="0" :max="31" /> -
<el-input-number v-model="cycle02" :min="0" :max="31" /> {{ this.$t('common.CronTab.day') }}
<el-input-number v-model="cycle01" :max="31" :min="0" size="mini" /> -
<el-input-number v-model="cycle02" :max="31" :min="0" size="mini" /> {{ this.$t('common.CronTab.day') }}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model="radioValue" :label="4">
{{ this.$t('common.CronTab.every') }}
<el-input-number v-model="average02" :min="1" :max="31" /> {{ this.$t('common.CronTab.day') }}{{ this.$t('common.CronTab.executeOnce') }}
<el-input-number v-model="average02" :max="31" :min="1" size="mini" /> {{ this.$t('common.CronTab.day') }}{{ this.$t('common.CronTab.executeOnce') }}
</el-radio>
</el-form-item>
@@ -27,8 +27,8 @@
{{ this.$t('common.CronTab.appoint') }}
<el-select
v-model="checkboxList"
clearable
:placeholder="$tc('common.CronTab.manyChoose')"
clearable
multiple
style="width:100%"
>

View File

@@ -10,15 +10,15 @@
<el-form-item>
<el-radio v-model="radioValue" :label="2">
{{ this.$t('common.CronTab.from') }}
<el-input-number v-model="cycle01" :min="0" :max="60" /> -
<el-input-number v-model="cycle02" :min="0" :max="60" /> {{ this.$t('common.CronTab.hour') }}
<el-input-number v-model="cycle01" :max="60" :min="0" size="mini" /> -
<el-input-number v-model="cycle02" :max="60" :min="0" size="mini" /> {{ this.$t('common.CronTab.hour') }}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model="radioValue" :label="3">
{{ this.$t('common.CronTab.every') }}
<el-input-number v-model="average02" :min="1" :max="60" /> {{ this.$t('common.CronTab.hour') }}{{ this.$t('common.CronTab.executeOnce') }}
<el-input-number v-model="average02" :max="60" :min="1" size="mini" /> {{ this.$t('common.CronTab.hour') }}{{ this.$t('common.CronTab.executeOnce') }}
</el-radio>
</el-form-item>
@@ -27,8 +27,8 @@
{{ this.$t('common.CronTab.appoint') }}
<el-select
v-model="checkboxList"
clearable
:placeholder="$tc('common.CronTab.manyChoose')"
clearable
multiple
style="width:100%"
>

View File

@@ -7,18 +7,11 @@
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model="radioValue" :label="2">
{{ this.$t('common.CronTab.from') }}
<el-input-number v-model="cycle01" :min="0" :max="60" /> -
<el-input-number v-model="cycle02" :min="0" :max="60" /> {{ this.$t('common.CronTab.min') }}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model="radioValue" :label="3">
{{ this.$t('common.CronTab.from') }}
<el-input-number v-model="average02" :min="1" :max="60" /> {{ this.$t('common.CronTab.min') }}{{ this.$t('common.CronTab.executeOnce') }}
<el-input-number v-model="average02" :max="60" :min="1" size="mini" />
{{ this.$t('common.CronTab.min') }}{{ this.$t('common.CronTab.executeOnce') }}
</el-radio>
</el-form-item>
@@ -27,13 +20,13 @@
{{ this.$t('common.CronTab.appoint') }}
<el-select
v-model="checkboxList"
clearable
:placeholder="$tc('common.CronTab.manyChoose')"
clearable
multiple
style="width:100%"
size="small"
style="width:100%"
>
<el-option v-for="item in 60" :key="item" :value="item-1">{{ item-1 }}</el-option>
<el-option v-for="item in 60" :key="item" :value="item-1">{{ item - 1 }}</el-option>
</el-select>
</el-radio>
</el-form-item>
@@ -158,7 +151,7 @@ export default {
</script>
<style scoped>
.el-form-item--small.el-form-item {
margin-bottom: 10px;
}
.el-form-item--small.el-form-item {
margin-bottom: 10px;
}
</style>

View File

@@ -10,15 +10,15 @@
<el-form-item>
<el-radio v-model="radioValue" :label="2">
{{ this.$t('common.CronTab.from') }}
<el-input-number v-model="cycle01" :min="1" :max="12" /> -
<el-input-number v-model="cycle02" :min="1" :max="12" /> {{ this.$t('common.CronTab.month') }}
<el-input-number v-model="cycle01" :max="12" :min="1" size="mini" /> -
<el-input-number v-model="cycle02" :max="12" :min="1" size="mini" /> {{ this.$t('common.CronTab.month') }}
</el-radio>
</el-form-item>
<el-form-item>
<el-radio v-model="radioValue" :label="3">
{{ this.$t('common.CronTab.every') }}
<el-input-number v-model="average02" :min="1" :max="12" /> {{ this.$t('common.CronTab.month') }}{{ this.$t('common.CronTab.executeOnce') }}
<el-input-number v-model="average02" :max="12" :min="1" size="mini" /> {{ this.$t('common.CronTab.month') }}{{ this.$t('common.CronTab.executeOnce') }}
</el-radio>
</el-form-item>
@@ -27,8 +27,8 @@
{{ this.$t('common.CronTab.appoint') }}
<el-select
v-model="checkboxList"
clearable
:placeholder="$tc('common.CronTab.manyChoose')"
clearable
multiple
style="width:100%"
>

View File

@@ -14,6 +14,7 @@
<script>
import parser from 'cron-parser'
import { toSafeLocalDateStr } from '@/utils/common'
export default {
name: 'CrontabResult',
props: {
@@ -51,6 +52,10 @@ export default {
const cur = interval.next().toString()
this.resultList.push(toSafeLocalDateStr(cur))
}
const first = new Date(this.resultList[0])
const second = new Date(this.resultList[1])
const diff = Math.abs(second - first)
this.$emit('crontabDiffChange', diff)
} catch (error) {
this.isShow = false
// debug(error, 'error')

View File

@@ -10,8 +10,8 @@
<el-form-item>
<el-radio v-model="radioValue" :label="3">
{{ this.$t('common.CronTab.cycleFromWeek') }}
<el-input-number v-model="cycle01" :min="1" :max="7" /> -
<el-input-number v-model="cycle02" :min="1" :max="7" />
<el-input-number v-model="cycle01" :max="7" :min="1" size="mini" /> -
<el-input-number v-model="cycle02" :max="7" :min="1" size="mini" />
</el-radio>
</el-form-item>
@@ -20,12 +20,14 @@
{{ this.$t('common.CronTab.appoint') }}
<el-select
v-model="checkboxList"
clearable
:placeholder="$tc('common.CronTab.manyChoose')"
clearable
multiple
style="width:100%"
>
<el-option v-for="(item,index) of weekList" :key="index" :value="index+1">{{ item }}</el-option>
<el-option v-for="(item,index) of weekList" :key="index" :value="index === 6 ? 0 : (index + 1)">
{{ item }}
</el-option>
</el-select>
</el-radio>
</el-form-item>

View File

@@ -1,13 +1,19 @@
<template>
<div>
<div class="box">
<el-input v-model="input" clearable @focus="showDialog" @clear="onClear" />
<el-input v-model="input" clearable @clear="onClear" @focus="showDialog" />
</div>
<el-dialog :title="$tc('common.CronTab.newCron')" :visible.sync="showCron" top="8vh" width="580px" append-to-body>
<el-dialog
:title="$tc('common.CronTab.newCron')"
:visible.sync="showCron"
append-to-body
top="8vh"
width="650px"
>
<Crontab
:expression="expression"
@hide="showCron = false"
@fill="crontabFill"
@hide="showCron = false"
/>
</el-dialog>
</div>

View File

@@ -7,13 +7,11 @@
v-bind="data.attrs"
>
<template v-if="data.helpTips" #label>
<el-tooltip placement="bottom" effect="light" popper-class="help-tips">
<div slot="content" v-html="data.helpTips" />
<el-button style="padding: 0">
<i class="fa fa-info-circle" />
</el-button>
</el-tooltip>
{{ data.label }}
<el-tooltip placement="top" effect="light" popper-class="help-tips">
<div slot="content" v-html="data.helpTips" />
<i class="fa fa-question-circle-o" />
</el-tooltip>
</template>
<template v-if="readonly && hasReadonlyContent">
<div
@@ -70,7 +68,8 @@
:key="opt.label"
v-bind="opt"
:label="'value' in opt ? opt.value : opt.label"
>{{ opt.label }}</el-radio>
>{{ opt.label }}
</el-radio>
</template>
</custom-component>
<div v-if="data.helpText" class="help-block" v-html="data.helpText" />

View File

@@ -38,11 +38,11 @@
v-if="defaultButton"
:disabled="!canSubmit"
:loading="isSubmitting"
size="small"
:size="submitBtnSize"
type="primary"
@click="submitForm('form')"
>
{{ $t('common.Submit') }}
{{ submitBtnText }}
</el-button>
</el-form-item>
</ElFormRender>
@@ -73,6 +73,16 @@ export default {
type: Boolean,
default: true
},
submitBtnSize: {
type: String,
default: 'small'
},
submitBtnText: {
type: String,
default() {
return this.$t('common.Submit')
}
},
hasSaveContinue: {
type: Boolean,
default: true
@@ -101,6 +111,9 @@ export default {
}
},
computed: {
elForm() {
return this.$refs.form
},
mobile() {
return this.$store.state.app.device === 'mobile'
},
@@ -123,8 +136,8 @@ export default {
})
},
//
resetForm(formName) {
this.$refs[formName].resetFields()
resetForm() {
this.$refs['form'].resetFields()
},
handleClick(button) {
const callback = button.callback || function(values, form) {
@@ -133,6 +146,9 @@ export default {
const form = this.$refs['form']
const values = form.getFormValue()
callback(values, form, button)
},
getFormValue() {
return this.$refs.form.getFormValue()
}
}
}

View File

@@ -18,7 +18,8 @@ export const IpCheck = {
required: true,
validator: (rule, value, callback) => {
value = value?.trim()
if (/^[\w://.?-]+$/.test(value)) {
const urlRegExp = /^[\w://.?=&#-]+$/
if (urlRegExp.test(value)) {
callback()
} else {
callback(new Error(i18n.t('common.FormatError')))
@@ -52,13 +53,27 @@ export const matchAlphanumericUnderscore = {
trigger: ['blur', 'change']
}
// 不能包含()
export const MatchExcludeParenthesis = {
validator: (rule, value, callback) => {
value = value?.trim()
if (!/^[^()]*$/.test(value)) {
callback(new Error(i18n.t('common.notParenthesis')))
} else {
callback()
}
},
trigger: ['blur', 'change']
}
export default {
IpCheck,
Required,
RequiredChange,
EmailCheck,
specialEmojiCheck,
matchAlphanumericUnderscore
matchAlphanumericUnderscore,
MatchExcludeParenthesis
}
export const JsonRequired = {

View File

@@ -0,0 +1,76 @@
<template>
<div>
<el-radio-group v-model="type" @input="handleTypeChange">
<el-radio v-for="tp of types" :key="tp.name" :label="tp.name">
{{ tp.label }}
</el-radio>
</el-radio-group>
<Select2 v-if="type === 'spec'" v-model="selected" v-bind="select2" @change="onChangeEmit" />
</div>
</template>
<script>
import Select2 from '@/components/Form/FormFields/Select2.vue'
export default {
name: 'AllOrSpec',
components: { Select2 },
props: {
value: {
type: [Array],
default: () => ([])
},
select2: {
type: Object,
default: () => ({})
},
resource: {
type: String,
default: ''
}
},
data() {
return {
type: 'all', // all, selected
types: [
{ name: 'all', label: this.$t('common.All') },
{ name: 'spec', label: this.$t('common.Spec') + this.$t('common.WordSep') + this.resource }
],
selected: []
}
},
computed: {
iValue() {
if (this.type === 'all') {
return ['all']
} else {
return this.selected
}
}
},
mounted() {
if (!this.value || this.value.length === 0) {
return
}
if (this.value.indexOf('all') > -1) {
this.type = 'all'
} else {
this.type = 'spec'
this.selected = this.value
}
},
methods: {
onChangeEmit() {
this.$emit('input', this.iValue)
},
handleTypeChange() {
this.$emit('input', this.iValue)
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,62 @@
<template>
<div>
<GenericCreateUpdateForm
class="attr-form"
v-bind="formConfig"
@submit="onSubmit"
/>
<DataTable :config="tableConfig" class="attr-list" />
</div>
</template>
<script>
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm'
import DataTable from '@/components/Table/DataTable/index.vue'
export default {
name: 'AttrInput',
components: { DataTable, GenericCreateUpdateForm },
props: {
formConfig: {
type: Object,
default: () => ({})
},
tableConfig: {
type: Object,
default: () => ({})
},
beforeSubmit: {
type: Function,
default: (val) => { return true }
}
},
data() {
return {}
},
methods: {
onSubmit(value) {
if (this.beforeSubmit(value)) {
const clonedValue = JSON.parse(JSON.stringify(value))
this.tableConfig.totalData.push(clonedValue)
this.$emit('submit', this.tableConfig.totalData)
}
}
}
}
</script>
<style lang="scss" scoped>
.attr-form {
>>> .el-select {
width: 100%;
}
>>> .el-form-item__content {
width: 100%;
}
>>> .form-buttons {
margin: auto;
}
}
</style>

View File

@@ -1,12 +1,14 @@
<template>
<el-tree
:data="iTree"
show-checkbox
node-key="value"
:default-checked-keys="iValue"
:default-expand-all="true"
:default-expanded-keys="iValue"
:default-checked-keys="iValue"
:props="defaultProps"
:render-content="renderContent"
class="el-tree-custom"
node-key="value"
show-checkbox
@check="handleCheckChange"
/>
</template>
@@ -67,11 +69,41 @@ export default {
}
return item
})
},
renderContent(h, { node, data, store }) {
let label = node.label
let helpText = ''
const regex = /(.*?)\s*\((.*?)\)/
const match = label.match(regex)
if (match) {
label = match[1]
helpText = match[2]
}
return (
<span >
<span>{label} </span>
{helpText
? (<el-tooltip content={helpText} placement='top'>
<i class='fa fa-question-circle-o'></i>
</el-tooltip>) : ''}
</span>)
}
}
}
</script>
<style scoped>
<style lang="scss" scoped>
.el-tree-custom >>> {
.help-tips {
margin-left: 10px;
font-size: 12px;
color: #999;
}
.el-tree-node__content:hover {
background-color: inherit;
}
}
</style>

View File

@@ -0,0 +1,36 @@
<template>
<div>
{{ value? trueText : falseText }}
</div>
</template>
<script>
export default {
props: {
value: {
type: [String, Boolean],
default: () => false
},
trueText: {
type: String,
default: function() {
return this.$t('common.Yes')
}
},
falseText: {
type: String,
default: function() {
return this.$t('common.No')
}
}
},
data() {
return {}
}
}
</script>
<style scoped>
</style>

View File

@@ -2,7 +2,7 @@
<div class="code-editor" style="font-size: 12px">
<div class="toolbar">
<div
v-for="(item,index) in toolbar.left"
v-for="(item,index) in iActions"
:key="index"
style="display: inline-block; margin: 0 2px"
>
@@ -16,17 +16,24 @@
>
<i :class="item.icon" style="margin-right: 4px;" />{{ item.name }}
</el-button>
<el-autocomplete
v-if="item.type === 'input' && item.el.autoComplete"
v-if="item.type === 'input' &&item.el && item.el.autoComplete"
v-model="item.value"
:placeholder="item.placeholder"
:fetch-suggestions="item.el.query"
class="inline-input"
size="mini"
@select="item.callback(item.value)"
@change="item.callback(item.value)"
/>
<el-input
v-else-if="item.type==='input'"
v-model="item.value"
:placeholder="item.placeholder"
class="inline-input"
size="mini"
@change="item.callback(item.value)"
/>
<div v-if="item.type==='select' && item.el && item.el.create" class="select-content">
<span class="filter-label">
{{ item.name }}:
@@ -86,6 +93,16 @@
</el-tooltip>
</div>
<div v-if="toolbar.hasOwnProperty('fold')" class="fold">
<el-tooltip :content="$tc('common.MoreActions')" placement="top">
<i
class="fa"
:class="[isFold ? 'fa-angle-double-right': 'fa-angle-double-down']"
@click="onChangeFold"
/>
</el-tooltip>
</div>
<div class="right-side" style="float: right">
<div
v-for="(item,index) in toolbar.right"
@@ -147,9 +164,19 @@ export default {
}
},
data() {
return {}
return {
isFold: true
}
},
computed: {
iActions() {
let actions = this.toolbar.left || {}
const fold = this.toolbar.fold || {}
if (!this.isFold) {
actions = { ...actions, ...fold }
}
return actions
},
iValue: {
get() {
return this.value
@@ -172,6 +199,9 @@ export default {
}
},
methods: {
onChangeFold() {
this.isFold = !this.isFold
},
getLabel(value, items) {
for (const item of items) {
if (item.value === value) {
@@ -198,6 +228,16 @@ export default {
margin-bottom: 5px;
}
.fold {
display: inline-block;
padding-left: 4px;
i {
font-weight: bold;
font-size: 15px;
cursor: pointer;
}
}
> > > .CodeMirror pre.CodeMirror-line,
> > > .CodeMirror-linenumber.CodeMirror-gutter-elt {
line-height: 18px !important;

View File

@@ -41,37 +41,23 @@ export default {
shortcuts: [
{
text: this.$t('common.DateLast24Hours'),
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24)
picker.$emit('pick', [start, end])
}
onClick: (picker) => this.onShortcutClick(picker, 1)
},
{
text: this.$t('common.DateLastWeek'),
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
onClick: (picker) => this.onShortcutClick(picker, 7)
}, {
text: this.$t('common.DateLastMonth'),
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
onClick: (picker) => this.onShortcutClick(picker, 30)
}, {
text: this.$t('common.DateLast3Months'),
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
onClick: (picker) => this.onShortcutClick(picker, 90)
}, {
text: this.$t('common.DateLastHarfYear'),
onClick: (picker) => this.onShortcutClick(picker, 183)
}, {
text: this.$t('common.DateLastYear'),
onClick: (picker) => this.onShortcutClick(picker, 365)
}
]
}
@@ -86,6 +72,12 @@ export default {
this.$log.debug('Date change: ', val)
this.$emit('dateChange', val)
}
},
onShortcutClick(picker, day) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * day)
picker.$emit('pick', [start, end])
}
}
}
@@ -108,7 +100,7 @@ export default {
.el-input__inner {
border: 1px solid #dcdee2;
border-radius: 3px;
height: 32x;
height: 32px;
}
.el-date-editor ::v-deep .el-range-separator {

View File

@@ -0,0 +1,96 @@
<template>
<div>
<div v-for="(command, index) in iValue" :key="index" :prop="'iValue.' + index + '.value'" class="command-item">
<el-input v-model="iValue[index]" size="mini">
<template slot="prepend"> {{ inputTitle + ' ' + (index + 1) }}</template>
</el-input>
<div class="input-button">
<el-button
:disabled="deleteDisabled()"
icon="el-icon-minus"
size="mini"
style="flex-shrink: 0;"
type="danger"
@click="handleDelete(command)"
/>
<el-button
v-if="index === iValue.length - 1"
icon="el-icon-plus"
size="mini"
style="flex-shrink: 0;"
type="primary"
@click="handleAdd()"
/>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: Array,
default: () => ['']
},
inputTitle: {
type: String,
default: () => ''
}
},
data() {
return {
iValue: ['']
}
},
watch: {
iValue: {
handler(v) {
this.$emit('input', Array.from(v))
}
}
},
created() {
this.iValue = Array.from(this.value)
},
methods: {
handleDelete(command) {
const index = this.iValue.indexOf(command)
if (index !== -1) {
this.iValue.splice(index, 1)
}
},
handleAdd() {
this.iValue.push('')
},
deleteDisabled() {
return this.iValue.length <= 1
}
}
}
</script>
<style lang="scss" scoped>
.el-input {
width: 85%;
}
.command-item {
display: flex;
margin: 5px 0;
}
.input-button {
margin-top: 2px;
display: flex;
margin-left: 20px
}
.input-button ::v-deep .el-button.el-button--mini {
height: 25px;
padding: 5px;
}
.el-input-group__append .el-button {
font-size: 14px;
color: #1a1a1a;
padding: 9px 20px;
}
</style>

View File

@@ -0,0 +1,161 @@
<template>
<Dialog
:destroy-on-close="true"
:show-buttons="false"
:title="$tc('common.SelectAttrs')"
v-bind="$attrs"
v-on="$listeners"
>
<div v-if="!loading">
<DataForm
:form="form"
class="attr-form"
v-bind="formConfig"
@submit="onAttrDialogConfirm"
/>
</div>
</Dialog>
</template>
<script>
import DataForm from '@/components/Form/DataForm/index.vue'
import Dialog from '@/components/Dialog/index.vue'
import ValueField from '@/components/Form/FormFields/JSONManyToManySelect/ValueField.vue'
import { attrMatchOptions, typeMatchMapper } from '@/components/const'
export default {
name: 'AttrFormDialog',
components: { Dialog, DataForm },
props: {
attrs: {
type: Array,
default: () => ([])
},
attrsAdded: {
type: Array,
default: () => ([])
},
form: {
type: Object,
default: () => ({})
}
},
data() {
const vm = this
return {
loading: true,
currentValue: '',
formConfig: {
// 为了方便更新,避免去取 fields 的索引
hasSaveContinue: false,
fields: [
{
id: 'name',
label: this.$t('common.AttrName'),
type: 'select',
options: this.attrs.map(attr => {
let disabled = this.attrsAdded.includes(attr.name) && this.form.name !== attr.name
if (attr.disabled) {
disabled = true
}
return { label: attr.label, value: attr.name, disabled: disabled }
}),
on: {
change: ([val], updateForm) => {
// 变化会影响 match 的选项
const attr = this.attrs.find(attr => attr.name === val)
if (!attr) return
const matchOption = vm.updateMatchOptions(attr)
setTimeout(() => {
updateForm({ match: matchOption.value })
}, 10)
}
}
},
{
id: 'match',
label: this.$t('common.Match'),
type: 'select',
options: attrMatchOptions,
on: {
change: ([value], updateForm) => {
// 变化会影响 value 的选项
setTimeout(() => {
this.formConfig.fields[2].el.match = value
}, 10)
}
}
},
{
id: 'value',
label: this.$t('common.AttrValue'),
component: ValueField,
el: {
match: attrMatchOptions[0].value,
attr: this.attrs[0]
},
on: {
input: ([value], updateForm) => {
vm.currentValue = value
}
}
}
]
}
}
},
mounted() {
if (this.form.index === undefined || this.form.index === -1) {
Object.assign(this.form, this.getDefaultAttrForm())
}
this.updateMatchOptions()
this.$log.debug('Attr Form config: ', this.formConfig)
this.loading = false
},
methods: {
getDefaultAttrForm() {
const attrKeys = this.attrs.map(attr => attr.name)
const diff = attrKeys.filter(attr => !this.attrsAdded.includes(attr))
let name = this.attrs[0].name
if (diff.length > 0) {
name = diff[0]
}
return {
name: name,
match: 'exact',
value: '',
rel: 'and'
}
},
onAttrDialogConfirm(form) {
this.$emit('confirm', form)
},
updateMatchOptions(attr) {
if (!attr) {
attr = this.attrs.find(attr => attr.name === this.form.name)
}
if (!attr) return
const attrType = attr.type || 'str'
const matchSupports = typeMatchMapper[attrType]
attrMatchOptions.forEach((option) => {
option.hidden = !matchSupports.includes(option.value)
})
this.formConfig.fields[2].el.attr = attr
const supports = attrMatchOptions.filter(option => !option.hidden)
const matchOption = supports.find(item => item.value === this.form.match) || supports[0]
this.formConfig.fields[2].el.match = matchOption.value
return matchOption
}
}
}
</script>
<style lang="scss" scoped>
.attr-form {
>>> .el-select {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,77 @@
<template>
<Dialog
:destroy-on-close="true"
:show-buttons="false"
:title="$tc('common.MatchResult')"
:v-bind="$attrs"
:v-on="$listeners"
:visible.sync="iVisible"
>
<ListTable v-bind="attrMatchTableConfig" />
</Dialog>
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import ListTable from '@/components/Table/ListTable/index.vue'
export default {
name: 'AttrMatchResultDialog',
components: { ListTable, Dialog },
props: {
url: {
type: String,
default: ''
},
attrs: {
type: Array,
default: () => ([])
},
visible: {
type: Boolean,
default: false
}
},
data() {
return {
attrMatchTableConfig: {
headerActions: {
hasCreate: false,
hasImport: false,
hasExport: false,
hasMoreActions: false
},
tableConfig: {
url: this.url,
columns: this.attrs.filter(item => item.inTable).map(item => {
return {
prop: item.name,
label: item.label,
formatter: item.formatter
}
}),
columnsMeta: {
actions: {
has: false
}
}
}
}
}
},
computed: {
iVisible: {
set(val) {
this.$emit('update:visible', val)
},
get() {
return this.visible
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,110 @@
<template>
<div v-if="!loading">
<TagInput v-if="type === 'array'" :value="iValue" @input="handleInput" />
<Select2 v-else-if="type === 'select'" :value="iValue" v-bind="attr.el" @change="handleInput" @input="handleInput" />
<Switcher v-else-if="type === 'bool'" :value="iValue" @change="handleInput" @input="handleInput" />
<el-input v-else :value="iValue" @input="handleInput" />
</div>
</template>
<script>
import TagInput from '@/components/Form/FormFields/TagInput.vue'
import Select2 from '@/components/Form/FormFields/Select2.vue'
import Switcher from '@/components/Form/FormFields/Switcher.vue'
export default {
name: 'ValueField',
components: { Switcher, TagInput, Select2 },
props: {
value: {
type: [String, Number, Boolean, Array, Object],
default: () => ''
},
match: {
type: String,
default: 'exact'
},
attr: {
type: Object,
default: () => ({})
}
},
data() {
return {
loading: true,
type: 'string'
}
},
computed: {
iValue: {
get() {
const multipleTypes = ['array', 'select']
let newValue = this.value
if (multipleTypes.includes(this.type)) {
if (!Array.isArray(this.value)) {
newValue = []
}
} else if (this.type === 'bool') {
newValue = !!this.value
} else {
if (Array.isArray(this.value)) {
newValue = ''
} else {
newValue = this.value.toString()
}
}
if (this.value !== newValue) {
this.handleInput(newValue)
}
return newValue
}
}
},
watch: {
match() {
this.changeValueType()
},
attr: {
handler() {
this.changeValueType()
},
deep: true
}
},
mounted() {
this.changeValueType()
},
methods: {
handleInput(value) {
this.$emit('input', value)
},
changeValueType() {
this.loading = true
this.type = this.getType()
this.$nextTick(() => {
this.loading = false
})
},
getType() {
const attrType = this.attr.type || 'str'
this.$log.debug('Value field attr type: ', attrType, this.attr, this.match)
if (['m2m', 'fk', 'select'].includes(attrType)) {
return 'select'
} else if (attrType === 'bool') {
return 'bool'
}
if (['in', 'ip_in'].includes(this.match)) {
return 'array'
} else if (this.match.startsWith('m2m')) {
return 'select'
} else {
return 'string'
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,90 @@
<template>
<span v-if="attr.type === 'bool'">
<i v-if="value" class="fa fa-check text-primary" />
<i v-else class="fa fa-times text-danger" />
</span>
<span v-else :title="value">
{{ value }}
</span>
</template>
<script>
import BaseFormatter from '@/components/Table/TableFormatters/base.vue'
import { setUrlParam } from '@/utils/common'
export default {
name: 'ValueFormatter',
extends: BaseFormatter,
props: {
formatterArgsDefault: {
type: Object,
default() {
return {
attrs: {}
}
}
}
},
data() {
const formatterArgs = Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
return {
formatterArgs: formatterArgs,
loading: true,
attr: {},
value: ''
}
},
watch: {
cellValue: {
handler() {
this.getValue()
},
deep: true
},
formatterArgs: {
handler() {
this.getValue()
},
deep: true
},
row: {
handler() {
this.getValue()
},
deep: true
}
},
mounted() {
setTimeout(() => {
this.getValue()
}, 100)
},
methods: {
async getValue() {
this.attr = this.formatterArgs.attrs.find(attr => attr.name === this.row.name)
const match = this.row.match
this.$log.debug('ValueFormatter: ', this.attr, this.row.name)
if (this.attr.type === 'm2m') {
const url = setUrlParam(this.attr.el.url, 'ids', this.cellValue.join(','))
const data = await this.$axios.get(url) || []
if (data.length > 0) {
const displayField = this.attr.el.displayField || 'name'
this.value = data.map(item => item[displayField]).join(', ')
}
} else if (this.attr.type === 'select') {
const options = this.attr.el.options || []
const items = options.filter(item => this.cellValue.includes(item.value))
this.value = items.map(item => item.label).join(', ')
} else if (['in', 'ip_in'].includes(match)) {
this.value = this.cellValue.join(', ')
} else {
this.value = this.cellValue
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,240 @@
<template>
<div>
<el-radio-group v-model="iValue.type" @input="handleTypeChange">
<el-radio v-for="tp of types" :key="tp.name" :label="tp.name">
{{ tp.label }}
</el-radio>
</el-radio-group>
<Select2 v-if="iValue.type === 'ids'" v-model="ids" v-bind="select2" @change="onChangeEmit" />
<div v-if="iValue.type === 'attrs'">
<DataTable :config="tableConfig" class="attr-list" />
<div class="actions">
<el-button size="mini" type="primary" @click="handleAttrAdd">
{{ $t('common.Add') }}
</el-button>
<span style="padding-left: 10px; font-size: 13px">
<span class="help-tips; ">{{ $t('common.MatchedCount') }}:</span>
<a class="text-link" style="padding: 0 5px;" @click="showAttrMatchTable">{{ attrMatchCount }}</a>
</span>
</div>
</div>
<AttrFormDialog
v-if="attrFormVisible"
:attrs="attrs"
:attrs-added="attrsAdded"
:form="attrForm"
:visible.sync="attrFormVisible"
@confirm="handleAttrDialogConfirm"
/>
<AttrMatchResultDialog
v-if="attrMatchTableVisible"
:attrs="attrs"
:url="attrMatchTableUrl"
:visible.sync="attrMatchTableVisible"
/>
</div>
</template>
<script>
import Select2 from '../Select2.vue'
import DataTable from '@/components/Table/DataTable/index.vue'
import ValueFormatter from './ValueFormatter.vue'
import AttrFormDialog from './AttrFormDialog.vue'
import AttrMatchResultDialog from './AttrMatchResultDialog.vue'
import { setUrlParam } from '@/utils/common'
import { attrMatchOptions } from '@/components/const'
import { toM2MJsonParams } from '@/utils/jms'
export default {
name: 'JSONManyToManySelect',
components: { AttrFormDialog, DataTable, Select2, AttrMatchResultDialog },
props: {
value: {
type: Object,
default: () => {
return {
type: 'all'
}
}
},
select2: {
type: Object,
required: true
},
attrs: {
type: Array,
default: () => ([])
},
resource: {
type: String,
default: ''
},
attrTableColumns: {
type: Array,
default: () => (['name'])
}
},
data() {
const tableFormatter = (colName) => {
return (row, col, cellValue) => {
const value = cellValue
switch (colName) {
case 'name':
return this.attrs.find(attr => attr.name === value)?.label || value
case 'match':
return attrMatchOptions.find(opt => opt.value === value).label || value
case 'value':
return Array.isArray(value) ? value.join(', ') : value
default:
return value
}
}
}
return {
iValue: Object.assign({ type: 'all' }, this.value),
attrFormVisible: false,
attrForm: {},
attrMatchCount: 0,
attrMatchTableVisible: false,
attrMatchTableUrl: '',
ids: this.value.ids || [],
editIndex: -1,
types: [
{ name: 'all', label: this.$t('common.All') + this.resource },
{ name: 'ids', label: this.$t('common.Spec') + this.resource },
{ name: 'attrs', label: this.$t('common.SelectByAttr') }
],
tableConfig: {
columns: [
{ prop: 'name', label: this.$t('common.AttrName'), formatter: tableFormatter('name') },
{ prop: 'match', label: this.$t('common.Match'), formatter: tableFormatter('match') },
{ prop: 'value', label: this.$t('common.AttrValue'), formatter: ValueFormatter, formatterArgs: { attrs: this.attrs }},
{ prop: 'action', label: this.$t('common.Action'), align: 'center', width: '120px', formatter: (row, col, cellValue, index) => {
return (
<div className='input-button'>
<el-button
icon='el-icon-edit'
size='mini'
style={{ 'flexShrink': 0 }}
type='primary'
onClick={this.handleAttrEdit({ row, col, cellValue, index })}
/>
<el-button
icon='el-icon-minus'
size='mini'
style={{ 'flexShrink': 0 }}
type='danger'
onClick={this.handleAttrDelete({ row, col, cellValue, index })}
/>
</div>
)
} }
],
totalData: this.value.attrs || [],
hasPagination: false
}
}
},
computed: {
attrsAdded() {
return this.tableConfig.totalData.map(item => item.name)
}
},
watch: {
attrFormVisible(val) {
if (!val) {
this.getAttrsCount()
}
}
},
mounted() {
if (this.value.type === 'attrs') {
this.getAttrsCount()
}
this.$emit('input', this.iValue)
},
methods: {
showAttrMatchTable() {
const [key, value] = this.getAttrFilterKey()
this.attrMatchTableUrl = setUrlParam(this.select2.url, key, value)
this.attrMatchTableVisible = true
},
getAttrFilterKey() {
if (this.tableConfig.totalData.length === 0) return ''
let attrFilter = { type: 'attrs', attrs: this.tableConfig.totalData }
attrFilter = toM2MJsonParams(attrFilter)
return attrFilter
},
getAttrsCount() {
const attrFilter = this.getAttrFilterKey()
if (!attrFilter) {
this.attrMatchCount = 0
return
}
const [key, value] = attrFilter
let url = setUrlParam(this.select2.url, key, value)
url = setUrlParam(url, 'limit', 1)
return this.$axios.get(url).then(res => {
this.attrMatchCount = res.count
})
},
handleAttrEdit({ row, index }) {
return () => {
this.attrForm = Object.assign({ index }, row)
this.editIndex = index
this.attrFormVisible = true
}
},
handleAttrDelete({ index }) {
return () => {
this.tableConfig.totalData.splice(index, 1)
this.getAttrsCount()
}
},
handleAttrAdd() {
this.attrForm = {}
this.editIndex = -1
this.attrFormVisible = true
},
onChangeEmit() {
const tp = this.iValue.type
this.handleTypeChange(tp)
},
handleTypeChange(val) {
switch (val) {
case 'ids':
this.$emit('input', { type: 'ids', ids: this.ids })
break
case 'attrs':
this.$emit('input', { type: 'attrs', attrs: this.tableConfig.totalData })
break
default:
this.$emit('input', { type: 'all' })
break
}
},
handleAttrDialogConfirm(form) {
if (this.editIndex > -1) {
this.tableConfig.totalData.splice(this.editIndex, 1)
}
const allAttrs = this.tableConfig.totalData
// 因为可能 attr 的 name 会重复,所以需要先删除再添加
const setIndex = allAttrs.findIndex(attr => attr.name === form.name)
if (setIndex === -1) {
allAttrs.push(Object.assign({}, form))
} else {
allAttrs.splice(setIndex, 1, Object.assign({}, form))
}
this.attrFormVisible = false
this.onChangeEmit()
}
}
}
</script>
<style lang="scss" scoped>
.attr-list {
width: 99%;
}
</style>

View File

@@ -4,6 +4,7 @@
v-model="resultInfo"
:mode="'code'"
:show-btns="false"
:class="{resize: resize === 'vertical'}"
@json-change="onJsonChange"
@json-save="onJsonSave"
@has-error="onError"
@@ -20,6 +21,13 @@ export default {
value: {
type: [String, Object, Array],
default: () => ({})
},
resize: {
type: String,
validator: (value) => {
return ['none', 'vertical'].indexOf(value) !== -1
},
default: 'vertical'
}
},
data() {
@@ -46,15 +54,21 @@ export default {
},
onError: _.debounce(function(value) {
this.$message.error(this.$tc('common.FormatError'))
}, 1100)
}, 1500)
}
}
</script>
<style lang="scss" scoped>
@import "~@/styles/variables.scss";
@import "~@/styles/variables";
.json-editor {
.resize {
& > > > .jsoneditor {
resize: vertical;
cursor: s-resize;
}
}
& > > > .jsoneditor {
border: 1px solid #e5e6e7;
}

View File

@@ -0,0 +1,97 @@
<template>
<div>
<div v-for="(item, index) in value || []" :key="index" class="value-item">
<el-input :value="item" class="input-z" @input="updateValue($event, index)" />
<div class="input-button">
<el-button
:disabled="disableDelete(item)"
icon="el-icon-minus"
size="mini"
style="flex-shrink: 0;"
type="danger"
@click="handleDelete(index)"
/>
<el-button
:disabled="disableAdd(item, index)"
icon="el-icon-plus"
size="mini"
style="flex-shrink: 0;"
type="primary"
@click="handleAdd(index)"
/>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ListField',
props: {
value: {
type: [Array, String],
default: () => ([])
}
},
data() {
return {
}
},
computed: {
},
mounted() {
const value = this.value
if (!value || !Array.isArray(value) || value.length === 0) {
this.$emit('input', [''])
}
},
methods: {
updateValue(v, index) {
const value = this.value
value[index] = v
this.$emit('input', value)
},
disableDelete() {
return false
},
disableAdd() {
return false
},
handleAdd(index) {
const value = this.value
value.splice(index + 1, 0, '')
this.$emit('input', value)
},
handleDelete(index) {
const value = this.value
value.splice(index, 1)
this.$emit('input', value)
}
}
}
</script>
<style lang="scss" scoped>
.input-z {
flex-shrink: 1;
width: calc(100% - 80px) !important;
}
.value-item {
display: flex;
margin: 4px 0;
}
.input-button {
display: flex;
margin-left: 20px;
margin-top: 4px;
}
.input-button ::v-deep .el-button.el-button--mini {
height: 25px;
padding: 5px;
}
</style>

View File

@@ -9,7 +9,7 @@
</template>
<script>
import Select2 from './Select2'
import Select2 from './Select2.vue'
export default {
name: 'NestedObjectSelect2',

View File

@@ -0,0 +1,106 @@
<template>
<div style="display: block">
<el-button size="mini" type="primary" @click="visible=true">
{{ $t('common.Setting') }}
</el-button>
<Dialog
:destroy-on-close="true"
:title="$tc('common.PasswordRule')"
:visible.sync="visible"
width="600px"
@cancel="handleCancel"
@confirm="handleConfirm"
@open="handleOpen"
>
<AutoDataForm ref="dataform" v-bind="form" />
</Dialog>
</div>
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
export default {
name: 'PasswordRule',
components: { Dialog, AutoDataForm },
props: {
value: {
type: Object,
default: () => ({
length: 16
})
}
},
data() {
return {
visible: false,
form: {
url: '',
hasButtons: false,
hasReset: false,
hasSaveContinue: false,
form: Object.assign({}, this.value),
fields: [
{
id: 'length',
label: this.$t('common.Length'),
type: 'input-number',
el: {
min: 8,
max: 30
}
},
{
id: 'uppercase',
label: this.$t('common.Uppercase'),
type: 'switch'
},
{
id: 'lowercase',
label: this.$t('common.Lowercase'),
type: 'switch'
},
{
id: 'digit',
label: this.$t('common.Digit'),
type: 'switch'
},
{
id: 'symbol',
label: this.$t('common.SpecialSymbol'),
type: 'switch'
},
{
id: 'exclude_symbols',
label: this.$t('common.ExcludeSymbol'),
type: 'input'
}
]
}
}
},
methods: {
handleConfirm() {
const formValue = this.$refs.dataform.dataForm.getFormValue()
this.form.form = formValue
this.$emit('input', formValue)
setTimeout(() => {
this.visible = false
}, 100)
},
handleCancel() {
this.$refs.dataform.dataForm.resetForm()
setTimeout(() => {
this.visible = false
}, 100)
},
handleOpen() {
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,73 @@
<template>
<div>
<el-input v-model="rawValue.phone" :placeholder="$tc('users.inputPhone')" @input="OnInputChange">
<el-select
slot="prepend"
:value="rawValue.code"
:placeholder="$tc('common.Select')"
style="width: 90px;"
@change="OnChange"
>
<el-option
v-for="country in countries"
:key="country.value"
:label="country.value"
:value="country.value"
style="width: 200px;"
>
<span style="float: left">{{ country.name }}</span>
<span style="float: right; font-size: 13px">{{ country.value }}</span>
</el-option>
</el-select>
</el-input>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'PhoneInput',
props: {
value: {
type: [Object, String],
default: () => ({ 'code': '+86', 'phone': '' })
}
},
data() {
return {
rawValue: {}
}
},
computed: {
fullPhone() {
if (!this.rawValue.phone) {
return ''
}
return `${this.rawValue.code}${this.rawValue.phone}`
},
countries: {
get() {
return this.publicSettings.COUNTRY_CALLING_CODES
}
},
...mapGetters(['publicSettings'])
},
mounted() {
this.rawValue = this.value || { code: '+86', phone: '' }
this.$emit('input', this.fullPhone)
},
methods: {
OnChange(countryCode) {
this.rawValue.code = countryCode
this.OnInputChange()
},
OnInputChange() {
this.$emit('input', this.fullPhone)
}
}
}
</script>
<style lang="scss" scoped>
</style>

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