Compare commits

..

327 Commits

Author SHA1 Message Date
Bryan
a861f77609 Merge pull request #4659 from jumpserver/dev
v4.7.0
2025-02-20 10:20:32 +08:00
Chenyang Shen
b8b764c04c Merge pull request #4660 from jumpserver/pr@dev@feat_fix_error_i18n
fix: fix error i18n
2025-02-19 19:07:53 +08:00
Aaron3S
45532a3eb6 fix: fix error i18n 2025-02-19 19:06:41 +08:00
zhaojisen
9e4afd5271 Fixed: Fixed Tabel Export 2025-02-19 17:32:22 +08:00
zhaojisen
e6efd91414 Fixed: Dashboard PRBP 2025-02-18 15:58:08 +08:00
zhaojisen
19469508f7 Fixed: PR-BT 2025-02-18 15:23:16 +08:00
Chenyang Shen
5166287b0e Merge pull request #4650 from jumpserver/pr@dev@feat_add_i18n
feat: add i18n
2025-02-18 15:11:35 +08:00
Aaron3S
494c4c5753 feat: add i18n 2025-02-18 15:08:30 +08:00
jiangweidong
eddff3d75b perf: Optimize the display of labels in strategy actions 2025-02-18 10:27:28 +08:00
jiangweidong
910bef6695 fix: When the scheduled synchronization interval is empty, the submission fails 2025-02-18 10:26:04 +08:00
w940853815
59945ad798 fix: When select_default_value is null, editing the playbook reports an error 2025-02-17 11:19:18 +08:00
zhaojisen
60b8748b95 Fixed: PT-BR languange in Setting Show Tooltip 2025-02-13 17:02:24 +08:00
feng
5708aa25c4 perf: Deepseek 2025-02-13 16:39:47 +08:00
zhaojisen
f2e6f89c08 Fixed: Fixed PT-BR language adaptation issue 2025-02-13 16:27:45 +08:00
zhaojisen
1c222c3ce0 Fixed: PT-BR languange adaptation issue 2025-02-13 16:27:45 +08:00
fit2bot
d6c28b8286 Fixed: Fixed PT-BR language adaptation issue (#4636)
* Fixed: Fixed PT-BR language adaptation issue

* Fiexed: Fixed En languange ACL Page adapation issue

---------

Co-authored-by: zhaojisen <1301338853@qq.com>
2025-02-13 15:55:58 +08:00
feng
d94752a021 perf: chat help text 2025-02-13 11:29:43 +08:00
feng
e93979ab5f perf: perm filter translate 2025-02-10 19:15:30 +08:00
feng
5d5e87595b perf: Login acl del license 2025-02-10 17:44:58 +08:00
zhaojisen
4bc039185c Fixed: Fixed Fix account template redirection error in remote applications 2025-02-10 16:17:46 +08:00
zhaojisen
9e768f4ef4 Fixed: Fixed time and thinking tip 2025-02-08 15:38:25 +08:00
zhaojisen
0e53d7e657 Perf: Support DeepSeek 2025-02-08 15:38:25 +08:00
feng
906a1accd1 feat: Chat ai setting 2025-02-08 15:38:25 +08:00
zhaojisen
1e381b06ee Fixed: Export Button Logic Differentiation 2025-02-07 11:21:25 +08:00
Bryan
e58ec6057c Merge pull request #4600 from jumpserver/dev
v4.6.0
2025-01-15 14:39:21 +08:00
zhaojisen
d1a48a2e56 Perf: Perf Pt Language Style 2025-01-14 15:35:11 +08:00
zhaojisen
5925ac448e Fixed: Fixed the issue that When switching different node export same file 2025-01-13 18:01:16 +08:00
zhaojisen
fdb1dd886b Fixed: Fixed the issue where tag deletion required double-click 2025-01-13 16:30:55 +08:00
jiangweidong
2f9ad17b1f feat: Cloud sync supports syncing tags 2025-01-08 14:34:26 +08:00
zhaojisen
25c7d0a372 Optimize the execution efficiency of data selection 2025-01-06 18:17:06 +08:00
jiangweidong
46898d2419 feat: VMware automatically syncs folders to node 2025-01-03 18:52:26 +08:00
Aaron3S
f2ac6a61ab feat: facelive add license check 2024-12-19 17:50:27 +08:00
Bryan
7ab20c5885 Merge pull request #4537 from jumpserver/dev
v4.5.0
2024-12-19 15:57:44 +08:00
fit2bot
5f904f99b6 perf: login redirect to next page (#4534)
Co-authored-by: ibuler <ibuler@qq.com>
2024-12-19 15:34:47 +08:00
Bai
6fbef03b33 fix: face auth bind/unbind error when open single page 2024-12-19 15:33:39 +08:00
zhaojisen
98aa11bb64 fixed: Remove Unused Style 2024-12-19 15:29:23 +08:00
zhaojisen
fba83f39bd fixed: Style Error 2024-12-19 12:49:30 +08:00
Bai
c3a2c3c23d fix: enter system org while user login 2024-12-19 12:05:45 +08:00
wangruidong
5125038e9c perf: Add default value validation to VariableCreateUpdateForm 2024-12-19 12:00:38 +08:00
zhaojisen
42ca29bdaf fixed: Side Menu text overflow 2024-12-19 11:16:08 +08:00
zhaojisen
1c4893bc22 fixed: Revert Select Strategy 2024-12-19 10:05:26 +08:00
wangruidong
5a26589052 fix: Execute after save sometimes the variable is undefine. 2024-12-18 19:15:47 +08:00
zhaojisen
4c2cdc1232 perf: It fits the PT-BR 2024-12-18 18:43:23 +08:00
zhaojisen
cb6e40bea7 fixed: Fixed an issue where the total number of cross-page multiple selections was the number of selected pages 2024-12-18 18:39:27 +08:00
Bai
4c61a84652 fix: approve ticket special account not found 2024-12-18 16:14:27 +08:00
wangruidong
ffaca80b21 feat: Add help tips to VariableCreateUpdateForm fields 2024-12-18 15:54:06 +08:00
zhaojisen
0f5f6b860b fixed: Optimize the execution efficiency of full selection and reverse selection, and solve the problem of invalid data selection in card 2024-12-18 11:22:41 +08:00
wangruidong
a9ba931bba perf: Add 'labels' column to AssetPermission and Domain tables 2024-12-18 10:55:48 +08:00
wangruidong
23fae59411 feat: Add permission checks for platform editing 2024-12-17 19:10:27 +08:00
Chenyang Shen
320613d15d Merge pull request #4520 from jumpserver/pr@dev@feat_add_translate
feat: add translate
2024-12-17 17:42:07 +08:00
Aaron3S
57ea32e785 feat: add translate 2024-12-17 17:40:44 +08:00
Eric
3e3f80c59b perf: component names use the same letter case 2024-12-17 16:21:31 +08:00
feng
c15ba5b078 fix: tag search display error 2024-12-17 10:32:18 +08:00
wangruidong
651888faab perf: Add 'labels' column to UserGroupList table 2024-12-16 19:00:00 +08:00
feng626
d220047fe2 Revert "perf: Platform global"
This reverts commit bbe80f0292.
2024-12-16 14:09:49 +08:00
feng
bbe80f0292 perf: Platform global 2024-12-16 11:40:35 +08:00
feng
41e73a2c9b perf: Firefox unable to download file 2024-12-13 12:51:52 +08:00
feng
7d89cf9f45 perf: Re-login after csrf expires 2024-12-13 10:23:36 +08:00
Bai
8a048f692f fix: remove log 2024-12-12 18:49:39 +08:00
Bai
800317346c fix: current org is system and no preorg user logout error 2024-12-12 11:35:31 +08:00
wangruidong
4a1e2db7ca fix: Org admin view platform perms 2024-12-11 19:12:57 +08:00
Bai
6702d94ba1 fix: org changed error 2024-12-11 10:37:56 +08:00
Bai
a87d04fb9d fix: user login always change to system org 2024-12-10 19:14:13 +08:00
zhaojisen
367db0daf1 fixed: Fixed a text wrap issue 2024-12-10 16:05:55 +08:00
zhaojisen
583b28a6c4 fixed: Fixed an organization error after entering Settings under Global Organization manually 2024-12-09 17:19:37 +08:00
Chenyang Shen
ec5f2e69eb Merge pull request #4498 from jumpserver/pr@dev@feat_change_face_context_url
feat: change face context url
2024-12-09 16:55:22 +08:00
Aaron3S
d058f0e777 feat: change face context url 2024-12-09 16:53:52 +08:00
老广
89befaad43 Create llm-code-review.yml 2024-12-09 16:20:12 +08:00
zhaojisen
548d0e0670 perf:Optimize the execution efficiency of all and reverse selection 2024-12-05 12:39:17 +08:00
zhaojisen
7b862ee0e1 fixed: Fixed an issue where all select a large number of data cards died 2024-12-04 16:58:45 +08:00
fit2bot
d4e37e9418 fixed: Fixed an issue where multiple selections across pages could not be reversed (#4482)
* fixed: Fixed an issue where multiple selections across pages could not be reversed

* fixed: Fixed an issue where multiple selections across pages could not be reversed

---------

Co-authored-by: zhaojisen <1301338853@qq.com>
2024-12-04 15:08:23 +08:00
Bai
b53b1d1e58 perf: languages settings 2024-12-03 17:01:12 +08:00
fit2bot
0342c73f07 feat: Vault adds Amazon Secrets Manager (#4455)
Co-authored-by: jiangweidong <1053570670@qq.com>
2024-11-29 18:03:26 +08:00
Aaron3S
727d9037d1 feat: move face setting to profile 2024-11-28 18:23:59 +08:00
Bai
b24c244186 perf: remove email format check for email host account 2024-11-27 19:13:28 +08:00
fit2bot
d365aea6a0 perf: add nec and facelive for monitor (#4468)
* perf: add nec and facelive for monitor

* perf: add nec and facelive component log

---------

Co-authored-by: Eric <xplzv@126.com>
2024-11-27 18:51:18 +08:00
zhaojisen
115f1171ad fixed: Fixed an issue with button text overflow in QuickActions 2024-11-27 11:13:51 +08:00
wangruidong
b2b477d4e0 fix: Creating an asset form does not allow deleting the default protocol 2024-11-27 11:01:02 +08:00
wangruidong
1e37ecff11 perf: Add variable management to Job and Adhoc details 2024-11-26 15:05:17 +08:00
wangruidong
9cf87404be perf: Add viewAssetOnlineSessionInfo conf 2024-11-25 15:32:21 +08:00
dependabot[bot]
2e51b0dea1 chore(deps): bump elliptic from 6.5.4 to 6.6.0
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.4 to 6.6.0.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.4...v6.6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-22 15:25:22 +08:00
wangruidong
a1f20c6f92 fix: adhoc and playbook detail page display many '-' 2024-11-22 15:24:21 +08:00
wangruidong
6b6a60b84f refactor: Component name and API endpoint 2024-11-22 15:03:23 +08:00
zhaojisen
4e999176ed fixed: Fixed an issue where the Form button was hidden 2024-11-22 14:51:40 +08:00
wangruidong
76ff273ec7 fix: run playbook job with no variable 2024-11-21 22:01:02 +08:00
Bryan
e47ddb5355 Merge pull request #4451 from jumpserver/dev
v4.4.0
2024-11-21 19:01:11 +08:00
wangruidong
475a77497f fix: disable job has no perms 2024-11-21 18:57:14 +08:00
Bai
5d1301c871 fix: public playbook variables are not displayed by other users 2024-11-21 17:50:59 +08:00
zhaojisen
772f40e13d fixed: Fixed an issue where the Form button was hidden 2024-11-21 14:44:29 +08:00
wangruidong
1b2f7ad28c fix: default value not display 2024-11-21 14:26:39 +08:00
wangruidong
84778881f6 fix: playbook job clone variable error 2024-11-20 23:06:24 +08:00
wangruidong
d6d0338666 perf: Add default value col in Variable & refactor JobUpdateCreate 2024-11-20 21:51:15 +08:00
wangruidong
de6f477d05 fix: playbook clone with variable 2024-11-20 15:35:34 +08:00
wangruidong
03e07ee280 fix: Add error handling in VariableCreateUpdateForm and wrap text in ItemValue 2024-11-20 10:55:51 +08:00
wangruidong
35e2273603 perf: Enhance MFA settings and display 2024-11-20 10:12:56 +08:00
zhaojisen
7a138388b2 fixed: Fixes ESLint issues 2024-11-19 18:05:13 +08:00
Bai
b1f42dd6bf perf: License info remove subscription id 2024-11-19 17:48:49 +08:00
wangruidong
2eeba4ee4f fix: playbook set variable perms 2024-11-19 10:12:52 +08:00
fit2bot
d91579e848 fixed: Fixed an issue where the number of commands overlaps with whether there are command fields (#4437)
* fixed: Fixed an issue where the number of commands overlaps with whether there are command fields

* fixed: Fixed an issue where the number of commands overlaps with whether there are command fields

---------

Co-authored-by: zhaojisen <1301338853@qq.com>
2024-11-18 16:13:06 +08:00
wangruidong
13751f14f3 perf: Variable table overflow show tooltip and CodeEditor cursor position 2024-11-18 15:09:51 +08:00
wangruidong
7ea00e17bd perf: Cancel template selection if the command is empty 2024-11-15 17:58:32 +08:00
wangruidong
ec78370ecc fix: duplicate variable names and job audit perms issue 2024-11-15 16:01:41 +08:00
Aaron3S
c9f62ae5d3 feat: 添加人脸识别 MFA 2024-11-13 17:42:29 +08:00
halo
b80b69dd26 perf: VAULT_BACKEND cannot be modified from the frontend 2024-11-13 17:30:48 +08:00
wangruidong
ac49415061 fix: Solve audit job and variable bugs 2024-11-13 17:30:20 +08:00
wangruidong
9edb64837d fix: Job create cannot be saved without setting interval 2024-11-12 15:37:18 +08:00
wangruidong
671fa65930 perf: Admin and auditor can view and stop task 2024-11-12 11:20:06 +08:00
Eric
0da81ea60d perf: add vnc port for endpoint 2024-11-11 21:58:21 +08:00
Halo
c610d396d6 feat: azure key vault (#4418) 2024-11-11 19:42:16 +08:00
feng
0ed02ca7f7 fix: Modify the platform protocol cursor disappears 2024-11-08 16:48:50 +08:00
feng
c22981e2c1 perf: Translate 2024-11-08 15:34:21 +08:00
fit2bot
b0af35a4b9 perf: Create a job that supports adding node parameters (#4412)
* perf: Create a job that supports adding node parameters

* feat: add variables component

* perf: Update file tip position

* feat: Add variable support to job from template

* feat: Parameters can be set when running job

* feat: Supports setting  variable type

* feat: Supports running adhoc with parameters

* feat: Supports running playbook with parameters

* feat: Support setting variables for scheduled tasks

* perf: Translate

---------

Co-authored-by: wangruidong <940853815@qq.com>
2024-11-07 10:41:07 +08:00
jiangweidong
1d16a165a5 perf: optimization 2024-11-06 15:06:27 +08:00
jiangweidong
f1f4242ad6 perf: Supports automatic release of assets and prevents accidental release of network errors 2024-11-06 15:06:27 +08:00
老广
bff8f10cd5 Create llm-code-review.yml 2024-11-05 18:23:09 +08:00
Bai
305e44ea1c feat: modify asset permission default expired from settings 2024-11-01 18:23:38 +08:00
feng
a636bb2037 perf: List preference translate 2024-10-18 14:32:45 +08:00
Bryan
56aa3caa83 Merge pull request #4410 from jumpserver/dev
v4.3.0
2024-10-17 14:56:18 +08:00
fit2bot
9b6f54c1ed perf: update placeholder (#4409)
* perf: update passkey create

* perf: update placeholder

---------

Co-authored-by: ibuler <ibuler@qq.com>
2024-10-17 11:10:18 +08:00
feng
611341307b fix: ticket duplicate submission 2024-10-16 19:09:18 +08:00
ibuler
f8479c53ff perf: update passkey create 2024-10-16 18:51:56 +08:00
wangruidong
e25bf46659 perf: url is null set remote false 2024-10-16 18:36:12 +08:00
wangruidong
6d07307e56 perf: users use Select2 2024-10-16 16:54:34 +08:00
wangruidong
6fb7fe8fa1 fix: interval field check 2024-10-16 11:01:44 +08:00
zhaojisen
19cd497095 fixed: Restores remote application styles 2024-10-15 17:06:06 +08:00
wangruidong
d858489367 fix: modify getPreference url 2024-10-15 14:13:10 +08:00
feng
341a30ba06 perf: The platform gets activeMenu and adds a default value 2024-10-15 11:32:51 +08:00
ZhaoJiSen
8390fb7429 Merge pull request #4398 from jumpserver/pr@dev@help_tip
perf: Help text automatic line wrapping
2024-10-12 14:40:43 +08:00
feng
5389f1d011 perf: Help text automatic line wrapping 2024-10-12 14:39:15 +08:00
ZhaoJiSen
bde642570f Merge pull request #4397 from jumpserver/pr@dev@perf_reveiver
style: Set special receiver to optimize the scroll wheel display
2024-10-12 11:35:16 +08:00
zhaojisen
3e7b970fe7 style: Set special receiver to optimize the scroll wheel display 2024-10-12 11:32:05 +08:00
ZhaoJiSen
2421e822b4 Merge pull request #4396 from jumpserver/pr@dev@perf_number_style
style: Optimize the number field style
2024-10-11 18:19:35 +08:00
zhaojisen
b2e474e3f6 style: Optimize the number field style 2024-10-11 18:15:46 +08:00
ZhaoJiSen
541836390a Merge pull request #4394 from jumpserver/pr@dev@fix_tab_change
fixed: Fixed command filtering and command group tab switching issues
2024-10-10 18:55:00 +08:00
zhaojisen
bb92e3f22b fixed: Fixed command filtering and command group tab switching issues 2024-10-10 18:51:50 +08:00
ZhaoJiSen
c9ad797b40 Merge pull request #4393 from jumpserver/pr@dev@fix_quick_job_node
fixed: Fixed the shortcut command selection node issue
2024-10-10 18:39:34 +08:00
zhaojisen
2a08310efc fixed: Fixed the shortcut command selection node issue 2024-10-10 18:37:47 +08:00
zhaojisen
3b2803b9a1 fixed: Fixed the shortcut command selection node issue 2024-10-10 18:36:33 +08:00
wangruidong
1b8ac9112e fix: user lack permission to view the type tree 2024-10-10 17:11:13 +08:00
ZhaoJiSen
9cc3dd4de9 Merge pull request #4390 from jumpserver/pr@dev@fix_add_lineHeight
style: Add Line Height
2024-10-10 14:40:30 +08:00
zhaojisen
120ef70eb1 style: Add Line Height 2024-10-10 14:36:01 +08:00
ZhaoJiSen
541f4ebc62 Merge pull request #4389 from jumpserver/pr@dev@fix_remote_app
style: Adjusting the styles of the remote application
2024-10-10 14:17:52 +08:00
zhaojisen
a47636abc7 style: Adjusting the styles of the remote application 2024-10-10 14:11:53 +08:00
ZhaoJiSen
78f6f4b36a Merge pull request #4388 from jumpserver/pr@dev@fix_comments_style
fixed: Fixed the presentation format of asset notes
2024-10-10 11:23:16 +08:00
zhaojisen
5abc0b77cf fixed: Fixed the presentation format of asset notes 2024-10-10 11:18:49 +08:00
ZhaoJiSen
464638e782 Merge pull request #4387 from jumpserver/pr@dev@fix_input_border
style: Fixed the display of the account number input box
2024-10-10 10:46:23 +08:00
zhaojisen
37cfeb2077 style: Fixed the display of the account number input box 2024-10-10 10:43:09 +08:00
ZhaoJiSen
44e297f01a Merge pull request #4386 from jumpserver/pr@dev@fix_tooltip_position
fixed: Fixed issue with sidebar toolTip placement
2024-10-10 09:50:33 +08:00
zhaojisen
febc283e36 fixed: Fixed issue with sidebar toolTip placement 2024-10-10 09:47:05 +08:00
ZhaoJiSen
dae33f55e8 Merge pull request #4385 from jumpserver/pr@dev@fix_special_chara
fixed: Fixed + and - not being special characters
2024-10-09 18:38:23 +08:00
zhaojisen
c62cd27690 fixed: Fixed + and - not being special characters 2024-10-09 18:34:31 +08:00
ZhaoJiSen
83443f8187 Merge pull request #4383 from jumpserver/pr@dev@fix_tab_refresh
fixed: Fixed An issue where adding a tag to asset does not work
2024-10-09 17:26:08 +08:00
zhaojisen
cb46f393e0 fixed: Fixed An issue where adding a tag to asset does not work 2024-10-09 17:17:12 +08:00
wangruidong
ddf5ac2151 fix: Determine the asset connection opening method based on the settings 2024-10-09 16:44:25 +08:00
wangruidong
88173f852a perf: Default endpoint cannot be disabled 2024-10-09 16:43:16 +08:00
wangruidong
d5f16e90e2 perf: add created_by field 2024-10-09 16:15:36 +08:00
ZhaoJiSen
d1c0aca4ff Merge pull request #4380 from jumpserver/pr@dev@fix_font_size
fixed: Fixed an issue where the font size of the resource is too large due to the parent element's font size being overridden
2024-10-09 14:47:42 +08:00
zhaojisen
a42edf17ec fixed: Fixed an issue where the font size of the resource is too large due to the parent element's font size being overridden 2024-10-09 14:32:06 +08:00
feng
dac5dfcd1c perf: Optimize file audit download prompt 2024-09-29 16:12:41 +08:00
ZhaoJiSen
8e6ca146e1 Merge pull request #4353 from jumpserver/dependabot/npm_and_yarn/serve-static-1.16.0
chore(deps-dev): bump serve-static from 1.15.0 to 1.16.0
2024-09-29 10:16:07 +08:00
ZhaoJiSen
9c06d36eab Merge pull request #4351 from jumpserver/dependabot/npm_and_yarn/path-to-regexp-3.3.0
chore(deps): bump path-to-regexp from 2.4.0 to 3.3.0
2024-09-29 10:14:32 +08:00
wisonic
ab7bd574f8 fix: assets proportion tooltip cause page bounced at first time 2024-09-27 17:34:25 +08:00
wangruidong
6007dc8621 perf: Endpoint add is_active field 2024-09-27 16:00:40 +08:00
wangruidong
a0e7c48dc9 perf: adhoc can clone 2024-09-27 14:12:13 +08:00
wangruidong
71dea791bf fix: xterm output truncate 2024-09-26 17:17:23 +08:00
ZhaoJiSen
e1b9184f41 Merge pull request #4372 from jumpserver/pr@dev@fix_perm_list_tree
fixed: Fixed an issue with draggable Tree nodes under asset authorization
2024-09-26 17:02:08 +08:00
zhaojisen
730d47f02a fixed: Fixed an issue with draggable Tree nodes under asset authorization 2024-09-26 16:59:18 +08:00
feng
e82ec68935 perf: The maximum length of the randomly generated password is changed to 36 2024-09-25 10:54:05 +08:00
feng
e8e5975d7f fix: fix after successfully creating an asset, the asset list cannot be partially refreshed 2024-09-24 15:44:55 +08:00
Bryan
19b1dc0dbc Merge pull request #4367 from jumpserver/dev
merge: from dev to master
2024-09-19 19:36:42 +08:00
ZhaoJiSen
3cb9dec978 Merge pull request #4366 from jumpserver/pr@dev@fix_style_fix
style: Restoring styles
2024-09-19 18:21:50 +08:00
zhaojisen
c025441075 style: Restoring styles 2024-09-19 18:20:41 +08:00
wangruidong
050b50fa74 fix: playbook template clone error 2024-09-19 15:07:31 +08:00
ibuler
e26befabc3 perf: filter gateway using new params 2024-09-19 11:24:51 +08:00
wangruidong
8545fc6136 fix: session is_locked show value 2024-09-19 11:20:54 +08:00
ZhaoJiSen
e343e0df9d Merge pull request #4363 from jumpserver/pr@dev@perf_remote_app
style: Adjust the remote application page layout
2024-09-18 21:16:47 +08:00
zhaojisen
74519e6f3c style: Adjust the remote application page layout 2024-09-18 21:15:49 +08:00
feng
1305f90372 perf: TimerExecution translate 2024-09-18 15:06:06 +08:00
feng
bbc3f53c0a perf: User details community version removes user acl 2024-09-18 11:11:53 +08:00
ZhaoJiSen
aa605adbc7 Merge pull request #4359 from jumpserver/pr@dev@fix_show_NaN
fixed: Fixed NaN issue when setting timing interval
2024-09-17 13:42:51 +08:00
zhaojisen
49ea7d0969 fixed: Fixed NaN issue when setting timing interval 2024-09-17 13:34:51 +08:00
feng
4221bdb2ab perf: Postgresql add ssl mode 2024-09-13 17:49:05 +08:00
dependabot[bot]
af4010e299 chore(deps-dev): bump serve-static from 1.15.0 to 1.16.0
Bumps [serve-static](https://github.com/expressjs/serve-static) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/expressjs/serve-static/releases)
- [Changelog](https://github.com/expressjs/serve-static/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/serve-static/compare/v1.15.0...1.16.0)

---
updated-dependencies:
- dependency-name: serve-static
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-12 02:22:33 +00:00
ibuler
9b7c4ed353 chore: lock the pull request when workflow start 2024-09-12 10:22:01 +08:00
dependabot[bot]
879df90503 chore(deps): bump path-to-regexp from 2.4.0 to 3.3.0
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) from 2.4.0 to 3.3.0.
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md)
- [Commits](https://github.com/pillarjs/path-to-regexp/compare/v2.4.0...v3.3.0)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-11 18:05:18 +00:00
wangruidong
41f841532f feat: LDAP HA 2024-09-11 18:26:49 +08:00
feng
0b9f47dd84 feat: Postgresql support ssl 2024-09-11 18:12:33 +08:00
wangruidong
d32a376e8c feat: Support playbook, adhoc share 2024-09-11 18:02:03 +08:00
ibuler
0f8a8845df perf: change gateway gateway tip 2024-09-11 15:26:57 +08:00
wangruidong
8bb2c66b99 perf: Add task description 2024-09-09 18:59:25 +08:00
ibuler
b3d0be2f60 perf: update platform icon 2024-09-06 11:15:07 +08:00
ibuler
5a94ddd976 perf: api can set radio and checkbox tip 2024-09-06 11:14:03 +08:00
ibuler
7b9627a80b perf: support change gateway platform 2024-09-06 11:10:46 +08:00
feng
5fcfecc060 perf: Acl action add notify and warn 2024-09-06 11:08:21 +08:00
wangruidong
f4d7a2283c feat: Add announcement start and end dates 2024-09-06 10:56:03 +08:00
feng
40bb8410d3 perf: System settings task cleanup add change secret and push record retention days 2024-09-02 10:18:13 +08:00
fit2bot
45344ac620 perf: ldap import user error msg (#4333)
* perf: ldap import user error msg

* fix: Duplication on new code

---------

Co-authored-by: wangruidong <940853815@qq.com>
2024-08-30 17:01:18 +08:00
feng
5b894c9667 perf: View the internal message and convert the content into markdown 2024-08-29 17:28:18 +08:00
ZhaoJiSen
d89bd15b6d Merge pull request #4337 from jumpserver/pr@dev@fix_table_reload
fixed: Fixed an issue where tables could not be refreshed
2024-08-29 13:06:40 +08:00
zhaojisen
5e640dd45c fixed: Fixed an issue where tables could not be refreshed 2024-08-29 11:50:23 +08:00
fit2bot
09aa750794 chore: update checkout action 2024-08-28 11:34:09 +08:00
吴小白
744b215800 feat: bump node from 16.20 to 20.15 2024-08-27 11:07:37 +08:00
ibuler
f4e11da053 perf: revert pre org if logout 2024-08-23 15:40:16 +08:00
ibuler
2e6b5706d5 perf: remove tab in query 2024-08-21 18:04:19 +08:00
feng
440a5b27ef perf: Asset authorization request asset optimization mini limit 300 2024-08-21 11:34:17 +08:00
feng
932e16844e perf: Role detail title translate 2024-08-20 11:29:02 +08:00
feng
aff2e439dd perf: Ztree cannot be dragged under global organization 2024-08-20 10:45:21 +08:00
Bryan
77ef172a23 Merge pull request #4320 from jumpserver/dev
v4.1.0
2024-08-15 21:42:42 +08:00
ZhaoJiSen
a618794c14 Merge pull request #4319 from jumpserver/pr@dev@fix_assets_tree
fixed: asset tree height
2024-08-15 17:51:17 +08:00
zhaojisen
8dd66c400a fixed: asset tree height 2024-08-15 17:49:14 +08:00
ZhaoJiSen
ac3ce464b7 Merge pull request #4318 from jumpserver/pr@dev@fix_aoumnt_formatter
fixed: Fixed an issue where the details list data would not refresh after the data was updated
2024-08-15 17:43:07 +08:00
zhaojisen
4d01c6dabe fixed: Fixed an issue where the details list data would not refresh after the data was updated 2024-08-15 17:39:59 +08:00
ZhaoJiSen
47eeac23eb Merge pull request #4317 from jumpserver/pr@dev@ticket_flow
perf: Ticket flow detail error
2024-08-15 16:42:58 +08:00
feng
592f783245 perf: Ticket flow detail error 2024-08-15 16:42:01 +08:00
Ewall555
c34ac6a56f perf: Translate ticket cancel button 2024-08-15 15:50:36 +08:00
Bai
9a7162cc9e perf: i18n for ldap user import 2024-08-15 15:49:25 +08:00
ZhaoJiSen
5edf2f34dd Merge pull request #4313 from jumpserver/pr@dev@fix_cloud_button
fixed: Fixed the issue that can be edited and deleted under the default cloud synchronization policy
2024-08-15 12:07:42 +08:00
zhaojisen
859d824195 fixed: Fixed the issue that can be edited and deleted under the default cloud synchronization policy 2024-08-15 12:06:36 +08:00
ZhaoJiSen
c382cc633e Merge pull request #4312 from jumpserver/pr@dev@fix_quick_job
fixed: Fixed the issue that the shortcut command could not get the account
2024-08-15 11:06:21 +08:00
zhaojisen
653e24773f fixed: Fixed the issue that the shortcut command could not get the account 2024-08-15 10:59:13 +08:00
wangruidong
ae489610ee perf: Translate batch approval 2024-08-14 19:28:11 +08:00
ZhaoJiSen
b5e85c66e4 Merge pull request #4311 from jumpserver/pr@dev@fix_icon_show
fixed: common icon
2024-08-14 19:01:49 +08:00
zhaojisen
4060fa4c4a fixed: common icon 2024-08-14 18:59:13 +08:00
ZhaoJiSen
9630b79daa Merge pull request #4310 from jumpserver/pr@dev@fix_common_icon
fixed: common icon
2024-08-14 18:55:52 +08:00
zhaojisen
ad93917387 fixed: common icon 2024-08-14 18:52:40 +08:00
feng
691590da7b perf: Cloud sync task interval to number 2024-08-14 18:52:19 +08:00
feng
71828fd78b perf: Translate 2024-08-14 16:38:04 +08:00
ZhaoJiSen
5a45f31ac4 Merge pull request #4305 from jumpserver/pr@dev@fix_ticket_tree
fixed: Fixed missing action book nodes in ticket details
2024-08-14 16:32:37 +08:00
zhaojisen
44901eee23 fixed: Fixed missing action book nodes in ticket details 2024-08-14 16:30:49 +08:00
ZhaoJiSen
2d91fa107d Merge pull request #4304 from jumpserver/pr@dev@fix_storage_textarea
fixed:Fixed the common item in the storage Settings to be a text field
2024-08-14 15:48:20 +08:00
zhaojisen
7a6ade4bd9 fixed:Fixed the common item in the storage Settings to be a text field 2024-08-14 15:46:31 +08:00
wangruidong
951778668f perf: profile page go to some page error 2024-08-13 18:16:04 +08:00
zhaojisen
0aca23bf38 fixed: internationalization issues 2024-08-13 17:50:49 +08:00
wangruidong
9b97dc9a74 perf: add auto generate ssh-key 2024-08-13 17:43:47 +08:00
feng
32cd030479 fix: Cloud sync task regions display 2024-08-09 17:40:50 +08:00
wangruidong
607bb476db perf: asset name edit add icon 2024-08-09 16:33:58 +08:00
feng
1d676ec9b7 perf: Domain detail gateway translate 2024-08-09 10:45:52 +08:00
wangruidong
614ebb4121 fix: platform page asset not allow go to detail page 2024-08-08 17:34:42 +08:00
ZhaoJiSen
8c2719a95d Merge pull request #4291 from jumpserver/pr@dev@fix_load
perf: add button loading status
2024-08-08 15:00:45 +08:00
zhaojisen
c6b0a958a6 perf: add button loading status 2024-08-08 14:53:42 +08:00
ZhaoJiSen
3edebf3f4f Merge pull request #4290 from jumpserver/pr@dev@fix_loading
perf: add button loading status
2024-08-08 14:47:26 +08:00
zhaojisen
f63405978e perf: add button loading status 2024-08-08 14:46:39 +08:00
fit2bot
c582c8de98 chore: remove build test 2024-08-08 13:50:57 +08:00
fit2bot
c44e79ed3b perf: add button loading status (#4285)
* perf: add button loading status

* perf: add button loading status

* perf: add button loading status

---------

Co-authored-by: zhaojisen <1301338853@qq.com>
2024-08-08 11:37:38 +08:00
feng
fc7d5b9c29 perf: Asset authorization: The number of accounts displayed is incorrect ignore @SPEC 2024-08-07 17:51:15 +08:00
Eric
335febd4a5 perf: update session page i18n 2024-08-07 17:45:52 +08:00
wangruidong
5ea2918fe7 feat: Allow users to customize asset name and comment 2024-08-07 16:47:54 +08:00
ZhaoJiSen
7668d10ba5 Merge pull request #4282 from jumpserver/pr@dev@fix_continue_add
fixed: Restore continues to add the ability to clear the form
2024-08-06 14:20:53 +08:00
zhaojisen
df15d141da fixed: Restore continues to add the ability to clear the form 2024-08-06 14:14:37 +08:00
wangruidong
e2f4bbde79 feat: support configuring multiple SSH keys for users 2024-08-05 15:21:14 +08:00
feng
3ca7de42af perf: Optimization settings auth org select2 2024-08-02 17:51:25 +08:00
Bai
e6a577ba9b perf: assets internal display for false 2024-08-02 16:04:59 +08:00
feng
6290179460 perf: Third-party user login settings default organization 2024-08-02 15:52:42 +08:00
ZhaoJiSen
4f3c9e9353 Merge pull request #4276 from jumpserver/pr@dev@fix_add_continue
fixed: Fixed the issue when adding
2024-08-01 14:57:54 +08:00
zhaojisen
aa28a8f765 fixed: Fixed the issue when adding 2024-08-01 14:57:01 +08:00
ZhaoJiSen
98d0a52fa2 Merge pull request #4275 from jumpserver/pr@dev@fix_filter_param
fixed: Filtering parameters
2024-07-31 14:30:54 +08:00
zhaojisen
8d9e1ffadb fixed: Filtering parameters 2024-07-31 14:29:49 +08:00
zhaojisen
b3be312a4d fixed: Fixed page refresh when tree nodes are clicked 2024-07-31 11:23:07 +08:00
feng
7c41d148aa perf: Support WeCom DingTalk FeiShu Lark Slack attribute mapping 2024-07-30 17:51:55 +08:00
feng
6dee911642 perf: Approval process role selection supports multiple strategies 2024-07-30 16:37:32 +08:00
wangruidong
6dd35e5173 feat: add assets amount field to platform page 2024-07-26 13:44:22 +08:00
halo
fdaa33f68d perf: Email service authentication username is optional 2024-07-26 11:39:31 +08:00
feng
0b813bb719 perf: Del profile scheduled request 2024-07-25 18:22:26 +08:00
fit2bot
7ba6b8d4e4 perf: fix session i18n default (#4258)
* perf: fix session i18n default

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: Bai <baijiangjie@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Bryan <jiangjie.bai@fit2cloud.com>
2024-07-24 10:03:24 +08:00
ibuler
ef99e25fde perf: workflow 2024-07-23 17:12:37 +08:00
github-actions[bot]
d1e7b907e3 perf: Update Dockerfile with new base image tag 2024-07-23 16:52:36 +08:00
ibuler
83f7dda5e7 perf: change base image 2024-07-23 16:52:36 +08:00
ibuler
80f929fdea perf: change build
perf: using cache

perf: using pnpm
2024-07-23 16:13:36 +08:00
feng
1ec3d02933 perf: You can modify sudo permissions multiple times 2024-07-22 18:17:50 +08:00
ZhaoJiSen
fdf148cc2b Merge pull request #4250 from jumpserver/pr@dev@fix_validation_refresh
fixed: Fixed an issue where the validation date does not refresh
2024-07-22 14:57:28 +08:00
ZhaoJiSen
c9e6ef89dc Merge pull request #4249 from jumpserver/pr@dev@fix_add_continue
fixed: Fixed an issue where the data would not refresh after the Save and Continue Add button was successfully added
2024-07-22 14:57:03 +08:00
zhaojisen
59f9f88f8b fixed: Fixed an issue where the validation date does not refresh 2024-07-22 14:48:51 +08:00
zhaojisen
16417ae843 fixed: Fixed an issue where the data would not refresh after the Save and Continue Add button was successfully added 2024-07-22 10:31:54 +08:00
ZhaoJiSen
58bd0a17c8 Merge pull request #4246 from jumpserver/pr@dev@fix_asset_refresh
fixed: Fixed an issue with asset refresh when clicked
2024-07-18 12:36:55 +08:00
zhaojisen
c4a1eb6938 fixed: Fixed an issue with asset refresh when clicked 2024-07-18 12:33:28 +08:00
feng
b7d9031889 perf: Translate 2024-07-17 18:05:19 +08:00
feng
96b29a9dc2 perf: Feishu lark support attributes settings 2024-07-17 17:00:02 +08:00
zhaojisen
336e176639 style: Code Editor style change 2024-07-17 16:52:03 +08:00
zhuoyang
2df4a9d66d fix:vue.config.js 2024-07-17 16:11:17 +08:00
feng
45a102cff1 perf: Translate 2024-07-17 11:40:17 +08:00
ZhaoJiSen
57ebfa0812 Merge pull request #4235 from jumpserver/pr@dev@fix_editor_style
style: Editor style adjustments
2024-07-17 11:14:53 +08:00
zhaojisen
4e9dd57efe style: Editor style adjustments 2024-07-17 11:13:05 +08:00
ZhaoJiSen
3fcc4ca160 Merge pull request #4229 from jumpserver/pr@dev@fix_params_push
fixed: Fixed the issue that parameter push parameters could not be saved
2024-07-17 11:01:02 +08:00
feng626
958760811c Merge pull request #4230 from jumpserver/pr@dev@ticket
perf: Del ticket comment mistake date
2024-07-17 10:54:50 +08:00
feng
b60e0251c2 perf: Del ticket comment mistake date 2024-07-17 10:51:40 +08:00
zhaojisen
3db0ed756e fixed: Fixed the issue that parameter push parameters could not be saved 2024-07-17 10:51:15 +08:00
feng626
453c4b1e4e Merge pull request #4222 from jumpserver/pr@dev@download
perf: Downloading files does not trigger the beforeunload event
2024-07-16 12:35:32 +08:00
feng
bf4d8ce7a6 perf: Downloading files does not trigger the beforeunload event 2024-07-16 12:34:18 +08:00
wangruidong
34effdbe15 perf: 社区版移除magnus 2024-07-15 19:27:16 +08:00
zhaojisen
274db466f2 fixed:Fixed an issue where the user was unable to enter non-MD content 2024-07-15 19:09:05 +08:00
ZhaoJiSen
cbe697f9dc Merge pull request #4218 from jumpserver/pr@dev@fix_job_tooltip
fixed: Fixed the tooltip shortcut command interface issue
2024-07-15 18:31:28 +08:00
zhaojisen
fb7acb100e fixed: Fixed the tooltip shortcut command interface issue 2024-07-15 18:28:15 +08:00
feng626
ebb36847df Merge pull request #4213 from jumpserver/pr@dev@cloud_sync_install
perf: Cloud sync instance execution add trigger
2024-07-15 18:02:46 +08:00
feng
beba4f1994 perf: Cloud sync instance execution add trigger 2024-07-11 17:15:58 +08:00
ZhaoJiSen
452796e3f5 Merge pull request #4212 from jumpserver/pr@dev@fix_params_remain
fixed: Fixed an issue where push parameters could not be saved when adding an asset account
2024-07-11 15:34:42 +08:00
zhaojisen
02a7969d90 fixed: Fixed an issue where push parameters could not be saved when adding an asset account 2024-07-11 15:32:19 +08:00
zhaojisen
798dbec151 fixed :Event bus shutdown 2024-07-11 15:20:51 +08:00
ibuler
0e11a56e37 perf: table search two times, one init one search 2024-07-11 15:20:01 +08:00
ibuler
be5344344c perf: view cache 2024-07-11 15:18:41 +08:00
ibuler
e75d711e0a perf: nest field change may be lead blink 2024-07-11 15:17:07 +08:00
wangruidong
46ee116f3e fix: role display error when update user 2024-07-11 15:07:29 +08:00
ZhaoJiSen
f4b304338f Merge pull request #4207 from jumpserver/pr@dev@fix_switch_showTooltip
fixed: Fixed tooltip issue with switch button
2024-07-11 10:48:46 +08:00
zhaojisen
e93e78307c fixed: Fixed tooltip issue with switch button 2024-07-11 10:47:33 +08:00
wangruidong
d22079446f perf: profile improvement mfa disabled 2024-07-10 14:21:06 +08:00
ibuler
d57b99b00c perf: phone code api 2024-07-09 19:24:07 +08:00
ZhaoJiSen
e7321356af Merge pull request #4197 from jumpserver/pr@dev@fix_card_style
style: Fine-tuning styles
2024-07-09 18:58:39 +08:00
zhaojisen
a0edc2c527 style: Fine-tuning styles 2024-07-09 18:54:45 +08:00
ibuler
10ebcfd64d perf: tags display inline 2024-07-09 16:08:55 +08:00
ibuler
a3fedb9697 perf: license display for community edition 2024-07-09 16:03:18 +08:00
吴小白
1eb8a18c66 fix: FromAsCasing keywords 2024-07-09 16:02:18 +08:00
wangruidong
f491c57c34 perf: display newly invited users at the top of the list 2024-07-09 15:54:31 +08:00
ZhaoJiSen
50b7f54652 Merge pull request #4193 from jumpserver/pr@dev@fix_menu_heighlight
fixed: Missing menu highlight in command group detail page
2024-07-09 11:12:00 +08:00
zhaojisen
5bf298a5bf fixed: Missing menu highlight in command group detail page 2024-07-09 11:07:01 +08:00
wangruidong
124ff9a8c2 fix: foot content allow blank 2024-07-08 19:24:34 +08:00
ZhaoJiSen
387ab4f1b3 Merge pull request #4186 from jumpserver/pr@dev@fix_item_empty
fixed: Fixed an issue where the field is empty
2024-07-08 18:55:00 +08:00
zhaojisen
d6fbdfa7ea fixed: Fixed an issue where the field is empty 2024-07-08 18:51:10 +08:00
feng626
cd4260fd8d Merge pull request #4184 from jumpserver/pr@dev@translate
perf: Translate
2024-07-05 16:37:29 +08:00
feng
baeece62d3 perf: Translate 2024-07-05 16:36:24 +08:00
wangruidong
576c8f5891 fix: update platform with update protocols 2024-07-05 15:07:10 +08:00
feng626
5e97600807 Merge pull request #4182 from jumpserver/pr@dev@translate
perf: Translate
2024-07-04 18:15:04 +08:00
feng
4e2eb3a37d perf: Translate 2024-07-04 18:13:49 +08:00
feng626
e92173f8e8 Merge pull request #4181 from jumpserver/pr@dev@applet
perf: Virtualapp and appprovider tab perm
2024-07-04 16:46:45 +08:00
feng
412d9c804e perf: Virtualapp and appprovider tab perm 2024-07-04 16:46:00 +08:00
ZhaoJiSen
b84a725d4a Merge pull request #4179 from jumpserver/pr@dev@fix_split_job
perf: split job management
2024-07-04 16:37:39 +08:00
feng626
58b39743e0 Merge pull request #4180 from jumpserver/pr@dev@apple
perf: Apple host detail perm
2024-07-04 16:33:39 +08:00
feng
7651443c25 perf: Apple host detail perm 2024-07-04 16:32:20 +08:00
zhaojisen
8d64e331d1 perf: split job management 2024-07-04 16:04:26 +08:00
252 changed files with 4918 additions and 1869 deletions

View File

@@ -0,0 +1,28 @@
name: LLM Code Review
permissions:
contents: read
pull-requests: write
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
llm-code-review:
runs-on: ubuntu-latest
steps:
- uses: fit2cloud/LLM-CodeReview-Action@main
env:
GITHUB_TOKEN: ${{ secrets.FIT2CLOUDRD_LLM_CODE_REVIEW_TOKEN }}
OPENAI_API_KEY: ${{ secrets.ALIYUN_LLM_API_KEY }}
LANGUAGE: English
OPENAI_API_ENDPOINT: https://dashscope.aliyuncs.com/compatible-mode/v1
MODEL: qwen2-1.5b-instruct
PROMPT: "Please check the following code differences for any irregularities, potential issues, or optimization suggestions, and provide your answers in English."
top_p: 1
temperature: 1
# max_tokens: 10000
MAX_PATCH_LENGTH: 10000
IGNORE_PATTERNS: "/node_modules,*.md,/dist,/.github"
FILE_PATTERNS: "*.java,*.go,*.py,*.vue,*.ts,*.js,*.css,*.scss,*.html"

72
.github/workflows/build-base-image.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
name: Build and Push Base Image
on:
push:
branches:
- 'pr*'
paths:
- 'package.json'
- 'package-lock.json'
- 'yarn.lock'
- 'Dockerfile-base'
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Lock Pull Request
run: |
curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-d '{"state":"pending", "description":"Action running, merge disabled", "context":"Lock PR"}' \
"https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }}"
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract date
id: vars
run: echo "IMAGE_TAG=$(date +'%Y%m%d_%H%M%S')" >> $GITHUB_ENV
- name: Extract repository name
id: repo
run: echo "REPO=$(basename ${{ github.repository }})" >> $GITHUB_ENV
- name: Build and push multi-arch image
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
file: Dockerfile-base
tags: jumpserver/${{ env.REPO }}-base:${{ env.IMAGE_TAG }}
- name: Update Dockerfile
run: |
sed -i 's|-base:.* AS stage-build|-base:${{ env.IMAGE_TAG }} AS stage-build|' Dockerfile
- name: Commit changes
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add Dockerfile
git commit -m "perf: Update Dockerfile with new base image tag"
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Unlock Pull Request
run: |
curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-d '{"state":"success", "description":"Action running, merge disabled", "context":"Lock PR"}' \
"https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }}"

View File

@@ -1,46 +0,0 @@
name: "Run Build Test"
on:
push:
paths:
- 'Dockerfile'
- 'Dockerfile*'
- 'package.json'
- 'yarn.lock'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
component: [lina]
version: [v4]
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Prepare Build
run: |
sed -i 's@registry.npmmirror.com@registry.yarnpkg.com@g' yarn.lock
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Image
uses: docker/build-push-action@v5
with:
context: .
push: true
file: Dockerfile
tags: ghcr.io/jumpserver/${{ matrix.component }}:${{ matrix.version }}
platforms: linux/amd64
build-args: |
VERSION=${{ matrix.version }}
APT_MIRROR=http://deb.debian.org
NPM_REGISTRY=https://registry.yarnpkg.com
outputs: type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@@ -0,0 +1,46 @@
name: "Run Build Test"
on:
push:
paths:
- 'Dockerfile'
- 'Dockerfile*'
- 'package.json'
- 'yarn.lock'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
component: [ lina ]
version: [ v4 ]
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Prepare Build
run: |
sed -i 's@registry.npmmirror.com@registry.yarnpkg.com@g' yarn.lock
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Image
uses: docker/build-push-action@v5
with:
context: .
push: true
file: Dockerfile
tags: ghcr.io/jumpserver/${{ matrix.component }}:${{ matrix.version }}
platforms: linux/amd64
build-args: |
VERSION=${{ matrix.version }}
APT_MIRROR=http://deb.debian.org
NPM_REGISTRY=https://registry.yarnpkg.com
outputs: type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true
cache-from: type=gha
cache-to: type=gha,mode=max

28
.github/workflows/llm-code-review.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: LLM Code Review
permissions:
contents: read
pull-requests: write
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
llm-code-review:
runs-on: ubuntu-latest
steps:
- uses: fit2cloud/LLM-CodeReview-Action@main
env:
GITHUB_TOKEN: ${{ secrets.FIT2CLOUDRD_LLM_CODE_REVIEW_TOKEN }}
OPENAI_API_KEY: ${{ secrets.ALIYUN_LLM_API_KEY }}
LANGUAGE: English
OPENAI_API_ENDPOINT: https://dashscope.aliyuncs.com/compatible-mode/v1
MODEL: qwen2-1.5b-instruct
PROMPT: "Please check the following code differences for any irregularities, potential issues, or optimization suggestions, and provide your answers in English."
top_p: 1
temperature: 1
# max_tokens: 10000
MAX_PATCH_LENGTH: 10000
IGNORE_PATTERNS: "/node_modules,*.md,/dist,/.github"
FILE_PATTERNS: "*.java,*.go,*.py,*.vue,*.ts,*.js,*.css,*.scss,*.html"

View File

@@ -31,7 +31,7 @@ jobs:
tag: ${{ steps.get_version.outputs.TAG }}
- uses: actions/setup-node@v2
with:
node-version: '16.20'
node-version: '20.15'
- name: Install dependencies
run: yarn install
- name: Build web

View File

@@ -1,34 +1,4 @@
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 \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
set -ex \
&& rm -f /etc/apt/apt.conf.d/docker-clean \
&& echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache \
&& sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& echo "no" | dpkg-reconfigure dash
ARG NPM_REGISTRY="https://registry.npmmirror.com"
RUN set -ex \
&& npm config set registry ${NPM_REGISTRY} \
&& yarn config set registry ${NPM_REGISTRY}
WORKDIR /data
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn,sharing=locked \
--mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=yarn.lock,target=yarn.lock \
yarn install
FROM jumpserver/lina-base:20240723_084702 AS stage-build
ARG VERSION
ENV VERSION=$VERSION

22
Dockerfile-base Normal file
View File

@@ -0,0 +1,22 @@
FROM node:20.15-bullseye-slim
ARG DEPENDENCIES=" \
g++ \
make \
python3"
RUN set -ex \
&& rm -f /etc/apt/apt.conf.d/docker-clean \
&& echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& echo "no" | dpkg-reconfigure dash
WORKDIR /data
COPY package.json yarn.lock ./
ARG NPM_MIRROR="https://registry.npmjs.org"
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn,sharing=locked,id=yarn-cache \
sed -i "s|https://registry.npmmirror.com|${NPM_MIRROR}|g" yarn.lock \
&& yarn install

View File

@@ -1,13 +1,13 @@
{
"name": "Lina",
"name": "lina",
"version": "v4.0.0",
"description": "JumpServer Web UI",
"author": "JumpServer Team <support@fit2cloud.com>",
"license": "GPL-3.0-or-later",
"scripts": {
"dev": "vue-cli-service serve",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"dev": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve",
"serve": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve",
"build": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service build",
"build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview",
@@ -30,6 +30,7 @@
"@ztree/ztree_v3": "3.5.44",
"axios": "0.28.0",
"axios-retry": "^3.1.9",
"caniuse-lite": "^1.0.30001642",
"cron-parser": "^4.0.0",
"crypto-js": "^4.1.1",
"css-color-function": "^1.3.3",
@@ -65,7 +66,7 @@
"normalize.css": "7.0.0",
"npm": "^7.8.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"path-to-regexp": "3.3.0",
"v-sanitize": "^0.0.13",
"vue": "2.6.10",
"vue-codemirror": "4.0.6",
@@ -116,7 +117,7 @@
"sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "0.7.2",
"serve-static": "^1.13.2",
"serve-static": "^1.16.0",
"strip-ansi": "^7.1.0",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.2",

View File

@@ -53,7 +53,7 @@ export function createJob(form) {
})
}
export function StopJob(form) {
export function stopJob(form) {
return request({
url: '/api/v1/ops/job-executions/stop/',
method: 'post',
@@ -71,3 +71,10 @@ export function JobUploadFile(form) {
})
}
export function auditUpdateJob(id, form) {
return request({
url: `/api/v1/audits/jobs/${id}/`,
method: 'patch',
data: form
})
}

View File

@@ -15,6 +15,7 @@ export function testEmailSetting(data) {
data: data
})
}
export function importLicense(formData) {
return request({
url: '/api/v1/xpack/license/import',
@@ -25,6 +26,7 @@ export function importLicense(formData) {
data: formData
})
}
export function testLdapSetting(data, refresh = true) {
let url = '/api/v1/settings/ldap/testing/config/'
if (refresh) {
@@ -96,9 +98,17 @@ export function getPublicSettings(isOpen) {
method: 'get'
})
}
export function getLogo() {
return request({
url: '/api/v1/xpack/interface/setting/',
method: 'get'
})
}
export function getPreference() {
return request({
url: '/api/v1/users/preference/?category=luna',
method: 'get'
})
}

View File

@@ -65,10 +65,6 @@ export function logout() {
})
}
export function refreshSessionIdAge() {
return getProfile()
}
export default {
getProfile,
getUserList

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 961 B

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 916 B

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -2,6 +2,7 @@
<Dialog
:close-on-click-modal="false"
:title="$tc('Assets')"
:disabled-status="!isLoaded"
custom-class="asset-select-dialog"
top="2vh"
v-bind="$attrs"
@@ -22,6 +23,8 @@
:url="baseUrl"
class="tree-table"
v-bind="$attrs"
v-on="$listeners"
@loaded="handleTableLoaded"
/>
</Dialog>
</template>
@@ -64,6 +67,7 @@ export default {
data() {
const vm = this
return {
isLoaded: false,
dialogVisible: false,
rowSelected: _.cloneDeep(this.value) || [],
rowsAdd: [],
@@ -124,6 +128,9 @@ export default {
}
},
methods: {
handleTableLoaded() {
this.isLoaded = true
},
handleClose() {
this.$refs.ListPage.$refs.TreeList.componentKey += 1
},

View File

@@ -28,7 +28,6 @@
<script>
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',
@@ -38,6 +37,10 @@ export default {
type: String,
default: '/api/v1/assets/assets/'
},
defaultPageSize: {
type: Number,
default: 10
},
baseNodeUrl: {
type: String,
default: '/api/v1/assets/nodes/'
@@ -71,6 +74,7 @@ export default {
value: iValue,
multiple: true,
clearable: true,
defaultPageSize: this.defaultPageSize,
ajax: {
url: this.baseUrl,
transformOption: (item) => {
@@ -81,7 +85,6 @@ export default {
}
},
methods: {
b,
handleFocus() {
this.$refs.select2.selectRef.blur()
this.dialogVisible = true

View File

@@ -32,6 +32,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/'
@@ -105,9 +109,9 @@ export default {
showAssets: false,
showSearch: false,
customTreeHeaderName: this.$t('TypeTree'),
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)
}
@@ -176,12 +180,12 @@ export default {
setParam('node_id', '')
setParam('asset_id', assetId)
} else if (treeNode.meta.type === 'category') {
url = setUrlParam(url, 'category', treeNode.meta.category)
setParam('category', treeNode.meta.category)
} else if (treeNode.meta.type === 'type') {
setParam('category', treeNode.meta.category)
setParam('type', treeNode.meta._type)
} else if (treeNode.meta.type === 'platform') {
url = setUrlParam(url, 'platform', treeNode.id)
setParam('platform', treeNode.id)
}
setTimeout(() => {
const query = this.setTreeUrlQuery()

View File

@@ -1,42 +1,74 @@
<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 :class="{ 'user-role': isUserRole }" class="chat-item">
<div class="chart-item-container">
<div class="avatar">
<el-avatar
:src="isUserRole ? userUrl : chatUrl"
class="header-avatar"
/>
</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 class="content">
<div class="operational">
<div v-if="!item.message.is_reasoning" class="date">
{{
$moment(item.message.create_time).format("YYYY-MM-DD HH:mm:ss")
}}
</div>
<div v-else class="thinking-time">{{ $i18n.t('DeeplyThoughtAbout') }}</div>
</div>
<div class="action">
<el-tooltip
v-if="isSystemError && isLoading"
:content="$tc('Reconnect')"
:open-delay="500"
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 :class="item.reasoning ? 'reasoning' : 'message'">
<div class="message-content">
<div v-if="!item.reasoning">
<span v-if="isSystemError" class="error">
{{ item.message.content }}
</span>
<span v-else class="chat-text">
<MessageText :message="item.message" />
</span>
</div>
<div v-else class="thinking-wrapper">
<div class="thinking-content">
<!-- eslint-disable-next-line -->
<div class="divider"></div>
<p>
<MessageText :message="item.reasoning" />
</p>
</div>
<div class="thinking-result">
<span v-if="isServerError" class="error">
{{ isServerError }}
</span>
<MessageText :message="item.result" />
</div>
</div>
</div>
<div class="action">
<el-tooltip
v-if="isSystemError && isLoading"
:content="$tc('Reconnect')"
:open-delay="500"
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>
@@ -45,7 +77,7 @@
<script>
import MessageText from './MessageText.vue'
import { mapState } from 'vuex'
import { mapGetters, mapState } from 'vuex'
import { copy } from '@/utils/common'
import { useChat } from '../../useChat.js'
import { reconnect } from '@/utils/socket'
@@ -65,7 +97,6 @@ export default {
},
data() {
return {
chatUrl: require('@/assets/img/chat.png'),
userUrl: '/api/v1/settings/logo/',
dropdownOptions: [
{
@@ -79,11 +110,26 @@ export default {
...mapState({
isLoading: state => state.chat.loading
}),
...mapGetters([
'publicSettings'
]),
isUserRole() {
return this.item.message?.role === 'user'
},
isSystemError() {
return this.item.type === 'error' && this.item.message?.role === 'assistant'
return (
this.item.type === 'error' && this.item?.role === 'assistant'
)
},
isServerError() {
return (this.item.type === 'finish' && this.item.result.content === '')
? this.$i18n.t('ServerBusyRetry')
: ''
},
chatUrl() {
return this.publicSettings.CHAT_AI_TYPE === 'gpt'
? require('@/assets/img/chat.png')
: require('@/assets/img/deepSeek.png')
}
},
methods: {
@@ -94,7 +140,7 @@ export default {
},
handleCommand(value) {
if (value === 'copy') {
copy(this.item.message.content)
copy(this.item.result.content)
}
}
}
@@ -104,101 +150,160 @@ export default {
<style lang="scss" scoped>
.chat-item {
display: flex;
padding: 16px 14px 0;
padding: 0.5rem;
&:last-child {
padding-bottom: 16px;
}
.chart-item-container {
display: flex;
gap: 0.5rem;
.avatar {
width: 22px;
height: 22px;
margin-top: 2px;
.avatar {
width: 24px;
height: 24px;
margin-top: 2px;
.header-avatar {
width: 100%;
height: 100%;
.header-avatar {
width: 100%;
height: 100%;
border-radius: 50%;
&::v-deep img {
background-color: #e5e5e7;
&::v-deep img {
background-color: #fff;
}
}
}
}
.content {
margin-left: 6px;
overflow: hidden;
.operational {
.content {
display: flex;
justify-content: space-between;
flex-direction: column;
// gap: 0.5rem;
overflow: hidden;
.copy {
float: right;
cursor: pointer;
}
}
.operational {
display: flex;
justify-content: space-between;
overflow: hidden;
.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;
.date {
padding-top: 5px;
}
.el-dropdown {
height: 32px;
line-height: 37px;
font-size: 13px;
.thinking-time {
width: 6rem;
display: flex;
justify-content: center;
padding: 5px 10px;
border-radius: 0.5rem;
background-color: #f5f5f5;
}
.el-dropdown-link {
i {
padding: 4px 5px;
font-size: 15px;
color: #8d9091;
.copy {
float: right;
cursor: pointer;
}
}
&:hover {
color: #7b8085
.reasoning {
display: flex;
gap: 0.5rem;
align-items: flex-end;
.message-content .thinking-wrapper {
display: flex;
flex-direction: column;
gap: 0.5rem;
.thinking-content {
position: relative;
color: #8b8b8b;
.divider {
position: absolute;
top: 0;
left: 0;
height: 100%;
border-left: 2px solid #e5e5e5;
}
p {
margin: unset;
padding-left: 0.5rem;
::v-deep p {
color: #8b8b8b;
}
}
}
}
}
.error {
color: red;
.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;
&:last-child {
padding-bottom: 16px;
}
.content {
margin-right: 10px;
&.user-role {
flex-direction: row-reverse;
.operational {
.chart-item-container {
flex-direction: row-reverse;
}
.message {
flex-direction: row-reverse;
.content {
margin-right: 10px;
.message-content {
background-color: var(--menu-hover);
border-radius: 12px 2px 12px 12px;
.operational {
flex-direction: row-reverse;
}
.message {
flex-direction: row-reverse;
.message-content {
background-color: var(--menu-hover);
border-radius: 12px 2px 12px 12px;
}
}
}
}

View File

@@ -123,7 +123,17 @@ export default {
setLoading(true)
removeLoadingMessageInChat()
this.conversationId = data.id
updateChaMessageContentById(data.message.id, data)
const newFragment = {
message: { id: data.message.id, is_reasoning: data.message.is_reasoning },
reasoning: { content: data.message.is_reasoning ? data.message.content : '' },
result: { content: data.message.is_reasoning ? '' : data.message.content },
role: data.message.role,
type: data.message.type,
create_time: data.message.create_time
}
updateChaMessageContentById(data.message.id, newFragment)
}
if (data.message?.type === 'finish') {
setLoading(false)

View File

@@ -1,5 +1,6 @@
<template>
<AssetTreeTable
ref="AssetTreeTable"
:header-actions="headerActions"
:table-config="tableConfig"
:tree-setting="treeSetting"
@@ -47,9 +48,19 @@ export default {
return this.tableUrl.replace('/assets/', `/assets/${row.id}/accounts/`)
}
},
nameDisabled: {
type: Boolean,
default: true
name: {
type: Object,
default: () => ({
formatter: DetailFormatter,
formatterArgs: {
route: 'AssetDetail',
can: true
}
})
},
comment: {
type: Object,
default: () => ({})
}
},
data() {
@@ -80,11 +91,7 @@ export default {
},
columnsMeta: {
name: {
formatter: DetailFormatter,
formatterArgs: {
route: 'AssetDetail',
can: !this.nameDisabled
}
...this.name
},
labels: {
formatterArgs: {
@@ -99,7 +106,8 @@ export default {
formatter: AccountInfoFormatter,
width: '100px'
},
connectivity: connectivityMeta
connectivity: connectivityMeta,
comment: { ...this.comment }
},
tableAttrs: {
rowClassName({ row }) {

View File

@@ -8,7 +8,7 @@
:visible.sync="visible"
class="dialog-content"
v-bind="$attrs"
width="600px"
width="740px"
@confirm="visible = false"
v-on="$listeners"
>
@@ -52,11 +52,21 @@
<el-row :gutter="24" style="margin: 0 auto;">
<el-col :md="24" :sm="24" style="display: flex; align-items: center; margin-bottom: 20px;">
<el-input
v-if="subTypeSelected !== 'face'"
v-model="secretValue"
:placeholder="inputPlaceholder"
:show-password="showPassword"
@keyup.enter.native="handleConfirm"
/>
<iframe
v-if="isFaceCaptureVisible && subTypeSelected ==='face' && faceCaptureUrl"
:src="faceCaptureUrl"
allow="camera"
sandbox="allow-scripts allow-same-origin"
style="width: 100%; height: 800px;border: none;"
/>
<span v-if="subTypeSelected === 'sms'" style="margin: -1px 0 0 20px;">
<el-button
:disabled="smsBtnDisabled"
@@ -72,9 +82,24 @@
</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">
<el-button
v-if="subTypeSelected!=='face'"
class="confirm-btn"
size="mini"
type="primary"
@click="handleConfirm"
>
{{ this.$t('Confirm') }}
</el-button>
<el-button
v-if="subTypeSelected==='face'&&!isFaceCaptureVisible"
class="confirm-btn"
size="mini"
type="primary"
@click="handleFaceCapture"
>
{{ this.$tc('VerifyFace') }}
</el-button>
</el-col>
</el-row>
</div>
@@ -113,7 +138,10 @@ export default {
visible: false,
callback: null,
cancel: null,
processing: false
processing: false,
isFaceCaptureVisible: false,
faceToken: null,
faceCaptureUrl: null
}
},
computed: {
@@ -129,6 +157,10 @@ export default {
},
methods: {
handleSubTypeChange(val) {
if (val !== 'face') {
this.isFaceCaptureVisible = false
}
this.inputPlaceholder = this.subTypeChoices.filter(item => item.name === val)[0]?.placeholder
this.smsWidth = val === 'sms' ? 6 : 0
},
@@ -192,6 +224,29 @@ export default {
this.$message.error(this.$tc('FailedToSendVerificationCode'))
})
},
startFaceCapture() {
const url = '/api/v1/authentication/face/context/'
this.$axios.post(url).then(data => {
const token = data['token']
this.faceCaptureUrl = '/facelive/capture?token=' + token
this.isFaceCaptureVisible = true
const timer = setInterval(() => {
this.$axios.get(url + `?token=${token}`).then(data => {
if (data['is_finished']) {
clearInterval(timer)
this.isFaceCaptureVisible = false
this.handleConfirm()
}
})
}, 1000)
}).catch(() => {
this.$message.error(this.$tc('FailedToStartFaceCapture'))
})
},
handleFaceCapture() {
this.startFaceCapture()
},
handleConfirm() {
if (this.confirmTypeRequired === 'relogin') {
return this.logout()
@@ -214,6 +269,8 @@ export default {
})
}).catch((err) => {
this.$message.error(err.message || this.$tc('ConfirmFailed'))
this.faceCaptureUrl = null
this.isFaceCaptureVisible = false
})
}
}
@@ -221,21 +278,21 @@ export default {
</script>
<style lang="scss" scoped>
.dialog-content ::v-deep .el-dialog__footer {
padding: 0;
}
.dialog-content ::v-deep .el-dialog__footer {
padding: 0;
}
.dialog-content ::v-deep .el-dialog {
padding: 8px;
.dialog-content ::v-deep .el-dialog {
padding: 8px;
.el-dialog__body {
padding-top: 30px;
padding-bottom: 30px;
}
.el-dialog__body {
padding-top: 30px;
padding-bottom: 30px;
}
}
.confirm-btn {
width: 100%;
line-height: 20px;
}
.confirm-btn {
width: 100%;
line-height: 20px;
}
</style>

View File

@@ -0,0 +1,133 @@
<template>
<AutoDataForm
ref="AutoDataForm"
class="variable-add"
:submit-btn-text="submitBtnText"
v-bind="$data"
@submit="confirm"
/>
</template>
<script>
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
export default {
name: 'VariableCreateForm',
components: {
AutoDataForm
},
props: {
variable: {
type: Object,
default: () => ({})
}
},
data() {
const defaultValidator = (rule, value, callback) => {
if (this.defaultValueRequired && !value) {
callback(new Error(this.$t('FieldRequiredError')))
} else {
callback()
}
}
return {
defaultValueRequired: false,
submitBtnText: this.$t('Confirm'),
url: '/api/v1/ops/variables/',
form: Object.assign({ 'on_invalid': 'error' }, this.variable || {}),
fields: [
['', ['name', 'var_name', 'type', 'text_default_value', 'select_default_value', 'extra_args', 'tips', 'required']]
],
fieldsMeta: {
text_default_value: {
label: this.$t('DefaultValue'),
hidden: (formValue) => {
return formValue.type !== 'text'
},
rules: [{ validator: defaultValidator }],
helpTip: this.$t('DefaultValueTip'),
el: {
type: 'input'
}
},
select_default_value: {
label: this.$t('DefaultValue'),
helpTip: this.$t('DefaultValueTip'),
hidden: (formValue) => {
return formValue.type !== 'select'
},
rules: [{ validator: defaultValidator }],
el: { type: 'input' }
},
extra_args: {
hidden: (formValue) => {
return formValue.type !== 'select'
},
el: { type: 'textarea', rows: 4, placeholder: this.$t('ExtraArgsPlaceholder') },
rules: [
{
validator: (rule, value, callback) => {
try {
const lines = value.split('\n')
const regex = /^[^:]+:[^:]+$/
for (const line of lines) {
if (!regex.test(line.trim())) {
callback(new Error(this.$t('ExtraArgsFormatError')))
}
}
} catch (e) {
callback(new Error(this.$t('ExtraArgsFormatError')))
}
callback()
}
}
]
},
required: {
on: {
change: ([event], updateForm) => {
this.defaultValueRequired = event
}
}
}
},
hasSaveContinue: false,
method: 'get'
}
},
mounted() {
this.defaultValueRequired = this.variable?.required || false
},
methods: {
confirm(form) {
if (this.variable?.name) {
this.$emit('edit', form)
} else {
this.$emit('add', form)
}
}
}
}
</script>
<style lang='scss' scoped>
.variable-add {
::v-deep .el-form-item {
margin-bottom: 5px;
.help-block {
margin-bottom: 5px;
}
}
::v-deep .form-group-header {
.hr-line-dashed {
margin: 5px 0;
}
h3 {
margin-bottom: 10px;
}
}
}
</style>

View File

@@ -0,0 +1,74 @@
<template>
<AutoDataForm
ref="AutoDataForm"
class="variable-set"
:submit-btn-text="submitBtnText"
v-bind="$data"
:fields="fields"
@submit="confirm"
/>
</template>
<script>
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
export default {
name: 'VariableSetForm',
components: {
AutoDataForm
},
props: {
formData: {
type: Array,
default: () => ([])
},
queryParam: {
type: String,
default: ''
}
},
data() {
return {
submitBtnText: this.$t('Confirm'),
// 防止缓存form remoteMeta
url: `/api/v1/ops/variables/form-data/?t=${new Date().getTime()}&` + this.queryParam,
form: {},
hasSaveContinue: false,
method: 'get',
hasReset: false
}
},
computed: {
fields() {
return [['', this.formData.map(item => item.var_name)]]
}
},
methods: {
confirm(form) {
this.$emit('confirm', form)
}
}
}
</script>
<style lang='scss' scoped>
.variable-set {
::v-deep .el-form-item {
margin-bottom: 5px;
.help-block {
margin-bottom: 5px;
}
}
::v-deep .form-group-header {
.hr-line-dashed {
margin: 5px 0;
}
h3 {
margin-bottom: 10px;
}
}
}
</style>

View File

@@ -78,7 +78,7 @@ export default {
formatterData = data
}
return (
<span>{formatterData}</span>
<span style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', lineHeight: '1.2' }}>{formatterData}</span>
)
}
if (this.value instanceof Array) {
@@ -92,7 +92,7 @@ export default {
)
}
return (
<span title={this.displayValue}>{this.displayValue}</span>
<span style='white-space: pre-wrap;' title={this.displayValue}>{this.displayValue}</span>
)
}
}

View File

@@ -57,7 +57,7 @@
@click="handleClick(action)"
>
<el-tooltip :content="action.tip" :disabled="!action.tip" :open-delay="500" placement="top">
<span>
<span :title="action.tip">
<span v-if="action.icon && !action.icon.startsWith('el-')" style="vertical-align: initial">
<i v-if="action.icon.startsWith('fa')" :class="'fa ' + action.icon" />
<svg-icon v-else :icon-class="action.icon" />
@@ -249,10 +249,11 @@ $color-drop-menu-border: #e4e7ed;
align-items: flex-end;
.el-button {
display: flex;
align-items: center;
padding: 2px 6px;
padding: 2px 5px;
color: $btn-text-color;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
* {
vertical-align: baseline !important;

View File

@@ -10,13 +10,16 @@
v-bind="$attrs"
v-on="$listeners"
>
<slot />
<div v-loading="disabledStatus">
<slot />
</div>
<div v-if="showButtons" slot="footer" class="dialog-footer">
<slot name="footer">
<el-button v-if="showCancel && showButtons" size="small" @click="onCancel">{{ cancelTitle }}</el-button>
<el-button
v-if="showConfirm && showButtons"
:loading="loadingStatus"
:disabled="disabledStatus"
size="small"
type="primary"
@click="onConfirm"
@@ -69,7 +72,7 @@ export default {
type: Boolean,
default: true
},
loadingStatus: {
disabledStatus: {
type: Boolean,
default: false
},
@@ -79,7 +82,8 @@ export default {
}
},
data() {
return {}
return {
}
},
computed: {
iWidth() {
@@ -119,6 +123,7 @@ export default {
&__body {
padding: 20px 30px;
font-size: 13px;
&:has(.el-table) {
background: #f3f3f4;

View File

@@ -85,6 +85,7 @@ export default {
// 如果不想等,证明是 value 自己变化导致的, 需要重新渲染
if (valJson !== this.formJson) {
this.iValue = val
this.$log.debug('Sub form value changed, rerender form: ', this.formJson, valJson)
this.loading = true
setTimeout(() => {
this.loading = false
@@ -95,11 +96,12 @@ export default {
}
},
methods: {
outputValue: _.debounce(function(val) {
this.$emit('input', val)
}),
updateValue(val) {
this.iValue = val
setTimeout(() => {
this.$emit('input', val)
}, 100)
this.outputValue(val)
},
objectToString(obj) {
let data = ''

View File

@@ -7,7 +7,7 @@
v-bind="$attrs"
v-on="$listeners"
>
<span
<div
v-for="(group, i) in groups"
:key="'group-'+group.name"
:slot="'id:'+group.name"
@@ -18,7 +18,7 @@
:index="i"
:line="i !== 0 && !groupHidden(groups[i - 1], i - 1)"
/>
</span>
</div>
</DataForm>
</template>

View File

@@ -7,7 +7,6 @@ import BasicTree from '@/components/Form/FormFields/BasicTree.vue'
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
import { assignIfNot, toSentenceCase } from '@/utils/common'
import TagInput from '@/components/Form/FormFields/TagInput.vue'
import TransferSelect from '@/components/Form/FormFields/TransferSelect.vue'
import i18n from '@/i18n/i18n'
export class FormFieldGenerator {
@@ -135,9 +134,6 @@ export class FormFieldGenerator {
case 'comment':
field.el.type = 'textarea'
break
case 'users':
field.component = TransferSelect
field.el.label = field.label
}
return field
}
@@ -197,6 +193,24 @@ export class FormFieldGenerator {
return field
}
setChoicesTips(field, fieldMeta, fieldRemoteMeta) {
// 设置 checkbox 的 tips
if (['checkbox-group', 'radio-group'].indexOf(field.type) !== -1) {
field.options.map(option => {
if (!option.tip && field.tips) {
option.tip = field.tips[option.value]
}
if (!option.tip) {
const match = option.label.match(/^(.+?)\s*\((.*?)\)$/)
if (match) {
option.label = match[1]
option.tip = match[2]
}
}
})
}
}
afterGenerateField(field) {
field.label = toSentenceCase(field.label)
@@ -204,15 +218,7 @@ export class FormFieldGenerator {
field.el.placeholder = field.placeholder
}
// 设置 checkbox 的 tips
if (field.tips && ['checkbox-group', 'radio-group'].indexOf(field.type) !== -1) {
field.options.map(option => {
if (!option.tip && field.tips[option.value]) {
option.tip = field.tips[option.value]
}
})
}
this.setChoicesTips(field)
return field
}

View File

@@ -17,7 +17,7 @@
placement="right"
popper-class="help-tips"
>
<div slot="content" v-sanitize="data.helpTip" /> <!-- Noncompliant -->
<div slot="content" v-sanitize="data.helpTip" class="help-tip-content" /> <!-- Noncompliant -->
<i class="fa fa-question-circle-o help-tip-icon" />
</el-tooltip>
</template>
@@ -322,4 +322,9 @@ export default {
cursor: pointer;
}
}
.help-tip-content {
white-space: pre-wrap;
word-wrap: break-word;
}
</style>

View File

@@ -33,6 +33,7 @@
>
{{ iSubmitBtnText }}
</el-button>
<el-button
v-if="defaultButton && hasSaveContinue"
size="small"
@@ -40,6 +41,7 @@
>
{{ $t("SaveAndAddAnother") }}
</el-button>
<el-button
v-if="defaultButton && hasReset"
size="small"
@@ -47,6 +49,7 @@
>
{{ $t("Reset") }}
</el-button>
<el-button
v-for="button in moreButtons"
v-show="!button.hidden"
@@ -217,9 +220,13 @@ export default {
.el-form-item__label {
padding: 0 30px 0 0;
line-height: 32px;
line-height: 30px;
color: var(--color-text-primary);
span {
display: unset;
}
i {
color: var(--color-icon-primary);
}

View File

@@ -8,99 +8,118 @@
:label="item.name"
:prop="item.name"
>
<template v-if="item.type === 'button' && !item.isVisible">
<el-button
:type="item.el && item.el.type"
class="start-stop-btn"
size="mini"
@click="item.callback()"
>
<i :class="item.icon" />{{ item.name }}
</el-button>
<el-tooltip :disabled="!item.tip" :content="item.tip">
<el-button
:type="item.el && item.el.type"
class="start-stop-btn"
size="mini"
@click="item.callback()"
>
<i :class="item.icon" />
{{ item.name }}
</el-button>
</el-tooltip>
</template>
<template v-if="item.type === 'input' && item.el && item.el.autoComplete">
<el-autocomplete
v-model="formModel[item.name]"
:fetch-suggestions="item.el.query"
:placeholder="item.placeholder"
class="inline-input"
size="mini"
@change="handleInputChange(item)"
@select="handleInputChange(item)"
/>
<el-tooltip :disabled="!item.tip" :content="item.tip">
<el-autocomplete
v-model="formModel[item.name]"
:fetch-suggestions="item.el.query"
:placeholder="item.placeholder"
class="inline-input"
size="mini"
clearable
@change="handleInputChange(item)"
@select="handleInputChange(item)"
/>
</el-tooltip>
</template>
<template v-else-if="item.type === 'input'">
<el-input
v-model="formModel[item.name]"
:class="!isFold ? 'special-style' : ''"
:placeholder="item.placeholder"
class="inline-input"
size="mini"
@change="item.callback(formModel[item.name])"
/>
<el-tooltip :disabled="!item.tip" :content="item.tip">
<el-input
v-model="formModel[item.name]"
:class="!isFold ? 'special-style' : ''"
:placeholder="item.placeholder"
class="inline-input"
size="mini"
@change="item.callback(formModel[item.name])"
/>
</el-tooltip>
</template>
<template v-if="item.type === 'select' && item.el && item.el.create">
<span class="filter-label">{{ item.name }}:</span>
<el-select
v-if="item.type === 'select' && item.el && item.el.create"
:key="index"
v-model="formModel[item.name]"
:allow-create="item.el.create || false"
:filterable="item.el.create || false"
:multiple="item.el.multiple"
:placeholder="item.name"
class="autoWidth-select"
default-first-option
size="mini"
@change="item.callback(item.value)"
>
<template slot="prefix">{{ item.label + ':' + item.value }}</template>
<el-option
v-for="(option, id) in item.options"
:key="id"
:label="option.label"
:title="option.value"
:value="option.value"
/>
</el-select>
<el-tooltip :disabled="!item.tip" :content="item.tip">
<span class="filter-label">{{ item.name }}:</span>
<el-select
v-if="item.type === 'select' && item.el && item.el.create"
:key="index"
v-model="formModel[item.name]"
:allow-create="item.el.create || false"
:filterable="item.el.create || false"
:multiple="item.el.multiple"
:placeholder="item.name"
class="autoWidth-select"
default-first-option
size="mini"
@change="item.callback(item.value)"
>
<template slot="prefix">{{ item.label + ':' + item.value }}</template>
<el-option
v-for="(option, id) in item.options"
:key="id"
:label="option.label"
:title="option.value"
:value="option.value"
/>
</el-select>
</el-tooltip>
</template>
<template v-if="item.type === 'select' && (!item.el || !item.el.create)">
<el-dropdown
class="select-dropdown"
trigger="click"
@command="(command) => {
item.value = command
item.callback(command)
}"
>
<el-button size="mini" type="primary">
<div class="text-content">
<span class="content">
{{ getLabel(item.value, item.options) }}
<i class="el-icon-arrow-down el-icon--right" />
</span>
</div>
</el-button>
<el-dropdown-menu v-slot="dropdown">
<el-dropdown-item
v-for="(option, i) in item.options"
:key="i"
:command="option.value"
>
{{ option.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-tooltip :disabled="!item.tip" :content="item.tip">
<el-dropdown
class="select-dropdown"
trigger="click"
@command="(command) => {
item.value = command
item.callback(command)
}"
>
<el-button size="mini" type="primary">
<div class="text-content">
<span class="content">
{{ getLabel(item.value, item.options) }}
<i class="el-icon-arrow-down el-icon--right" />
</span>
</div>
</el-button>
<el-dropdown-menu v-slot="dropdown">
<el-dropdown-item
v-for="(option, i) in item.options"
:key="i"
:command="option.value"
>
{{ option.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-tooltip>
</template>
<template v-if="item.type === 'switch'">
<el-switch
v-model="formModel[item.name]"
:active-text="item.name"
:disabled="item.disabled"
@change="item.callback(formModel[item.name])"
/>
<el-tooltip :disabled="!item.tip" :content="item.tip">
<el-switch
v-model="formModel[item.name]"
:active-text="item.name"
:disabled="item.disabled"
@change="item.callback(formModel[item.name])"
/>
</el-tooltip>
</template>
</el-form-item>
<div
@@ -226,7 +245,7 @@ export default {
},
set(val) {
this.$emit('update:value', val)
this.$emit('change', val)
this.$emit('input', val)
}
},
iOptions() {
@@ -379,7 +398,6 @@ $input-border-color: #C0C4CC;
}
.editor {
//margin-left: 30px;
border: 1px solid var(--color-border);
overflow: hidden;
}

View File

@@ -84,6 +84,12 @@ export default {
</script>
<style lang='scss' scoped>
html:lang(pt-br) {
.datepicker ::v-deep .el-range-separator {
padding: 0 10px;
}
}
.datepicker {
margin-left: 10px;
width: 233px;

View File

@@ -15,6 +15,7 @@ export default {
},
data() {
return {
defaultValue: 24,
displayMapper: {
'second': this.$t('Second'), // 'sec' is the default value of 'unit
'min': this.$t('Minute'), // 'min' is the default value of 'unit

View File

@@ -48,7 +48,8 @@ export default {
type: 'input-number',
el: {
min: 8,
max: 30
max: 36,
size: 'mini'
}
},
{

View File

@@ -1,21 +1,20 @@
<template>
<div>
<el-input v-model="rawValue.phone" required :placeholder="$tc('InputPhone')" @input="OnInputChange">
<el-input v-model="rawValue.phone" :placeholder="$tc('InputPhone')" required @input="onInputChange">
<el-select
slot="prepend"
:placeholder="$tc('Select')"
:value="rawValue.code"
style="width: 75px;"
@change="OnChange"
style="width: 105px;"
@change="onChange"
>
<el-option
v-for="country in countries"
:key="country.value"
:key="country.name"
:label="country.value"
:value="country.value"
style="width: 200px;"
>
<span style="float: left">{{ country.name }}</span>
<span class="country-name">{{ country.name }}</span>
<span style="float: right; font-size: 13px">{{ country.value }}</span>
</el-option>
</el-select>
@@ -24,19 +23,19 @@
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'PhoneInput',
props: {
value: {
type: [Object, String],
default: () => ({ 'code': '+86', 'phone': '' })
default: null
}
},
data() {
return {
rawValue: {}
rawValue: {},
countries: [{ 'name': 'China', 'value': '+86' }]
}
},
computed: {
@@ -45,26 +44,38 @@ export default {
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: '' }
const defaults = { code: localStorage.getItem('prePhoneCode') || '+86', phone: '' }
this.rawValue = this.value || defaults
this.$axios.get('/api/v1/common/countries/').then(res => {
this.countries = res.map(item => {
return { name: `${item.flag} ${item.name}`, value: item.phone_code }
})
})
this.$emit('input', this.fullPhone)
},
methods: {
OnChange(countryCode) {
onChange(countryCode) {
this.rawValue.code = countryCode
this.OnInputChange()
this.onInputChange()
localStorage.setItem('prePhoneCode', countryCode)
},
OnInputChange() {
onInputChange() {
this.$emit('input', this.fullPhone)
}
}
}
</script>
<style scoped>
.country-name {
display: inline-block;
width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-right: 5px;
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div v-if="!loading" :class="showSetting ? 'show-setting' : 'hide-setting'">
<div :class="showSetting ? 'show-setting' : 'hide-setting'">
<div v-for="(item, index) in items" :key="item.name" class="protocol-item">
<el-input
v-model="item.port"
@@ -114,8 +114,7 @@ export default {
name: '',
items: [],
currentProtocol: {},
showDialog: false,
loading: false
showDialog: false
}
},
computed: {
@@ -144,13 +143,8 @@ export default {
watch: {
choices: {
handler(value, oldValue) {
if (value?.length === oldValue?.length) {
return
}
this.loading = true
setTimeout(() => {
this.setDefaultItems(value)
this.loading = false
},)
},
deep: true,
@@ -345,6 +339,10 @@ export default {
.protocol-item {
display: flex;
margin: 5px 0;
&:first-of-type {
margin-top: 0;
}
}
.input-button {

View File

@@ -125,16 +125,19 @@ export default {
allowCreate: {
type: Boolean,
default: false
},
defaultPageSize: {
type: Number,
default: 10
}
},
data() {
const vm = this
const defaultPageSize = 10
const defaultParams = {
search: '',
page: 1,
hasMore: true,
pageSize: defaultPageSize
pageSize: vm.defaultPageSize
}
// 设置axios全局报错提示不显示
const validateStatus = (status) => {
@@ -194,7 +197,6 @@ export default {
}
},
iAjax() {
const defaultPageSize = 10
const defaultMakeParams = (params) => {
const page = params.page || 1
const offset = (page - 1) * params.pageSize
@@ -237,7 +239,7 @@ export default {
}
const defaultAjax = {
url: '',
pageSize: defaultPageSize,
pageSize: this.defaultPageSize,
makeParams: defaultMakeParams,
transformOption: defaultTransformOption,
processResults: defaultProcessResults,

View File

@@ -14,10 +14,12 @@
:close-on-click-modal="false"
:title="label"
:visible.sync="showTransfer"
:disabled-status="!isLoaded"
class="the-dialog"
width="730px"
@cancel="handleTransCancel"
@confirm="handleTransConfirm"
v-on="$listeners"
>
<krryPaging v-if="selectInitialized" ref="pageTransfer" class="transfer" v-bind="pagingTransfer" />
</Dialog>
@@ -77,13 +79,16 @@ export default {
if (keyword) {
params['search'] = keyword
}
this.isLoaded = false
const data = await this.$axios.get(url, { params })
this.isLoaded = true
return data['results'].map(item => {
const n = transformOption(item)
return { id: n.value, label: n.label }
})
}
return {
isLoaded: false,
showTransfer: false,
selectInitialized: false,
select2: {
@@ -167,7 +172,3 @@ export default {
}
}
</script>
<style scoped>
</style>

View File

@@ -42,7 +42,7 @@ export default {
patterns.push([/\d/, i18n.t('NUMBER_REQUIRED')])
}
if (passwordRule['SECURITY_PASSWORD_SPECIAL_CHAR']) {
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~@#¥……&*()——|{}【】‘;:”“'。,、?]")
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~@#¥……&*()——|{}【】‘;:”“'。,、?_+-]")
patterns.push([pattern, i18n.t('SPECIAL_CHAR_REQUIRED')])
}
for (const [pattern, msg] of patterns) {

View File

@@ -39,6 +39,22 @@ export default {
</script>
<style scoped>
html:lang(en) .quick-actions ::v-deep button {
width: 80px;
}
html:lang(ja) .quick-actions ::v-deep button {
width: 100px;
}
html:lang(zh-tw) .quick-actions ::v-deep button {
width: 65px;
}
html:lang(zh-cn) .quick-actions ::v-deep button {
width: 65px;
}
.quick-actions ::v-deep table {
width: 100%;
}
@@ -58,11 +74,9 @@ export default {
.quick-actions ::v-deep button {
padding: 4px 5px;
font-size: 13px;
width: 65px;
span {
overflow: hidden;
white-space: nowrap; /* 控制文本不换行 */
white-space: nowrap;
text-overflow: ellipsis;
display: block;
}

View File

@@ -3,7 +3,7 @@
v-if="showColumnSettingPopover"
:cancel-title="$tc('RestoreDefault')"
:destroy-on-close="true"
:title="$tc('TableSetting')"
:title="$tc('ListPreference')"
:visible.sync="showColumnSettingPopover"
top="10%"
width="50%"
@@ -36,6 +36,7 @@
<el-checkbox
:disabled="item.prop==='actions' || minColumns.indexOf(item.prop)!==-1"
:label="item.prop"
:title="item.label"
>
{{ item.label }}
</el-checkbox>
@@ -90,8 +91,14 @@ export default {
}
}
},
beforeDestroy() {
this.$eventBus.$off('showColumnSettingPopover', this.showColumnSettingPopoverHandler)
},
mounted() {
this.$eventBus.$on('showColumnSettingPopover', ({ url }) => {
this.$eventBus.$on('showColumnSettingPopover', this.showColumnSettingPopoverHandler)
},
methods: {
showColumnSettingPopoverHandler({ url }) {
if (url === this.url) {
this.checkAll = false
this.showColumnSettingPopover = true
@@ -105,9 +112,7 @@ export default {
this.checkAll = false
this.isIndeterminate = true
}
})
},
methods: {
},
handleColumnConfirm() {
this.showColumnSettingPopover = false
this.$emit('columnsUpdate', { columns: this.iCurrentColumns, url: this.url })

View File

@@ -65,7 +65,17 @@ export default {
isDeactivated: false
}
},
computed: {},
computed: {
dynamicActionWidth() {
if (this.$i18n.locale === 'en') {
return '120px'
}
if (this.$i18n.locale === 'pt-br') {
return '160px'
}
return '100px'
}
},
watch: {
config: {
handler: _.debounce(function(iNew, iOld) {
@@ -131,7 +141,7 @@ export default {
prop: 'actions',
label: i18n.t('Actions'),
align: 'center',
width: '100px',
width: this.dynamicActionWidth,
formatter: ActionsFormatter,
fixed: 'right',
formatterArgs: {}

View File

@@ -7,14 +7,14 @@
<template v-else>
<el-table
ref="table"
v-loading="loading"
v-loading="tableLoading"
:data="data"
:row-class-name="rowClassName"
v-bind="tableAttrs"
@select="selectStrategy.onSelect"
v-on="$listeners"
@selection-change="selectStrategy.onSelectionChange"
@select-all="selectStrategy.onSelectAll($event, canSelect)"
@select-all="handleSelectAll($event, canSelect)"
@sort-change="onSortChange"
>
<!--TODO 不用jsx写, 感觉template逻辑有点不清晰了-->
@@ -749,7 +749,7 @@ export default {
page: defaultFirstPage,
// https://github.com/ElemeFE/element/issues/1153
total: null,
loading: false,
tableLoading: false,
// 多选项的数组
selected: [],
@@ -940,11 +940,11 @@ export default {
},
getListFromStaticData({ loading = true } = {}) {
if (loading) {
this.loading = true
this.tableLoading = true
}
if (!this.hasPagination) {
this.data = this.totalData
this.loading = false
this.tableLoading = false
if (this.isTree) {
this.data = this.tree2Array(this.data, this.expandAll)
}
@@ -957,7 +957,7 @@ export default {
const end = (page + pageOffset) * this.size
this.$log.debug(`page: ${page}, size: ${this.size}, start: ${start}, end: ${end}`)
this.data = this.totalData.slice(start, end)
this.loading = false
this.tableLoading = false
this.data = this.tree2Array(this.data, this.expandAll)
return this.data
},
@@ -983,7 +983,7 @@ export default {
queryUtil.stringify(query, '=', '&')
// 请求开始
this.loading = loading
this.tableLoading = loading
// 存储query记录, 便于后面恢复
if (this.saveQuery) {
@@ -1023,7 +1023,7 @@ export default {
this.total === 0 &&
(_isEmpty(formValue) || _values(formValue).every(isFalsey))
this.loading = false
this.tableLoading = false
/**
* 请求返回, 数据更新后触发
* @property {object} data - table的数据
@@ -1043,7 +1043,7 @@ export default {
*/
this.$emit('error', err)
this.total = 0
this.loading = false
this.tableLoading = false
})
},
search(attrs, reset) {
@@ -1106,6 +1106,14 @@ export default {
this.page = val
this.getList()
},
handleSelectAll(selection, selectable = () => true) {
this.tableLoading = true
try {
this.selectStrategy.onSelectAll(selection, selectable)
} finally {
this.tableLoading = false
}
},
/**
* 切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否
*

View File

@@ -69,12 +69,48 @@ class StrategyPersistSelection extends StrategyAbstract {
* 用户切换当前页的多选
*/
onSelectAll(selection, selectable = () => true) {
const isSelected = !!selection.length
this.elDataTable.data.forEach(r => {
if (selectable(r)) {
this.toggleRowSelection(r, isSelected)
}
})
const { id, selected, data } = this.elDataTable
const selectableRows = data.filter(selectable)
// const isSelected = !!selection.length
// 创建已选择项的 id 集合,用于快速查找
const selectedIds = new Set(selected.map(r => r[id]))
const currentPageIds = new Set(selectableRows.map(row => row[id]))
// 前页面的选中状态
const currentPageSelectedCount = selectableRows.filter(row =>
selectedIds.has(row[id])
).length
// 判断是全选还是取消全选
const shouldSelectAll = currentPageSelectedCount < selectableRows.length
this.elTable.clearSelection()
if (shouldSelectAll) {
selectableRows.forEach(row => {
if (!selectedIds.has(row[id])) selected.push(row)
this.elTable.toggleRowSelection(row, true)
// ! 这里需要触发事件,否则在 el-table 中无法触发 selection-change 事件
this.elDataTable.$emit('toggle-row-selection', true, row)
})
} else {
const newSelected = []
selected.forEach(row => {
if (!currentPageIds.has(row[id])) {
newSelected.push(row)
} else {
this.elDataTable.$emit('toggle-row-selection', false, row)
}
})
this.elDataTable.selected = newSelected
}
this.elDataTable.$emit('selection-change', this.elDataTable.selected)
}
/**
* toggleRowSelection和clearSelection管理elDataTable的selected数组
@@ -83,14 +119,17 @@ class StrategyPersistSelection extends StrategyAbstract {
toggleRowSelection(row, isSelected) {
const { id, selected } = this.elDataTable
const foundIndex = selected.findIndex(r => r[id] === row[id])
if (typeof isSelected === 'undefined') {
isSelected = foundIndex <= -1
}
if (isSelected && foundIndex === -1) {
selected.push(row)
} else if (!isSelected && foundIndex > -1) {
selected.splice(foundIndex, 1)
}
this.elDataTable.$emit('toggle-row-selection', isSelected, row)
this.updateElTableSelection()
}
@@ -103,14 +142,17 @@ class StrategyPersistSelection extends StrategyAbstract {
*/
updateElTableSelection() {
const { data, id, selected } = this.elDataTable
// 历史勾选的行已经不在当前页了所以要将当前页的行数据和selected合并
const mergeData = _.uniqWith([...data, ...selected], _.isEqual)
mergeData.forEach(r => {
const isSelected = !!selected.find(r2 => r[id] === r2[id])
if (!this.elTable) {
return
const selectedIds = new Set(selected.map(r => r[id]))
this.elTable.clearSelection()
data.forEach(row => {
const shouldBeSelected = selectedIds.has(row[id])
if (!this.elTable) return
if (shouldBeSelected) {
this.elTable.toggleRowSelection(row, true)
}
this.elTable.toggleRowSelection(r, isSelected)
})
}
}

View File

@@ -153,6 +153,8 @@ export default {
this.toggleRowSelection(row, true)
}
}
this.$emit('loaded')
},
handleSizeChange(val) {
localStorage.setItem('paginationSize', val)

View File

@@ -171,14 +171,18 @@ export default {
]
}
},
beforeDestroy() {
this.$eventBus.$off('showExportDialog', this.showExportDialogHandler)
},
mounted() {
this.$eventBus.$on('showExportDialog', ({ selectedRows, url, name }) => {
this.$eventBus.$on('showExportDialog', this.showExportDialogHandler)
},
methods: {
showExportDialogHandler({ selectedRows, url, name }) {
if (url === this.url || url.indexOf(this.url) > -1) {
this.showExportDialog()
}
})
},
methods: {
},
showExportDialog() {
if (!this.mfaVerifyRequired) {
this.exportDialogShow = true

View File

@@ -142,14 +142,18 @@ export default {
this.showTable = false
}
},
beforeDestroy() {
this.$eventBus.$off('showImportDialog', this.showImportEventHandler)
},
mounted() {
this.$eventBus.$on('showImportDialog', ({ url }) => {
this.$eventBus.$on('showImportDialog', this.showImportEventHandler)
},
methods: {
showImportEventHandler({ url }) {
if (url === this.url) {
this.showImportDialog = true
}
})
},
methods: {
},
closeDialog() {
this.showImportDialog = false
this.$emit('importDialogClose')

View File

@@ -44,8 +44,9 @@
import DataTable from '@/components/Table/DataTable/index.vue'
import { getUpdateObjURL } from '@/utils/common'
import { sleep } from '@/utils/time'
import { EditableInputFormatter, StatusFormatter } from '@/components/Table/TableFormatters'
import { EditableInputFormatter } from '@/components/Table/TableFormatters'
import { encryptPassword } from '@/utils/crypto'
import getStatusColumnMeta from '@/components/Table/ListTable/TableAction/const'
export default {
name: 'ImportTable',
@@ -223,38 +224,7 @@ export default {
},
methods: {
generateTableColumns(tableTitles, tableData) {
const vm = this
const columns = [{
prop: '@status',
label: vm.$t('Status'),
width: '80px',
align: 'center',
formatter: StatusFormatter,
formatterArgs: {
faChoices: {
ok: 'fa-check text-primary',
error: 'fa-times text-danger',
pending: 'fa-clock-o'
},
getChoicesKey(val) {
if (val === 'ok' || val === 'pending') {
return val
}
return 'error'
},
getTip(val) {
if (val === 'ok') {
return vm.$t('Success')
} else if (val === 'pending') {
return vm.$t('Pending')
} else if (val && val.name === 'error') {
return val.error
}
return ''
},
hasTips: true
}
}]
const columns = [{ ...getStatusColumnMeta.bind(this)().status }]
for (const item of tableTitles) {
const dataItemLens = tableData.map(d => {
if (!d) {

View File

@@ -71,7 +71,16 @@ export default {
this.listenViewPort()
},
mounted() {
this.$eventBus.$on('labelSearch', label => {
this.$eventBus.$on('labelSearch', this.labelSearchHandler)
},
beforeDestroy(label) {
this.$eventBus.$off('labelSearch', this.labelSearchHandler)
},
methods: {
handleCascaderFocus() {
this.setSearchFocus()
},
labelSearchHandler(label) {
if (!label) {
this.labelValue = []
this.showLabelSearch = true
@@ -82,14 +91,6 @@ export default {
setTimeout(() => {
this.showLabelSearch = true
}, 500)
})
},
destroyed() {
this.$eventBus.$off('labelSearch')
},
methods: {
handleCascaderFocus() {
this.setSearchFocus()
},
handleCascaderVisibleChange(visible) {
const input = this.$refs.labelCascader.$el

View File

@@ -40,8 +40,8 @@ export default {
handleExportClick: {
type: Function,
default: function({ selectedRows }) {
const { exportOptions, tableUrl } = this
const url = exportOptions?.url ? exportOptions.url : tableUrl
// const { exportOptions, tableUrl } = this
const url = this.iExportOptions.url
this.dialogExportVisible = true
this.$nextTick(() => {
this.$eventBus.$emit('showExportDialog', { selectedRows, url, name: this.name })
@@ -106,7 +106,7 @@ export default {
{
name: 'actionSetting',
icon: 'system-setting',
tip: this.$t('TableSetting'),
tip: this.$t('ListPreference'),
has: this.hasColumnSetting,
callback: this.handleTableSettingClick.bind(this)
},
@@ -155,7 +155,18 @@ export default {
})
},
iExportOptions() {
return assignIfNot(this.exportOptions, { url: this.tableUrl })
/**
* 原本是使用 assignIfNot 此函数内部使用 partialRight, 该函数
* 只在目标对象的属性未定义时才从源对象复制属性,如果目标对象已经有值,则保留原值
* 那如果首次点击的树节点,那么此时 url 就会被确定,后续点击的树节点,那么 url 就将不会携带节点信息
*
*/
// return assignIfNot(this.exportOptions, { url: this.tableUrl })
return {
...this.exportOptions,
url: this.tableUrl
}
}
},
methods: {

View File

@@ -0,0 +1,40 @@
import { StatusFormatter } from '@/components/Table/TableFormatters'
import i18n from '@/i18n/i18n'
export const getStatusColumnMeta = (prop = '@status') => {
return {
status: {
prop: prop,
label: i18n.t('Status'),
width: '80px',
align: 'center',
formatter: StatusFormatter,
formatterArgs: {
faChoices: {
ok: 'fa-check text-primary',
error: 'fa-times text-danger',
pending: 'fa-clock-o'
},
getChoicesKey: (val) => {
if (val === 'ok' || val === 'pending') {
return val
}
return 'error'
},
getTip: (val) => {
if (val === 'ok') {
return i18n.t('Success')
} else if (val === 'pending') {
return i18n.t('Pending')
} else if ((val && val.name === 'error') || val.error !== undefined) {
return val.error
}
return ''
},
hasTips: true
}
}
}
}
export default getStatusColumnMeta

View File

@@ -189,22 +189,23 @@ export default {
}
},
mounted() {
this.urlUpdated[this.tableUrl] = location.href
this.$set(this.urlUpdated, this.tableUrl, location.href)
},
deactivated() {
this.isDeactivated = true
},
activated() {
this.isDeactivated = false
const preURL = this.urlUpdated[this.tableUrl]
if (!preURL || preURL === location.href) {
return
}
this.urlUpdated[this.tableUrl] = location.href
this.$log.debug('Reload the table get latest data: pre ', preURL, ' current: ', location.href)
setTimeout(() => {
this.$nextTick(() => {
this.isDeactivated = false
const cleanUrl = this.tableUrl.split('?')[0]
const preURL = this.urlUpdated[cleanUrl]
if (!preURL || preURL === location.href) return
this.$set(this.urlUpdated, this.tableUrl, location.href)
this.$log.debug('Reload the table get latest data: pre ', preURL, ' current: ', location.href)
this.reloadTable()
}, 500)
})
},
methods: {
handleActionInitialDone() {

View File

@@ -51,7 +51,7 @@ export default {
const formatterArgs = Object.assign(this.formatterArgsDefault, this.col.formatterArgs || {})
return {
formatterArgs: formatterArgs,
data: formatterArgs.async ? [] : (this.cellValue || []),
listData: formatterArgs.async ? [] : (this.cellValue || []),
amount: '',
asyncGetDone: false
}
@@ -68,17 +68,18 @@ export default {
return [this.$t('Loading') + '...']
}
const getItem = this.formatterArgs.getItem || (item => item.name)
let data = []
if (Array.isArray(this.data)) {
data = this.data.map(item => getItem(item)) || []
} else {
// object {key: [value]}
data = Object.entries(this.data).map(([key, value]) => {
if (Array.isArray(this.listData)) {
data = this.listData.map(item => getItem(item)).filter(Boolean)
} else if (this.listData && typeof this.listData === 'object') {
data = Object.entries(this.listData).map(([key, value]) => {
const item = { key: key, value: value }
return getItem(item)
}) || []
}).filter(Boolean)
}
data = data.filter(Boolean)
return data
},
showItems() {
@@ -86,8 +87,12 @@ export default {
}
},
watch: {
cellValue() {
this.computeAmount()
cellValue: {
handler(newValue) {
// listData 需要重新赋值一遍 items 重新计算
this.listData = newValue
this.computeAmount()
}
}
},
async mounted() {
@@ -105,6 +110,7 @@ export default {
// object {key: [value]}
cellValue = Object.keys(this.cellValue)
}
this.amount = (cellValue?.filter(value => !this.cellValueToRemove.includes(value)) || []).length
}
},
@@ -127,7 +133,7 @@ export default {
const params = this.formatterArgs.ajax.params || {}
const transform = this.formatterArgs.ajax.transform || (resp => resp[this.col.prop.replace('_amount', '')])
const response = await this.$axios.get(url, { params: params })
this.data = transform(response)
this.listData = transform(response)
this.asyncGetDone = true
}
}

View File

@@ -1,5 +1,5 @@
<template>
<span>{{ value }}</span>
<span class="date">{{ dateValue }}</span>
</template>
<script>
@@ -10,24 +10,31 @@ export default {
name: 'DateFormatter',
extends: BaseFormatter,
data() {
let value
if (this.cellValue) {
value = toSafeLocalDateStr(this.cellValue)
} else {
value = '-'
}
// let value
// if (this.cellValue) {
// value = toSafeLocalDateStr(this.cellValue)
// } else {
// value = '-'
// }
// const locale = this.$i18n.locale
// const value = dt.toLocaleString(locale, { hourCycle: 'h23' })
// debug(this.$i18n.locale)
return {
value: value
}
// return {
// value: value
// }
// return {
// value: `${year}-${month}-${date} ${hour}:${minutes}:${seconds}`
// }
return {}
},
computed: {
dateValue() {
if (this.cellValue) {
return toSafeLocalDateStr(this.cellValue)
} else {
return '-'
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,7 +1,8 @@
<template>
<div style="width: 100%;min-height: 20px" @click.stop="editCell">
<div class="edit-container" style="min-height: 20px" @click.stop="editCell">
<el-input
v-if="inEditMode"
ref="inputRef"
v-model="value"
class="editInput"
size="mini"
@@ -9,8 +10,17 @@
@keyup.enter.native="onInputEnter"
/>
<template v-else>
<span>{{ iCellValue }}</span>
<span class="cellValue">{{ iCellValue }}</span>
<a
v-if="formatterArgs.showEditBtn"
:class="[{ 'disabled-link': this.$store.getters.currentOrgIsRoot },'edit-btn']"
style="padding-left: 5px"
@click="editCell"
>
<i class="fa fa-edit" />
</a>
</template>
</div>
</template>
@@ -61,6 +71,9 @@ export default {
editCell() {
if (this.formatterArgs.canEdit) {
this.inEditMode = true
this.$nextTick(() => {
this.$refs.inputRef.focus()
})
}
},
getCellValue(val) {
@@ -88,7 +101,7 @@ export default {
}
</script>
<style scoped>
<style lang="scss" scoped>
.editInput ::v-deep .el-input__inner {
padding: 2px;
line-height: 12px;
@@ -97,4 +110,35 @@ export default {
.editInput {
padding: -6px;
}
.edit-btn {
visibility: hidden;
position: relative;
transition: all 1s;
& > i {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
}
.edit-container {
display: flex;
flex-wrap: nowrap;
&:hover {
.edit-btn {
visibility: visible;
}
}
.cellValue {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
</style>

View File

@@ -4,8 +4,8 @@
<span v-if="!iLabels || iLabels.length === 0" style="vertical-align: top;">
-
</span>
<div v-else>
<div
<span v-else class="label-wrapper">
<span
v-for="label of iLabels"
:key="label.id"
>
@@ -15,8 +15,9 @@
class="tag-formatter"
@click="handleLabelSearch(label)"
/>
</div>
</div>
<span />
</span>
</span>
</a>
<a
v-if="formatterArgs.showEditBtn"
@@ -30,15 +31,16 @@
v-if="showDialog"
:title="$tc('BindLabel')"
:visible.sync="showDialog"
class="tag-dialog"
width="600px"
@cancel="handleCancel"
@confirm="handleConfirm"
>
<el-row :gutter="1" class="tag-select">
<el-row class="tag-select">
<el-col :span="12">
<Select2 v-model="keySelect2.value" v-bind="keySelect2" @change="handleKeyChanged" />
</el-col>
<el-col :span="12">
<el-col :span="12" style="padding-left: 5px">
<Select2
v-model="valueSelect2.value"
:disabled="!keySelect2.value"
@@ -129,6 +131,16 @@ export default {
}
},
computed: {},
watch: {
cellValue: {
handler(newValue) {
if (newValue) {
this.initial = this.formatterArgs.getLabels(this.cellValue)
this.iLabels = [...this.initial]
}
}
}
},
mounted() {
this.initial = this.formatterArgs.getLabels(this.cellValue)
this.iLabels = [...this.initial]
@@ -207,18 +219,11 @@ export default {
flex-wrap: wrap;
& > span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.tag-select {
::v-deep .el-input__inner::placeholder {
font-size: 13px;
}
}
.edit-btn {
visibility: hidden;
position: relative;
@@ -233,9 +238,14 @@ export default {
.label-container {
display: flex;
height: 28px;
.label-formatter-col {
overflow: hidden;
&:hover {
overflow: auto;
}
}
&:hover {
@@ -249,15 +259,23 @@ export default {
}
}
.tag-zone {
margin: 20px 0 0 0;
border: solid 1px #ebeef5;
padding: 10px;
background: #f2f2f5;
.tag-dialog {
.tag-zone {
margin: 20px 0 0 0;
border: solid 1px #ebeef5;
padding: 10px;
background: #f2f2f5;
.tag-formatter {
margin: 1px 3px;
display: inline-block;
.tag-formatter {
margin: 1px 3px;
display: inline-block;
}
}
.tag-select {
::v-deep .el-input__inner::placeholder {
font-size: 13px;
}
}
}
@@ -270,7 +288,6 @@ export default {
.tag-formatter {
margin: 2px 0;
}
.tag-tip {

View File

@@ -24,6 +24,9 @@ export default {
},
isDisplay(row) {
return true
},
callback({ row }) {
return null
}
}
}
@@ -49,10 +52,11 @@ export default {
methods: {
onChange(val) {
this.$axios.patch(this.patchUrl, this.patchData).then(res => {
this.$message.success(this.$t('updateSuccessMsg'))
this.formatterArgs.callback(this.row)
this.$message.success(this.$t('UpdateSuccessMsg'))
}).catch(err => {
this.value = !val
this.$message.error(this.$t('updateErrorMsg' + ' ' + err))
this.$message.error(this.$t('UpdateErrorMsg' + ' ' + err))
})
}
}

View File

@@ -55,8 +55,9 @@ export default {
<style lang="scss" scoped>
.tag {
display: flex;
display: inline-block;
flex-wrap: wrap;
& > span {
overflow: hidden;
white-space: nowrap;

View File

@@ -17,7 +17,7 @@
size="small"
type="info"
@click="handleTagClick(v,k)"
@close="handleTagClose(k)"
@close.stop="handleTagClose(k)"
>
<strong v-if="v.label">{{ v.label + ':' }}</strong>
<span v-if="v.valueLabel">{{ v.valueLabel }}</span>
@@ -128,7 +128,7 @@ export default {
deep: true
},
filterTags: {
handler() {
handler(newValue) {
this.$emit('tag-search', this.filterMaps)
},
deep: true
@@ -137,6 +137,15 @@ export default {
if (newValue === '' && oldValue !== '') {
this.emptyCount = 1
}
},
'$route'(to, from) {
if (from.query !== to.query) {
this.filterTags = {}
if (to.query && Object.keys(to.query).length) {
const routeFilter = this.checkInTableColumns(this.options)
this.filterTagSearch(routeFilter)
}
}
}
},
mounted() {
@@ -202,9 +211,10 @@ export default {
label: current.label,
value: valueDecode
}
if (curChildren.length > 0) {
for (const item of curChildren) {
if (valueDecode === item.value) {
if (valueDecode === String(item.value)) {
searchFieldOption.valueLabel = item.label
break
}

View File

@@ -39,7 +39,7 @@ export default {
showRenameBtn: false,
drag: {
isCopy: false,
isMove: true
isMove: !this.$store.getters.currentOrgIsRoot
}
},
callback: {

View File

@@ -35,13 +35,29 @@ export default {
]),
announcement() {
const ann = this.publicSettings.ANNOUNCEMENT
return { id: ann['ID'], subject: ann['SUBJECT'], content: ann['CONTENT'], link: ann['LINK'] }
return {
id: ann['ID'],
subject: ann['SUBJECT'],
content: ann['CONTENT'],
link: ann['LINK'],
date_start: ann['DATE_START'],
date_end: ann['DATE_END']
}
},
enabled() {
return this.publicSettings.ANNOUNCEMENT_ENABLED && (this.announcement.content || this.announcement.subject)
return this.publicSettings.ANNOUNCEMENT_ENABLED && (this.announcement.content || this.announcement.subject) && this.isDateValid
},
title() {
return this.$t('Announcement') + ': ' + this.announcement.subject
},
isDateValid() {
if (this.announcement.date_start === undefined || this.announcement.date_end === undefined) {
return true
}
const now = new Date()
const start = new Date(this.announcement.date_start)
const end = new Date(this.announcement.date_end)
return now >= start && now <= end
}
},
methods: {

View File

@@ -1,12 +1,17 @@
import VueCookie from 'vue-cookie'
import store from '@/store'
export function getLangCode() {
export function getLangCode(withInternalCode = false) {
const cookieLang = VueCookie.get('django_language')
const browserLang = navigator.systemLanguage || navigator.language || navigator.userLanguage
let lang = cookieLang || browserLang || 'en'
lang = lang.replace('-', '_')
if (lang !== 'zh_hant') {
lang = lang.slice(0, 2)
let lang = cookieLang || navigator.language.toLowerCase()
if (withInternalCode) {
const languages = store.getters.publicSettings['LANGUAGES']
for (const langObj of languages) {
if (langObj['other_codes'].indexOf(lang) > -1) {
lang = langObj['code']
break
}
}
}
return lang
}

View File

@@ -33,11 +33,13 @@ export default {
query[k] = v
}
let key
if (this.$route.name.toLowerCase().includes('list')) {
return _.trimEnd(this.$route.path, '/') + '?' + new URLSearchParams(query).toString()
key = _.trimEnd(this.$route.path, '/') + '?' + new URLSearchParams(query).toString()
} else {
return new Date().getTime()
key = new Date().getTime()
}
return key
},
chatAiEnabled() {
return this.publicSettings?.CHAT_AI_ENABLED
@@ -50,7 +52,7 @@ export default {
<style lang="scss" scoped>
.app-main {
background-color: #f3f3f4;
height: 100% !important;
height: 100vh !important;
width: 100%;
position: relative;
overflow: auto;

View File

@@ -59,18 +59,22 @@ export default {
this.$router.push({ name: 'Profile' })
break
case 'PasswordAndSSHKey':
this.$router.push({ name: 'PasswordAndSSHKey' })
this.$router.push({ name: 'SSHKeyList' })
break
case 'Preferences':
this.$router.push({ name: 'Preferences' })
break
case 'logout':
this.logout()
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
break
}
},
logout() {
async logout() {
const currentOrg = this.$store.getters.currentOrg
if (currentOrg.autoEnter || currentOrg.is_system) {
await this.$store.dispatch('users/setCurrentOrg', this.$store.getters.preOrg)
}
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
}
}
}

View File

@@ -25,7 +25,6 @@ export default {
return {
langCookeName: 'django_language', // 后端Django需要的COOKIE KEY
supportLanguages: [],
currentLangCode: '',
defaultLang: {
title: 'English',
code: 'en',
@@ -41,41 +40,25 @@ export default {
}, {})
},
currentLang() {
const lang = this.supportedLangMapper[this.currentLangCode] || this.defaultLang
return lang
const lang = getLangCode(true)
return this.supportedLangMapper[lang] || this.defaultLang
}
},
mounted() {
this.currentLangCode = getLangCode()
this.supportLanguages = store.getters.publicSettings['LANGUAGES'].map(item => {
let code = item.code.replace('-', '_')
if (code !== 'zh_hant') {
code = code.slice(0, 2)
}
return {
title: item.name,
code: code,
code: item.code,
cookieCode: item.code
}
})
this.changeMomentLang()
},
methods: {
changeLang() {
if (this.currentLang.code !== this.$i18n.locale) {
this.changeLangTo(this.currentLang)
}
},
changeMomentLang() {
if (this.currentLang.code.indexOf('en') > -1) {
this.$moment.locale('en')
} else if (this.currentLang.code.indexOf('ja') > -1) {
this.$moment.locale('ja')
} else if (this.currentLang.code.indexOf('zh_hant') > -1) {
this.$moment.locale('zh-tw')
} else {
this.$moment.locale('zh-cn')
}
const lang = getLangCode()
this.$moment.locale(lang)
document.documentElement.lang = lang
},
changeLangTo(item) {
this.$axios.get(`/core/i18n/${item.cookieCode}/`).then(() => {

View File

@@ -70,7 +70,7 @@
<span class="msg-detail-time">{{ formatDate(currentMsg.date_created) }}</span>
</div>
<div class="msg-detail-txt">
<span v-sanitize="currentMsg.content.message" />
<MarkDown :value="currentMsg.content.message" />
</div>
</div>
</Dialog>
@@ -80,10 +80,14 @@
<script>
import { toSafeLocalDateStr } from '@/utils/time'
import Dialog from '@/components/Dialog'
import MarkDown from '@/components/Widgets/MarkDown'
export default {
name: 'SiteMessages',
components: { Dialog },
components: {
Dialog,
MarkDown
},
data() {
return {
show: false,

View File

@@ -108,7 +108,7 @@ export default {
methods: {
async handleSelectView(key, keyPath) {
const routeName = this.viewsMapper[key] || '/'
localStorage.setItem('PreView', key)
localStorage.setItem('preView', key)
// Next 之前要重置 init 状态,否则这些路由守卫就不走了
await store.dispatch('app/reset')
if (!this.tipHasRead) {

View File

@@ -176,7 +176,7 @@ export default {
// 未找到与之对应的
& ::v-deep .el-submenu__title {
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
padding: 0 8px;
line-height: $headerHeight;
height: $headerHeight;

View File

@@ -17,6 +17,14 @@ export default {
const { icon, title } = context.props
const vNodes = []
const ellipsisStyle = {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
width: '100%',
display: 'inline-block'
}
if (icon) {
if (icon.startsWith('fa-')) {
vNodes.push(<i class={`fa ${icon}`} />)
@@ -26,7 +34,7 @@ export default {
}
if (title) {
vNodes.push(<span slot='title'>{title}</span>)
vNodes.push(<span slot='title' style={ellipsisStyle} title={ title }>{title}</span>)
}
return vNodes
}

View File

@@ -12,9 +12,9 @@
trigger="hover"
>
<span slot="reference" style="width: 100%">
<span v-show="!isCollapse" class="view-title">
{{ isRouteMeta.title || '' }}
</span>
<el-tooltip v-show="!isCollapse" :content="isRouteMeta.title" :open-delay="1000" placement="bottom" effect="dark" class="view-title">
<span class="text-overflow">{{ isRouteMeta.title || '' }}</span>
</el-tooltip>
<span class="icon-zone">
<svg-icon class="icon" icon-class="switch" />
</span>

View File

@@ -116,7 +116,6 @@ export default {
<style lang="scss" scoped>
.page {
height: calc(100vh - 50px);
overflow-y: hidden;
overflow-x: hidden;

View File

@@ -46,7 +46,7 @@
<el-alert v-if="helpMessage" type="success">
<span v-sanitize="helpMessage" class="announcement-main" />
</el-alert>
<transition v-if="!loading" appear mode="out-in" name="fade-transform">
<transition appear mode="out-in" name="fade-transform">
<slot>
<keep-alive>
<component :is="computeActiveComponent" />
@@ -83,16 +83,18 @@ export default {
},
data() {
return {
loading: true,
toSentenceCase: toSentenceCase
loading: false,
toSentenceCase: toSentenceCase,
activeTab: this.activeMenu
}
},
computed: {
iActiveMenu: {
get() {
return this.activeMenu
return this.activeTab
},
set(item) {
this.activeTab = item
this.$emit('update:activeMenu', item)
}
},
@@ -119,16 +121,13 @@ export default {
},
watch: {
$route(to, from) {
const activeTab = to.query?.tab
if (activeTab && this.iActiveMenu !== activeTab) {
this.iActiveMenu = activeTab
}
// 好像没必要
// const activeTab = to.query?.tab
// if (activeTab && this.iActiveMenu !== activeTab) {
// this.iActiveMenu = activeTab
// }
}
},
activated() {
this.iActiveMenu = this.getPropActiveTab()
this.loading = false
},
created() {
this.iActiveMenu = this.getPropActiveTab()
this.loading = false
@@ -136,15 +135,8 @@ export default {
methods: {
handleTabClick(tab) {
this.$emit('tab-click', tab)
this.$emit('update:activeMenu', tab.name)
this.iActiveMenu = tab.name
this.$cookie.set(this.$route.path, tab.name, 1)
if (this.$router.currentRoute.query[this.$route.path]) {
this.$router.push({
query: { ...this.$route.query, [this.$route.path]: '' }
})
}
},
getPropActiveTab() {
let activeTab = ''

View File

@@ -80,15 +80,5 @@ export default [
}
}
]
},
{
path: 'job-execution-log',
name: 'JobExecutionLog',
component: () => import('@/views/audits/JobExecutionLogList'),
meta: {
title: i18n.t('JobExecutionLog'),
icon: 'task',
permissions: ['audits.view_joblog']
}
}
]

View File

@@ -3,6 +3,7 @@ import i18n from '@/i18n/i18n'
import SessionRoutes from './sessions'
import LogRoutes from './audits'
import JobRoutes from './jobs'
import empty from '@/layout/empty'
import store from '@/store'
@@ -54,6 +55,18 @@ export default {
permissions: []
},
children: LogRoutes
},
{
path: '/audit/jobs',
component: empty,
redirect: '',
name: 'AuditsJobs',
meta: {
title: i18n.t('JobsAudit'),
icon: 'job',
permissions: ['audits.view_joblog']
},
children: JobRoutes
}
]
}

53
src/router/audit/jobs.js Normal file
View File

@@ -0,0 +1,53 @@
import i18n from '@/i18n/i18n'
import empty from '@/layout/empty'
export default [
{
path: 'job-list',
name: 'JobList',
component: empty,
redirect: {
name: 'JobList'
},
meta: {
title: i18n.t('JobList'),
icon: 'task',
permissions: ['audits.view_joblog']
},
children: [
{
path: '',
name: 'AuditJobList',
component: () => import('@/views/audits/JobPeriodTaskList'),
meta: {
title: i18n.t('JobList'),
permissions: []
}
}
]
},
{
path: 'job-execution-log',
name: 'JobExecutionLog',
component: empty,
redirect: {
name: 'AuditJobExecutionLog'
},
meta: {
title: i18n.t('JobExecutionLog'),
icon: 'history',
permissions: ['audits.view_joblog']
},
children: [
{
path: '',
name: 'AuditJobExecutionLog',
component: () => import('@/views/audits/JobExecutionLogList'),
meta: {
title: i18n.t('JobExecutionLog'),
permissions: ['audits.view_joblog']
}
}
]
}
]

View File

@@ -24,7 +24,6 @@ export default [
meta: {
title: i18n.t('BaseUserLoginAclList'),
app: 'acls',
licenseRequired: true,
resource: 'loginacl',
disableOrgsChange: true
},
@@ -201,7 +200,7 @@ export default [
hidden: true,
meta: {
title: i18n.t('CommandGroupDetail'),
activeMenu: ''
activeMenu: '/console/perms/acls/cmd-acls'
}
},
{

View File

@@ -2,7 +2,6 @@ import i18n from '@/i18n/i18n'
import empty from '@/layout/empty'
import XPackRoutes from './xpack'
const globalSubmenu = () => import('@/layout/globalOrg.vue')
export default [
{
path: 'assets',
@@ -294,12 +293,11 @@ export default [
},
{
path: 'platforms',
component: globalSubmenu,
component: empty,
meta: {
permissions: ['assets.view_platform'],
resource: 'platform',
icon: 'platform',
disableOrgsChange: true
icon: 'platform'
},
redirect: '',
children: [

View File

@@ -28,13 +28,39 @@ export default {
},
{
path: '/profile/password-and-ssh-key',
name: 'PasswordAndSSHKey',
component: () => import('@/views/profile/PasswordAndSSHKey/index'),
component: empty,
meta: {
title: i18n.t('PasswordAndSSHKey'),
icon: 'personal',
permissions: []
}
icon: 'personal'
},
children: [
{
path: '',
component: () => import('@/views/profile/PasswordAndSSHKey/index'),
name: 'SSHKeyList',
icon: 'key',
meta: { title: i18n.t('PasswordAndSSHKey'), permissions: ['authentication.view_sshkey'] }
},
{
path: 'create',
component: () => import('@/views/profile/PasswordAndSSHKey/SSHKey/SSHKeyCreateUpdate.vue'),
name: 'SSHKeyCreate',
hidden: true,
meta: {
title: i18n.t('SSHKey'),
permissions: ['authentication.add_sshkey']
}
},
{
path: ':id/update',
component: () => import('@/views/profile/PasswordAndSSHKey/SSHKey/SSHKeyCreateUpdate.vue'),
name: 'SSHKeyUpdate',
hidden: true,
meta: {
title: i18n.t('SSHKey'),
permissions: ['authentication.change_sshkey']
}
}
]
},
{
path: '/profile/passkeys',

View File

@@ -40,11 +40,26 @@ const mutations = {
updateChaMessageContentById(state, { id, data }) {
const chats = state.activeChat.chats || []
const filterChat = chats.filter((chat) => chat.message.id === id)?.[0] || {}
if (Object.keys(filterChat).length > 0) {
filterChat.message.content = data.message.content
const index = chats.findIndex((chat) => chat.message.id === data.message.id)
if (index === -1) {
// 如果没有记录,直接添加新消息
chats.push({
message: { id: data.message.id },
reasoning: { content: data.reasoning.content },
result: { content: data.result.content },
role: data.role,
type: data.type,
create_time: data.create_time
})
} else {
chats?.push(data)
if (data.reasoning.content !== '') {
chats[index].reasoning.content = data.reasoning.content
}
if (data.result.content !== '') {
chats[index].result.content = data.result.content
}
}
}
}

View File

@@ -71,10 +71,11 @@ const mutations = {
state.consoleOrgs = state.consoleOrgs.filter(i => i.id !== org.id)
},
SET_CURRENT_ORG(state, org) {
// 系统组织和全局组织不设置成 Pre org
if (!state.currentOrg?.autoEnter && !state.currentOrg?.is_root) {
state.preOrg = state.currentOrg
setPreOrgLocal(state.username, state.currentOrg)
// 系统组织不设置成 Pre org
const currentOrg = state.currentOrg
if (currentOrg && !currentOrg.autoEnter && !currentOrg.is_system) {
state.preOrg = currentOrg
setPreOrgLocal(state.username, currentOrg)
}
state.currentOrg = org
saveCurrentOrgLocal(state.username, org)
@@ -144,6 +145,7 @@ const actions = {
const systemOrg = {
id: orgUtil.SYSTEM_ORG_ID,
name: 'SystemSetting',
is_system: true,
autoEnter: new Date().getTime()
}
commit('SET_CURRENT_ORG', systemOrg)

BIN
src/styles/icons/cloud.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -38,7 +38,7 @@ body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: auto;
color: var(--color-text-primary);
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 1.428;
}
@@ -586,3 +586,24 @@ li.rmenu i.fa {
color: var(--color-link);
}
}
.el-table__row {
::-webkit-scrollbar {
width: 6px; /* 设置垂直滚动条的宽度 */
height: 6px; /* 设置水平滚动条的高度 */
}
}
.black-theme-popover {
width: 300px;
max-height: 700px;
overflow-y: scroll;
}
.text-overflow {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
width: 100%;
}

View File

@@ -23,6 +23,10 @@ $single-menu-height: 38px;
}
}
.el-menu--vertical {
background-color: #fff;
}
.el-menu {
border-right: none !important;
background-color: inherit !important;
@@ -139,6 +143,10 @@ $single-menu-height: 38px;
.nest-menu .level2-menu {
line-height: $single-menu-height;
}
.el-tooltip {
width: 55px !important;
}
}
}
}

View File

@@ -116,6 +116,11 @@
background: url('./icons/clickhouse.png') no-repeat center left transparent;
}
&.WebCloud_ico_docu {
background: url('./icons/cloud.png') no-repeat center left transparent;
background-size: contain;
}
&.ico_loading {
background: url(./icons/loading.gif) no-repeat scroll 0 0 transparent;
}

View File

@@ -1,7 +1,7 @@
import VueCookie from 'vue-cookie'
const CURRENT_ORG_KEY = 'jms_current_org'
const CURRENT_ROLE_KEY = 'jms_current_role'
const CURRENT_ORG_KEY = 'currentOrg'
const CURRENT_ROLE_KEY = 'currentRole'
let cookieNamePrefix = VueCookie.get('SESSION_COOKIE_NAME_PREFIX')
if (!cookieNamePrefix || ['""', "''"].indexOf(cookieNamePrefix) > -1) {
cookieNamePrefix = ''
@@ -17,7 +17,7 @@ export function setTokenToCookie(value, expires) {
}
export function getCurrentRoleLocal(username) {
const key = CURRENT_ROLE_KEY + '_' + username
const key = CURRENT_ROLE_KEY + ':' + username
const role = localStorage.getItem(key)
if (role) {
return parseInt(role) || null
@@ -26,12 +26,12 @@ export function getCurrentRoleLocal(username) {
}
export function saveCurrentRoleLocal(username, role) {
const key = CURRENT_ROLE_KEY + '_' + username
const key = CURRENT_ROLE_KEY + ':' + username
return localStorage.setItem(key, role)
}
export function getCurrentOrgLocal(username) {
const key = CURRENT_ORG_KEY + '_' + username
const key = CURRENT_ORG_KEY + ':' + username
const value = localStorage.getItem(key)
try {
return JSON.parse(value)
@@ -41,18 +41,23 @@ export function getCurrentOrgLocal(username) {
}
export function saveCurrentOrgLocal(username, org) {
const key = CURRENT_ORG_KEY + '_' + username
localStorage.setItem(key, JSON.stringify(org))
VueCookie.set('X-JMS-ORG', org.id)
const key = CURRENT_ORG_KEY + ':' + username
if (org) {
localStorage.setItem(key, JSON.stringify(org))
VueCookie.set('X-JMS-ORG', org.id)
} else {
localStorage.removeItem(key)
VueCookie.delete('X-JMS-ORG')
}
}
export function setPreOrgLocal(username, org) {
const key = 'PRE_ORG_' + username
const key = 'preOrg' + ':' + username
localStorage.setItem(key, JSON.stringify(org))
}
export function getPreOrgLocal(username) {
const key = 'PRE_ORG_' + username
const key = 'preOrg' + ':' + username
const value = localStorage.getItem(key)
try {
return JSON.parse(value)

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