mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-14 11:55:34 +00:00
Compare commits
464 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49bdd98812 | ||
|
|
743c2ee6e9 | ||
|
|
9d70eecee0 | ||
|
|
b65664f9c4 | ||
|
|
f64def0bec | ||
|
|
06f6202bc4 | ||
|
|
6fa7800d6b | ||
|
|
54aa252c20 | ||
|
|
c083f6c4a4 | ||
|
|
73bb854ebb | ||
|
|
d6f9df277e | ||
|
|
ba78e33f89 | ||
|
|
09ef15cff0 | ||
|
|
62d520e625 | ||
|
|
f07a857813 | ||
|
|
2699d5e8eb | ||
|
|
fb398ca3e4 | ||
|
|
3230c37318 | ||
|
|
bc258a7ff8 | ||
|
|
64d610e282 | ||
|
|
4d95461b5c | ||
|
|
f364c8fdf9 | ||
|
|
88dc2d9271 | ||
|
|
3aced25da4 | ||
|
|
fcf142b696 | ||
|
|
2123037897 | ||
|
|
f5bc2842ec | ||
|
|
2c95e5f10b | ||
|
|
b3ff9c5bcb | ||
|
|
905e5e00b1 | ||
|
|
81db3d86fa | ||
|
|
ea15515264 | ||
|
|
4048a000c7 | ||
|
|
a60693c41c | ||
|
|
57d5c893d3 | ||
|
|
435ce24c75 | ||
|
|
4a757bb6bc | ||
|
|
32fa4f0b11 | ||
|
|
9f12e1aa18 | ||
|
|
6165709747 | ||
|
|
0ad1eef196 | ||
|
|
d2d07555b5 | ||
|
|
7f60224c6d | ||
|
|
bbf502c85d | ||
|
|
e6aaa52506 | ||
|
|
70affacfde | ||
|
|
40a8da5e58 | ||
|
|
24266bb929 | ||
|
|
14286b961e | ||
|
|
0498db9a8f | ||
|
|
266d107ffd | ||
|
|
4f2a9c0c6c | ||
|
|
affb0ec2bb | ||
|
|
3075d50357 | ||
|
|
e49da02c4d | ||
|
|
7df5736354 | ||
|
|
98886149f9 | ||
|
|
abb98d55b9 | ||
|
|
f9c979af88 | ||
|
|
89018a2258 | ||
|
|
a0c29563ca | ||
|
|
21223fddea | ||
|
|
254a2b58cc | ||
|
|
777c371070 | ||
|
|
0cba2b3116 | ||
|
|
c27dd0baef | ||
|
|
990aebefdd | ||
|
|
6f84312dbe | ||
|
|
d4c12fb38f | ||
|
|
1bb94824df | ||
|
|
5772430761 | ||
|
|
790941f361 | ||
|
|
a9ce01ac0e | ||
|
|
1cdc406e70 | ||
|
|
cb60660272 | ||
|
|
8625e21077 | ||
|
|
2251a1653e | ||
|
|
ff1debcbce | ||
|
|
17e5564cd7 | ||
|
|
615576b3fd | ||
|
|
c0d3fbb47a | ||
|
|
09075b13b7 | ||
|
|
4e92c1a77c | ||
|
|
7cc49bc907 | ||
|
|
2e2b5bf873 | ||
|
|
66b1d17dd2 | ||
|
|
ee26e47c4d | ||
|
|
aedf6d2158 | ||
|
|
6e848e65b4 | ||
|
|
5c0108906c | ||
|
|
6c1f8ec8f7 | ||
|
|
dda36d2b40 | ||
|
|
1abf30c347 | ||
|
|
697b5a3d13 | ||
|
|
74e4c3397e | ||
|
|
c227bf59a6 | ||
|
|
0092f6d6d7 | ||
|
|
747477b27c | ||
|
|
4943dab50c | ||
|
|
5cac3ee1f7 | ||
|
|
fa5a227aff | ||
|
|
792e8595b8 | ||
|
|
9d62614ff4 | ||
|
|
48c0f6e8c6 | ||
|
|
31b17b384d | ||
|
|
858d7a9d6f | ||
|
|
48b6c48581 | ||
|
|
38be9dd367 | ||
|
|
7e5570ad72 | ||
|
|
16476caa1e | ||
|
|
6a5c28ac26 | ||
|
|
5335faa789 | ||
|
|
fd1ee6ef7d | ||
|
|
52d8c34bbf | ||
|
|
9eac41c0c3 | ||
|
|
6881316203 | ||
|
|
d4ee8379e8 | ||
|
|
f64e877491 | ||
|
|
f46a63cfcf | ||
|
|
65a71df10e | ||
|
|
fd6b0532ba | ||
|
|
e02d05a327 | ||
|
|
23740cdce0 | ||
|
|
a50f224227 | ||
|
|
f8ec327f11 | ||
|
|
1d76e037a4 | ||
|
|
98f5f38694 | ||
|
|
b78b95e67a | ||
|
|
5f60130952 | ||
|
|
5a82931fc2 | ||
|
|
ad49e3250b | ||
|
|
7ef95f4567 | ||
|
|
25a9d21fd7 | ||
|
|
b849df1dc1 | ||
|
|
1519ccb8e2 | ||
|
|
47e88e7bb4 | ||
|
|
c86aef999c | ||
|
|
2f9d7ab826 | ||
|
|
b4311b8a59 | ||
|
|
04a97a9923 | ||
|
|
c7624f9092 | ||
|
|
fc29fc6c6d | ||
|
|
7afc501db5 | ||
|
|
8ed5672e95 | ||
|
|
951c9f56c5 | ||
|
|
2c0e079aa2 | ||
|
|
a8e7ea9c80 | ||
|
|
30143f833a | ||
|
|
ad88daef9a | ||
|
|
9eb80eb6ca | ||
|
|
112de6e81c | ||
|
|
d01f903a9e | ||
|
|
af6d0aff7c | ||
|
|
edf8621e8f | ||
|
|
eeb15c624a | ||
|
|
79e2d49a3d | ||
|
|
628395e447 | ||
|
|
6cb6e6444b | ||
|
|
1b9aa23761 | ||
|
|
537c385ecf | ||
|
|
c0f7d6e7ff | ||
|
|
78df96f888 | ||
|
|
829957090d | ||
|
|
026c8f37ea | ||
|
|
97340c6aac | ||
|
|
bbe54eae48 | ||
|
|
9137207055 | ||
|
|
0ef30ef651 | ||
|
|
5ae8a6c9e4 | ||
|
|
ace1dcd0b8 | ||
|
|
5df487d6bd | ||
|
|
6e548749e1 | ||
|
|
99885d9f28 | ||
|
|
83163e11e3 | ||
|
|
8c191fee67 | ||
|
|
ff9862fa06 | ||
|
|
db0cea7051 | ||
|
|
305c713a57 | ||
|
|
8a5f93e268 | ||
|
|
c4d262150b | ||
|
|
94f161f7e6 | ||
|
|
2c5bfb3f4c | ||
|
|
8e12837a77 | ||
|
|
4385d84f01 | ||
|
|
214bb28c4c | ||
|
|
14c2285ac8 | ||
|
|
18cbe578f0 | ||
|
|
e19ded8365 | ||
|
|
0150008075 | ||
|
|
b0bca65cab | ||
|
|
cb95b9ba4f | ||
|
|
4a840288c5 | ||
|
|
11b1d6638d | ||
|
|
2225807a36 | ||
|
|
a8baca81d5 | ||
|
|
30161c7178 | ||
|
|
e222d147f6 | ||
|
|
3ddc41707c | ||
|
|
a245055150 | ||
|
|
ba5fdf2027 | ||
|
|
e1999e5ce8 | ||
|
|
be4e0b5e35 | ||
|
|
82c381b80d | ||
|
|
7d71aa96b9 | ||
|
|
93408e52c1 | ||
|
|
cea03df4eb | ||
|
|
72ee5f60b9 | ||
|
|
f6a8e5634b | ||
|
|
868e77c983 | ||
|
|
2a3fd42ca1 | ||
|
|
596a26bfb6 | ||
|
|
2fc8cea9ef | ||
|
|
6f8a5c2bfc | ||
|
|
260901351f | ||
|
|
66845e58db | ||
|
|
0e9e549bea | ||
|
|
785611414e | ||
|
|
19267ee001 | ||
|
|
65326916ca | ||
|
|
0b925ccf33 | ||
|
|
58505d2b50 | ||
|
|
ecae504a80 | ||
|
|
844bc5b44f | ||
|
|
c1dcf82fbd | ||
|
|
336406ddff | ||
|
|
81e8e650bf | ||
|
|
af6308e1b3 | ||
|
|
aae552f374 | ||
|
|
b4c1ee786a | ||
|
|
61da88114d | ||
|
|
279859ce81 | ||
|
|
820bb075a3 | ||
|
|
d96bd76ca9 | ||
|
|
7c56c889f2 | ||
|
|
3547fb26ad | ||
|
|
ac1363b377 | ||
|
|
6b5c90ee86 | ||
|
|
8164fa57ef | ||
|
|
96c9f229e2 | ||
|
|
58313f5fe0 | ||
|
|
738a9c3da1 | ||
|
|
0f10ed9ffc | ||
|
|
294e05cb06 | ||
|
|
1598dcbfbc | ||
|
|
cf2d6a47c2 | ||
|
|
c8fb334dae | ||
|
|
93dba0bbee | ||
|
|
2832d876fd | ||
|
|
5cd89cee6a | ||
|
|
2f69861361 | ||
|
|
8f51d9b0ea | ||
|
|
3a2b6d79fb | ||
|
|
67ede69685 | ||
|
|
17c4c9b2ef | ||
|
|
0319d43942 | ||
|
|
dcd088fd58 | ||
|
|
9875ded710 | ||
|
|
e26cd95ef9 | ||
|
|
1bb8e8c709 | ||
|
|
09617fa606 | ||
|
|
62a6d11332 | ||
|
|
8f00dbf23e | ||
|
|
8fd624e0b7 | ||
|
|
cef6521a2b | ||
|
|
da1217972a | ||
|
|
9efacb68b6 | ||
|
|
b3c22f96d8 | ||
|
|
6bf15655b7 | ||
|
|
ee3dc30985 | ||
|
|
21da017f8e | ||
|
|
38b4810d9e | ||
|
|
d49aae69ab | ||
|
|
012fefa3ea | ||
|
|
1f91b9a72f | ||
|
|
e061d9eb75 | ||
|
|
00cd04e103 | ||
|
|
4b3b1a723f | ||
|
|
2d3a43c202 | ||
|
|
e8e751668d | ||
|
|
c6e0a17aaa | ||
|
|
066d81446c | ||
|
|
a63b07cf2e | ||
|
|
0ab96fa413 | ||
|
|
e064c1cfc4 | ||
|
|
2913699597 | ||
|
|
c413623a22 | ||
|
|
92f08605df | ||
|
|
3fb32ad81c | ||
|
|
760dbad5ac | ||
|
|
878933bc07 | ||
|
|
603ebff771 | ||
|
|
c23ed70df4 | ||
|
|
d21598cf1c | ||
|
|
34f1b5d662 | ||
|
|
c493aca11b | ||
|
|
cc937d600b | ||
|
|
fcbe61cb92 | ||
|
|
b39f01a023 | ||
|
|
794ad35f84 | ||
|
|
729b07798e | ||
|
|
f2514f68b8 | ||
|
|
30d4044d20 | ||
|
|
e7cd6e49e8 | ||
|
|
75276d37e4 | ||
|
|
5a92c4f3ee | ||
|
|
ae6fb22fae | ||
|
|
ccd98606a1 | ||
|
|
7f2ed5d038 | ||
|
|
d24741ab4b | ||
|
|
9ac5eabff2 | ||
|
|
18b5fafa41 | ||
|
|
8a76bb05ac | ||
|
|
ad5b5c7c20 | ||
|
|
fe7d43f669 | ||
|
|
aad23f3de5 | ||
|
|
9c33093b01 | ||
|
|
c94a5dfa1f | ||
|
|
143973531c | ||
|
|
7db590950c | ||
|
|
7e65a52062 | ||
|
|
226d118d28 | ||
|
|
0e3bd186fe | ||
|
|
50e8dfc86d | ||
|
|
a023e03074 | ||
|
|
a0a592f064 | ||
|
|
4d86edd65e | ||
|
|
c597bc1dca | ||
|
|
a3d45fd4b9 | ||
|
|
e16d3c2e3f | ||
|
|
e251127f6e | ||
|
|
58b8917739 | ||
|
|
ad423e921a | ||
|
|
326302ea5f | ||
|
|
be213268fa | ||
|
|
b82231f3ea | ||
|
|
f976800cde | ||
|
|
4973c62618 | ||
|
|
5db7538216 | ||
|
|
786528f9b2 | ||
|
|
661409b99d | ||
|
|
702bb3acfa | ||
|
|
448b64757c | ||
|
|
4289c36bd4 | ||
|
|
bbccba3731 | ||
|
|
57b6e02960 | ||
|
|
8c6d2a1150 | ||
|
|
1295fb7fd2 | ||
|
|
d3fbb9a391 | ||
|
|
2b14cf0225 | ||
|
|
a78f8a3633 | ||
|
|
c4868dabac | ||
|
|
faf848dca5 | ||
|
|
05edffe173 | ||
|
|
4b3862443a | ||
|
|
536ebd7513 | ||
|
|
7dce39d79c | ||
|
|
a8389304d6 | ||
|
|
649d4ac848 | ||
|
|
781bbe0ffa | ||
|
|
2771d80749 | ||
|
|
86b5cb81fc | ||
|
|
273dc7234b | ||
|
|
9927bf46b5 | ||
|
|
652919f827 | ||
|
|
9f5d121f9c | ||
|
|
efb5de9289 | ||
|
|
d94b3c9fc9 | ||
|
|
50482d5d8b | ||
|
|
15ed63f589 | ||
|
|
1ffcf9e7b4 | ||
|
|
6afc54391b | ||
|
|
d1bb6b8909 | ||
|
|
a665d6ed20 | ||
|
|
00e2e7e433 | ||
|
|
47c05922ae | ||
|
|
a036562e5c | ||
|
|
a92f09f6af | ||
|
|
a2d6281a6a | ||
|
|
36fa19df89 | ||
|
|
f72baa33e3 | ||
|
|
9c415fca87 | ||
|
|
0ef5852fd8 | ||
|
|
dac04cfa61 | ||
|
|
68149563fc | ||
|
|
37cce2effd | ||
|
|
5412e40856 | ||
|
|
6ae70c8c41 | ||
|
|
06fc671547 | ||
|
|
6571615643 | ||
|
|
b0c03b6c1f | ||
|
|
6be89ba479 | ||
|
|
56f58d49cc | ||
|
|
c4efba4c83 | ||
|
|
14a15efcd9 | ||
|
|
3e32eae8f3 | ||
|
|
7e0f53e403 | ||
|
|
0d172f94b2 | ||
|
|
b1fc95db5b | ||
|
|
9c16321ed7 | ||
|
|
1e98827e98 | ||
|
|
38d5b81cd0 | ||
|
|
fda09a6712 | ||
|
|
5bed98b991 | ||
|
|
6d4c9f3676 | ||
|
|
fe84cf42eb | ||
|
|
e6295c9dc5 | ||
|
|
d79b178229 | ||
|
|
474e132f50 | ||
|
|
cad8c9aeb2 | ||
|
|
d72cf1e825 | ||
|
|
9e9c8639bc | ||
|
|
386afc9e0c | ||
|
|
acfe87f1c6 | ||
|
|
a9700e77f2 | ||
|
|
36ed0a02c2 | ||
|
|
383d57c1c7 | ||
|
|
96863322ac | ||
|
|
abf979c177 | ||
|
|
d9a34a898f | ||
|
|
8105306278 | ||
|
|
e8073d9feb | ||
|
|
ac64ab7145 | ||
|
|
8bd4e8b7c0 | ||
|
|
d581996e87 | ||
|
|
24a850c99f | ||
|
|
ecaa4cfa70 | ||
|
|
8aebf366bc | ||
|
|
863b89888d | ||
|
|
85012d9558 | ||
|
|
7db080b418 | ||
|
|
622ab4cbc9 | ||
|
|
f862a39c03 | ||
|
|
c3d0367662 | ||
|
|
75e234e3e2 | ||
|
|
7f8aa6a65a | ||
|
|
f6aa6fc2ac | ||
|
|
f1ad3ba85b | ||
|
|
55c959fd50 | ||
|
|
7de33e175f | ||
|
|
f7c3a3ac3b | ||
|
|
1d74137144 | ||
|
|
de547e6a5a | ||
|
|
d3ebdb0d61 | ||
|
|
004c0e3f66 | ||
|
|
57e4b65059 | ||
|
|
bd0d1a1014 | ||
|
|
7b3e38ac78 | ||
|
|
4927c6520a | ||
|
|
c10145c9cc | ||
|
|
7eccb20269 | ||
|
|
26ec366332 | ||
|
|
0ed6ad055e | ||
|
|
ff5b77c1a9 | ||
|
|
7f788a4610 | ||
|
|
553805e03d | ||
|
|
f9f90e55c1 | ||
|
|
4be2db93c7 | ||
|
|
65855b3e07 | ||
|
|
3ff5519bfa | ||
|
|
a79af0a7c4 | ||
|
|
4f120ae81d | ||
|
|
9e7c570216 | ||
|
|
1cd4f1f228 | ||
|
|
5aa28903dd |
@@ -1,2 +1,3 @@
|
||||
lina
|
||||
dist
|
||||
node_modules
|
||||
|
||||
@@ -2,4 +2,5 @@ build/*.js
|
||||
src/assets
|
||||
public
|
||||
dist
|
||||
lina
|
||||
node_modules
|
||||
|
||||
45
.github/release-config.yml
vendored
Normal file
45
.github/release-config.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name-template: 'v$RESOLVED_VERSION'
|
||||
tag-template: 'v$RESOLVED_VERSION'
|
||||
categories:
|
||||
- title: '🌱 新功能 Features'
|
||||
labels:
|
||||
- 'feature'
|
||||
- 'enhancement'
|
||||
- 'feat'
|
||||
- '新功能'
|
||||
- title: '🚀 性能优化 Optimization'
|
||||
labels:
|
||||
- 'perf'
|
||||
- 'opt'
|
||||
- 'refactor'
|
||||
- 'Optimization'
|
||||
- '优化'
|
||||
- title: '🐛 Bug修复 Bug Fixes'
|
||||
labels:
|
||||
- 'fix'
|
||||
- 'bugfix'
|
||||
- 'bug'
|
||||
- title: '🧰 其它 Maintenance'
|
||||
labels:
|
||||
- 'chore'
|
||||
- 'docs'
|
||||
exclude-labels:
|
||||
- 'no'
|
||||
- '无需处理'
|
||||
- 'wontfix'
|
||||
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
|
||||
version-resolver:
|
||||
major:
|
||||
labels:
|
||||
- 'major'
|
||||
minor:
|
||||
labels:
|
||||
- 'minor'
|
||||
patch:
|
||||
labels:
|
||||
- 'patch'
|
||||
default: patch
|
||||
template: |
|
||||
## 版本变化 What’s Changed
|
||||
|
||||
$CHANGES
|
||||
12
.github/workflows/jms-generic-action-handler.yml
vendored
Normal file
12
.github/workflows/jms-generic-action-handler.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
on: [push, pull_request, release]
|
||||
|
||||
name: JumpServer repos generic handler
|
||||
|
||||
jobs:
|
||||
generic_handler:
|
||||
name: Run generic handler
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: jumpserver/action-generic-handler@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PRIVATE_TOKEN }}
|
||||
46
.github/workflows/release-drafter.yml
vendored
Normal file
46
.github/workflows/release-drafter.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
name: Create Release And Upload assets
|
||||
|
||||
jobs:
|
||||
create-realese:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: |
|
||||
TAG=$(basename ${GITHUB_REF})
|
||||
VERSION=${TAG/v/}
|
||||
echo "::set-output name=TAG::$TAG"
|
||||
echo "::set-output name=VERSION::$VERSION"
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: release-drafter/release-drafter@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
config-name: release-config.yml
|
||||
version: ${{ steps.get_version.outputs.TAG }}
|
||||
tag: ${{ steps.get_version.outputs.TAG }}
|
||||
|
||||
build-and-release:
|
||||
needs: create-realese
|
||||
name: Build and Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build it and upload
|
||||
uses: jumpserver/action-build-upload-assets@node10
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-realese.outputs.upload_url }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
lina/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "src/views/xpack"]
|
||||
path = src/views/xpack
|
||||
url = git@github.com:fit2cloudrd/js-xpack-web.git
|
||||
|
||||
25
Dockerfile
25
Dockerfile
@@ -1,12 +1,23 @@
|
||||
FROM node:10 as stage-build
|
||||
WORKDIR /data
|
||||
ADD ./package.json /data/package.json
|
||||
ADD ./yarn.lock /data/yarn.lock
|
||||
RUN yarn
|
||||
ADD . /data
|
||||
RUN yarn build:prod
|
||||
ARG VERSION
|
||||
ENV VERSION=$VERSION
|
||||
ARG NPM_REGISTRY="https://registry.npm.taobao.org"
|
||||
ENV NPM_REGISTY=$NPM_REGISTRY
|
||||
ARG SASS_BINARY_SITE="https://npm.taobao.org/mirrors/node-sass"
|
||||
ENV SASS_BINARY_SITE=$SASS_BINARY_SITE
|
||||
|
||||
WORKDIR /data
|
||||
|
||||
RUN npm config set sass_binary_site=${SASS_BINARY_SITE}
|
||||
RUN npm config set registry ${NPM_REGISTRY}
|
||||
RUN yarn config set registry ${NPM_REGISTRY}
|
||||
COPY package.json yarn.lock /data/
|
||||
COPY utils /data/utils/
|
||||
RUN ls && cd utils && bash -xieu build.sh dep
|
||||
|
||||
ADD . /data
|
||||
RUN cd utils && bash -xieu build.sh build
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=stage-build /data/dist /opt/lina/
|
||||
COPY --from=stage-build /data/release/lina /opt/lina
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
@@ -2,8 +2,8 @@ server {
|
||||
listen 80;
|
||||
|
||||
location /ui/ {
|
||||
try_files $uri / /ui/index.html;
|
||||
alias /opt/lina/;
|
||||
try_files $uri / /ui/index.html;
|
||||
alias /opt/lina/;
|
||||
}
|
||||
|
||||
location / {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"dependencies": {
|
||||
"@ztree/ztree_v3": "3.5.44",
|
||||
"axios": "0.18.1",
|
||||
"axios-retry": "^3.1.9",
|
||||
"deepmerge": "^4.2.2",
|
||||
"echarts": "^4.7.0",
|
||||
"element-ui": "2.13.2",
|
||||
@@ -82,8 +83,8 @@
|
||||
"less-loader": "^5.0.0",
|
||||
"lint-staged": "^10.1.2",
|
||||
"mockjs": "1.0.1-beta3",
|
||||
"node-sass": "^4.9.0",
|
||||
"runjs": "^4.3.2",
|
||||
"sass": "^1.26.10",
|
||||
"sass-loader": "^7.1.0",
|
||||
"script-ext-html-webpack-plugin": "2.1.3",
|
||||
"script-loader": "0.7.2",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Cache-control" content="no-cache">
|
||||
<meta http-equiv="Cache" content="no-cache">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= webpackConfig.name %></title>
|
||||
|
||||
@@ -51,6 +51,14 @@ export function refreshLdapUserCache() {
|
||||
})
|
||||
}
|
||||
|
||||
export function StartLdapUserCache() {
|
||||
return request({
|
||||
disableFlashErrorMsg: true,
|
||||
url: '/api/v1/settings/ldap/users/?cache_police=1',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function importLdapUser(data) {
|
||||
return request({
|
||||
disableFlashErrorMsg: true,
|
||||
|
||||
8
src/api/ticket.js
Normal file
8
src/api/ticket.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getTicketOpenCount(assign) {
|
||||
return request({
|
||||
url: `/api/v1/tickets/tickets/?assign=${assign}&status=open&offset=0&limit=15&display=1&draw=1/`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -65,3 +65,7 @@ export function logout() {
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function refreshSessionIdAge() {
|
||||
return getProfile()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<div :class="grouped ? 'el-button-group' : ''">
|
||||
<el-button v-for="item in iActions" :key="item.name" :size="size" v-bind="item" @click="handleClick(item.name)">
|
||||
<i v-if="item.fa" :class="'fa ' + item.fa" />{{ item.title }}
|
||||
<el-tooltip v-if="['actionExport', 'actionImport', 'actionRefresh'].indexOf(item.name) !== -1" effect="dark" :content="item.tip" placement="top">
|
||||
<i v-if="item.fa" :class="'fa ' + item.fa" />{{ item.title }}
|
||||
</el-tooltip>
|
||||
<span v-else>
|
||||
<i v-if="item.fa" :class="'fa ' + item.fa" />{{ item.title }}
|
||||
</span>
|
||||
</el-button>
|
||||
<el-dropdown v-if="iMoreActions.length > 0" trigger="click" @command="handleClick">
|
||||
<el-dropdown v-if="iMoreActions.length > 0" trigger="click" :placement="moreActionsPlacement" @command="handleClick">
|
||||
<el-button :size="size" :type="moreActionsType" class="btn-more-actions">
|
||||
{{ iMoreActionsTitle }}<i class="el-icon-arrow-down el-icon--right" />
|
||||
</el-button>
|
||||
@@ -47,6 +52,11 @@ export default {
|
||||
moreActionsType: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
moreActionsPlacement: {
|
||||
type: String,
|
||||
default: 'bottom'
|
||||
// 居中对齐
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -109,6 +119,11 @@ export default {
|
||||
if (!has) {
|
||||
continue
|
||||
}
|
||||
// 是否有分割线
|
||||
const divided = this.checkItem(action, 'divided', false)
|
||||
delete action['divided']
|
||||
action.divided = divided
|
||||
|
||||
// 是否是disabled
|
||||
const can = this.checkItem(action, 'can')
|
||||
delete action['can']
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<table style="width: 100%">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<AssetSelect ref="assetSelect" />
|
||||
<AssetSelect ref="assetSelect" :can-select="canSelect" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -49,6 +49,12 @@ export default {
|
||||
onAddSuccess: {
|
||||
type: Function,
|
||||
default: (objects, that) => {}
|
||||
},
|
||||
canSelect: {
|
||||
type: Function,
|
||||
default(row, index) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -34,6 +34,12 @@ export default {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
canSelect: {
|
||||
type: Function,
|
||||
default(row, index) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -68,6 +74,7 @@ export default {
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/assets/',
|
||||
hasTree: true,
|
||||
canSelect: this.canSelect,
|
||||
columns: [
|
||||
{
|
||||
prop: 'hostname',
|
||||
@@ -81,7 +88,7 @@ export default {
|
||||
},
|
||||
{
|
||||
prop: 'ip',
|
||||
label: this.$t('assets.ip'),
|
||||
label: this.$t('assets.ipDomain'),
|
||||
sortable: 'custom'
|
||||
}
|
||||
],
|
||||
@@ -156,20 +163,20 @@ export default {
|
||||
.el-select{
|
||||
width: 100%;
|
||||
}
|
||||
.page /deep/ .page-heading{
|
||||
.page ::v-deep .page-heading{
|
||||
display: none;
|
||||
}
|
||||
.el-dialog__wrapper /deep/.el-dialog__body{
|
||||
.el-dialog__wrapper ::v-deep .el-dialog__body{
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.page /deep/ .treebox{
|
||||
.page ::v-deep .treebox{
|
||||
height: inherit !important;
|
||||
}
|
||||
.asset-select-dialog >>> .transition-box:first-child {
|
||||
background-color: #f3f3f3 ;
|
||||
}
|
||||
|
||||
.el-dialog__wrapper /deep/.el-dialog__body .wrapper-content {
|
||||
.el-dialog__wrapper ::v-deep .el-dialog__body .wrapper-content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,20 +114,23 @@ export default {
|
||||
columns: [
|
||||
{
|
||||
prop: 'hostname',
|
||||
label: this.$t('assets.Hostname')
|
||||
label: this.$t('assets.Hostname'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'ip',
|
||||
label: this.$t('assets.ip')
|
||||
label: this.$t('assets.ip'),
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
prop: 'username',
|
||||
label: this.$t('assets.Username')
|
||||
label: this.$t('assets.Username'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'version',
|
||||
label: this.$t('assets.Version'),
|
||||
width: '50px'
|
||||
width: '70px'
|
||||
},
|
||||
{
|
||||
prop: 'date_created',
|
||||
@@ -138,6 +141,7 @@ export default {
|
||||
prop: 'id',
|
||||
label: this.$t('common.Action'),
|
||||
align: 'center',
|
||||
width: 150,
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
@@ -150,7 +154,7 @@ export default {
|
||||
type: 'primary',
|
||||
callback: function(val) {
|
||||
this.MFAInfo.asset = val.cellValue
|
||||
if (this.MFAVerifyAt + this.MFA_TTl * 1000 > (new Date()).valueOf()) {
|
||||
if (!this.needMFAVerify) {
|
||||
this.showMFADialog = true
|
||||
this.MFAConfirmed = true
|
||||
this.$axios.get(`/api/v1/assets/asset-user-auth-infos/${this.MFAInfo.asset}/`).then(res => {
|
||||
@@ -172,7 +176,7 @@ export default {
|
||||
this.$axios.delete(`/api/v1/assets/asset-users/${val.cellValue}/`).then(() => {
|
||||
this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
this.$refs.ListTable.reloadTable()
|
||||
}).catch(() => this.$message.error(this.$t('common.deleteFailedMsg')))
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -235,7 +239,8 @@ export default {
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'MFA_TTl',
|
||||
'MFAVerifyAt'
|
||||
'MFAVerifyAt',
|
||||
'publicSettings'
|
||||
]),
|
||||
needMFAVerify() {
|
||||
if (!this.publicSettings.SECURITY_VIEW_AUTH_NEED_MFA) {
|
||||
@@ -308,6 +313,7 @@ export default {
|
||||
key: ''
|
||||
}
|
||||
this.showDialog = false
|
||||
this.$refs.ListTable.reloadTable()
|
||||
},
|
||||
Onchange(e) {
|
||||
const vm = this
|
||||
@@ -347,6 +353,7 @@ export default {
|
||||
key: ''
|
||||
}
|
||||
this.showDialog = false
|
||||
this.$refs.ListTable.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ export default {
|
||||
groups: []
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.optionUrlMeta()
|
||||
},
|
||||
@@ -54,7 +53,7 @@ export default {
|
||||
this.meta = data.actions[this.method.toUpperCase()] || {}
|
||||
this.generateColumns()
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
this.$log.error(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
@@ -63,9 +62,11 @@ export default {
|
||||
switch (type) {
|
||||
case 'choice':
|
||||
type = 'radio-group'
|
||||
field.options = fieldMeta.choices.map(v => {
|
||||
return { label: v.display_name, value: v.value }
|
||||
})
|
||||
if (!fieldMeta.read_only) {
|
||||
field.options = fieldMeta.choices.map(v => {
|
||||
return { label: v.display_name, value: v.value }
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'datetime':
|
||||
type = 'date-picker'
|
||||
@@ -76,6 +77,9 @@ export default {
|
||||
case 'field':
|
||||
type = ''
|
||||
field.component = Select2
|
||||
if (fieldMeta.required) {
|
||||
field.el.clearable = false
|
||||
}
|
||||
break
|
||||
case 'string':
|
||||
type = 'input'
|
||||
@@ -89,12 +93,14 @@ export default {
|
||||
break
|
||||
}
|
||||
if (type === 'radio-group') {
|
||||
const options = fieldMeta.choices.map(v => {
|
||||
return { label: v.display_name, value: v.value }
|
||||
})
|
||||
if (options.length > 4) {
|
||||
type = 'select'
|
||||
field.el.filterable = true
|
||||
if (!fieldMeta.read_only) {
|
||||
const options = fieldMeta.choices.map(v => {
|
||||
return { label: v.display_name, value: v.value }
|
||||
})
|
||||
if (options.length > 4) {
|
||||
type = 'select'
|
||||
field.el.filterable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
field.type = type
|
||||
@@ -128,7 +134,8 @@ export default {
|
||||
},
|
||||
generateField(name) {
|
||||
let field = { id: name, prop: name, el: {}, attrs: {}}
|
||||
const fieldMeta = this.meta[name] || {}
|
||||
// const fieldMeta = this.meta[name] || this.meta['attrs']['children'][name] || {}
|
||||
const fieldMeta = this.meta[name] || ((this.meta['attrs']) ? (this.meta['attrs']['children'][name]) : {})
|
||||
field.label = fieldMeta.label
|
||||
field = this.generateFieldByType(fieldMeta.type, field, fieldMeta)
|
||||
field = this.generateFieldByName(name, field)
|
||||
@@ -146,12 +153,25 @@ export default {
|
||||
})
|
||||
return this.generateFields(fields)
|
||||
},
|
||||
generateFieldAttrs(name) {
|
||||
const fields = []
|
||||
Object.keys(this.meta[name]['children']).forEach((key, i) => {
|
||||
const filed = this.generateField(key)
|
||||
fields.push(filed)
|
||||
})
|
||||
return fields
|
||||
},
|
||||
generateFields(data) {
|
||||
let fields = []
|
||||
for (let field of data) {
|
||||
if (field instanceof Array) {
|
||||
const items = this.generateFieldGroup(field)
|
||||
fields = [...fields, ...items]
|
||||
} else if (field === 'attrs') {
|
||||
const items = this.generateFieldAttrs(field)
|
||||
fields = [...fields, ...items]
|
||||
// 修改title插入ID
|
||||
this.groups[this.groups.length - 1].name = items[0].id
|
||||
} else if (typeof field === 'string') {
|
||||
field = this.generateField(field)
|
||||
fields.push(field)
|
||||
@@ -165,15 +185,23 @@ export default {
|
||||
},
|
||||
generateColumns() {
|
||||
this.totalFields = this.generateFields(this.fields)
|
||||
this.$log.debug('Total fields: ', this.totalFields)
|
||||
},
|
||||
setFieldError(name, error) {
|
||||
const field = this.totalFields.find((v) => v.prop === name)
|
||||
if (!field) {
|
||||
return
|
||||
}
|
||||
if (field.attrs.error === error) {
|
||||
error += '.'
|
||||
if (typeof error === 'object') {
|
||||
const str = error
|
||||
error = ''
|
||||
Object.keys(str).forEach(key => {
|
||||
error += `${parseInt(key) + 1}.${str[key][0]} `
|
||||
})
|
||||
}
|
||||
// if (field.attrs.error === error) {
|
||||
// error += '.'
|
||||
// }
|
||||
field.attrs.error = error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,19 @@ export default {
|
||||
}
|
||||
const option = {
|
||||
label: field.label,
|
||||
type: field.type,
|
||||
value: name
|
||||
|
||||
}
|
||||
if (field.type === 'choice' && field.choices) {
|
||||
option.children = field.choices.map(item => {
|
||||
if (typeof (item.value) === 'boolean') {
|
||||
if (item.value) {
|
||||
return { label: item.display_name, value: 'True' }
|
||||
} else {
|
||||
return { label: item.display_name, value: 'False' }
|
||||
}
|
||||
}
|
||||
return { label: item.display_name, value: item.value }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<DataTable v-if="!loading" ref="dataTable" v-loading="loading" :config="iConfig" v-bind="$attrs" v-on="$listeners" />
|
||||
<DataTable v-if="!loading" ref="dataTable" v-loading="loading" :config="iConfig" v-bind="$attrs" v-on="$listeners" @filter-change="filterChange" />
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
@@ -15,6 +15,10 @@ export default {
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
filterTable: {
|
||||
type: Function,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -55,6 +59,7 @@ export default {
|
||||
case 'name':
|
||||
col.formatter = DetailFormatter
|
||||
col.sortable = 'custom'
|
||||
col.showOverflowTooltip = true
|
||||
break
|
||||
case 'actions':
|
||||
col = {
|
||||
@@ -117,6 +122,37 @@ export default {
|
||||
}
|
||||
return col
|
||||
},
|
||||
addFilterIfNeed(col) {
|
||||
if (col.prop) {
|
||||
const column = this.meta[col.prop] || {}
|
||||
if (!column.filter) {
|
||||
return col
|
||||
}
|
||||
if (column.type === 'boolean') {
|
||||
col.filters = [
|
||||
{ text: this.$t('common.Yes'), value: true },
|
||||
{ text: this.$t('common.No'), value: false }
|
||||
]
|
||||
col.sortable = false
|
||||
col['column-key'] = col.prop
|
||||
}
|
||||
if (column.type === 'choice' && column.choices) {
|
||||
col.filters = column.choices.map(item => {
|
||||
if (typeof (item.value) === 'boolean') {
|
||||
if (item.value) {
|
||||
return { text: item.display_name, value: 'True' }
|
||||
} else {
|
||||
return { text: item.display_name, value: 'False' }
|
||||
}
|
||||
}
|
||||
return { text: item.display_name, value: item.value }
|
||||
})
|
||||
col.sortable = false
|
||||
col['column-key'] = col.prop
|
||||
}
|
||||
}
|
||||
return col
|
||||
},
|
||||
generateColumn(name) {
|
||||
const colMeta = this.meta[name] || {}
|
||||
const customMeta = this.config.columnsMeta ? this.config.columnsMeta[name] : {}
|
||||
@@ -126,6 +162,7 @@ export default {
|
||||
col = this.generateColumnByType(colMeta.type, col)
|
||||
col = Object.assign(col, customMeta)
|
||||
col = this.addHelpTipsIfNeed(col)
|
||||
col = this.addFilterIfNeed(col)
|
||||
return col
|
||||
},
|
||||
generateColumns() {
|
||||
@@ -141,6 +178,12 @@ export default {
|
||||
}
|
||||
config.columns = columns
|
||||
this.iConfig = config
|
||||
},
|
||||
filterChange(filters) {
|
||||
const key = Object.keys(filters)[0]
|
||||
const attr = {}
|
||||
attr[key] = filters[key][0]
|
||||
this.filterTable(attr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,8 +139,6 @@ export default {
|
||||
treeNode.name = treeNode.name + ' (' + assetsAmount + ')'
|
||||
this.zTree.updateNode(treeNode)
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
|
||||
})
|
||||
},
|
||||
onBodyMouseDown: function(event) {
|
||||
@@ -167,6 +165,10 @@ export default {
|
||||
if (!this.setting.showMenu) {
|
||||
return
|
||||
}
|
||||
// 屏蔽收藏资产
|
||||
if (treeNode.id === '-12') {
|
||||
return
|
||||
}
|
||||
if (!treeNode && event.target.tagName.toLowerCase() !== 'button' && $(event.target).parents('a').length === 0) {
|
||||
this.zTree.cancelSelectedNode()
|
||||
this.showRMenu('root', event.clientX, event.clientY)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<el-form-item>
|
||||
<el-button v-for="button in moreButtons" :key="button.title" size="small" v-bind="button" @click="handleClick(button)">{{ button.title }}</el-button>
|
||||
<el-button v-if="defaultButton && hasReset" size="small" @click="resetForm('form')">{{ $t('common.Reset') }}</el-button>
|
||||
<el-button v-if="defaultButton && hasSaveContinue" size="small" @click="submitForm('form', true)">{{ $t('common.SaveAndAddAnother') }}</el-button>
|
||||
<el-button v-if="defaultButton" size="small" :loading="isSubmitting" type="primary" @click="submitForm('form')">{{ $t('common.Submit') }}</el-button>
|
||||
</el-form-item>
|
||||
</ElFormRender>
|
||||
@@ -35,6 +36,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hasSaveContinue: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
@@ -60,11 +65,11 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
// 获取表单数据
|
||||
submitForm(formName) {
|
||||
submitForm(formName, addContinue) {
|
||||
const form = this.$refs[formName]
|
||||
form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$emit('submit', form.getFormValue(), form)
|
||||
this.$emit('submit', form.getFormValue(), form, addContinue)
|
||||
} else {
|
||||
this.$emit('invalid', valid)
|
||||
return false
|
||||
@@ -77,7 +82,7 @@ export default {
|
||||
},
|
||||
handleClick(button) {
|
||||
const callback = button.callback || function(values, form) {
|
||||
console.log('Click ', button.title, ': ', values)
|
||||
// console.log('Click ', button.title, ': ', values)
|
||||
}
|
||||
const form = this.$refs['form']
|
||||
const values = form.getFormValue()
|
||||
@@ -88,27 +93,27 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-form /deep/ .el-form-item {
|
||||
.el-form ::v-deep .el-form-item {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.el-form /deep/ .el-form-item__content {
|
||||
.el-form ::v-deep .el-form-item__content {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.el-form /deep/ .el-form-item__label {
|
||||
.el-form ::v-deep .el-form-item__label {
|
||||
padding: 0 30px 0 0;
|
||||
}
|
||||
|
||||
.el-form /deep/ .el-form-item__error {
|
||||
.el-form ::v-deep .el-form-item__error {
|
||||
position: inherit;
|
||||
}
|
||||
|
||||
.el-form /deep/ .form-group-header {
|
||||
.el-form ::v-deep .form-group-header {
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
.el-form /deep/ .help-block {
|
||||
.el-form ::v-deep .help-block {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
@@ -116,7 +121,7 @@ export default {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.el-form /deep/ .help-block a {
|
||||
.el-form ::v-deep .help-block a {
|
||||
color: #1c84c6;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
v-bind="tableAttrs"
|
||||
:data="data"
|
||||
:row-class-name="rowClassName"
|
||||
v-on="$listeners"
|
||||
@selection-change="selectStrategy.onSelectionChange"
|
||||
@select="selectStrategy.onSelect"
|
||||
@select-all="selectStrategy.onSelectAll($event, selectable)"
|
||||
@select-all="selectStrategy.onSelectAll($event, canSelect)"
|
||||
@sort-change="onSortChange"
|
||||
>
|
||||
<!--TODO 不用jsx写, 感觉template逻辑有点不清晰了-->
|
||||
@@ -90,11 +91,14 @@
|
||||
|
||||
<!--非树-->
|
||||
<template v-else>
|
||||
<el-data-table-column v-if="hasSelection" type="selection" :align="selectionAlign" />
|
||||
<el-data-table-column v-if="hasSelection" type="selection" :align="selectionAlign" :selectable="canSelect" />
|
||||
<el-data-table-column
|
||||
v-for="col in columns"
|
||||
:key="col.prop"
|
||||
:formatter="typeof col.formatter === 'function' ? col.formatter : null"
|
||||
:filters="col.filters || null"
|
||||
:filter-multiple="false"
|
||||
:filter-method="typeof col.filterMethod === 'function' ? col.filterMethod : null"
|
||||
v-bind="{align: columnsAlign, ...col}"
|
||||
>
|
||||
<template v-if="col.formatter && typeof col.formatter !== 'function'" v-slot:default="{row, column, index}">
|
||||
@@ -422,7 +426,7 @@ export default {
|
||||
onEdit: {
|
||||
type: Function,
|
||||
default(row) {
|
||||
console.log('On delete row')
|
||||
// console.log('On delete row')
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -713,6 +717,12 @@ export default {
|
||||
hasDetail: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
canSelect: {
|
||||
type: Function,
|
||||
default(row, index) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -953,6 +963,8 @@ export default {
|
||||
})
|
||||
},
|
||||
search(attrs, reset) {
|
||||
// 重置搜索结果到第一页
|
||||
this.page = defaultFirstPage
|
||||
// Orange 重置查询对象
|
||||
if (reset) {
|
||||
this.innerQuery = merge({}, attrs)
|
||||
@@ -962,6 +974,8 @@ export default {
|
||||
return this.getList()
|
||||
},
|
||||
searchDate(attrs) {
|
||||
// 重置搜索结果到第一页
|
||||
this.page = defaultFirstPage
|
||||
this.innerQuery = merge(this.innerQuery, attrs)
|
||||
return this.getList()
|
||||
},
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
.el-data-table /deep/ .el-pagination{
|
||||
.el-data-table ::v-deep .el-pagination{
|
||||
text-align: center !important;
|
||||
}
|
||||
.el-data-table /deep/ .el-table td{
|
||||
.el-data-table ::v-deep .el-table td{
|
||||
padding: 4px 0;
|
||||
}
|
||||
.el-data-table /deep/ .el-table th{
|
||||
.el-data-table ::v-deep .el-table th{
|
||||
padding: 4px 0;
|
||||
}
|
||||
.el-data-table/deep/ .el-form-item{
|
||||
.el-data-table ::v-deep .el-form-item{
|
||||
margin-bottom:10px !important ;
|
||||
margin-top:10px;
|
||||
}
|
||||
.el-data-table/deep/ .el-pagination{
|
||||
.el-data-table ::v-deep .el-pagination{
|
||||
padding:15px 0 !important ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,6 @@ export default {
|
||||
computed: {
|
||||
tableConfig() {
|
||||
const config = Object.assign(this.defaultConfig, this.config)
|
||||
this.$log.debug('Datatable found config change')
|
||||
return config
|
||||
},
|
||||
iListeners() {
|
||||
@@ -96,7 +95,6 @@ export default {
|
||||
watch: {
|
||||
config: {
|
||||
handler() {
|
||||
this.$log.debug('DataTable: found config change', this.tableConfig.url)
|
||||
// this.getList()
|
||||
},
|
||||
deep: true
|
||||
@@ -140,16 +138,16 @@ export default {
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.el-table /deep/ .el-table__row > td {
|
||||
.el-table ::v-deep .el-table__row > td {
|
||||
line-height: 1.5;
|
||||
padding: 8px 0;
|
||||
}
|
||||
.el-table /deep/ .el-table__row > td> div > span {
|
||||
.el-table ::v-deep .el-table__row > td> div > span {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.el-table /deep/ .el-table__header > thead > tr >th {
|
||||
.el-table ::v-deep .el-table__header > thead > tr >th {
|
||||
padding: 8px 0;
|
||||
background-color: #F5F5F6;
|
||||
font-size: 13px;
|
||||
@@ -160,11 +158,11 @@ export default {
|
||||
}
|
||||
|
||||
//分页
|
||||
.el-pagination /deep/ .el-pagination__total{
|
||||
.el-pagination ::v-deep .el-pagination__total{
|
||||
float: left;
|
||||
}
|
||||
|
||||
.el-pagination /deep/ .el-pagination__sizes{
|
||||
.el-pagination ::v-deep .el-pagination__sizes{
|
||||
float: left;
|
||||
}
|
||||
//修改颜色
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="treebox">
|
||||
<ul v-show="loading" class="ztree">
|
||||
{{ this.$t('common.tree.Loading') }}...
|
||||
</ul>
|
||||
<div v-show="!loading" class="treebox">
|
||||
<ul :id="iZTreeID" class="ztree">
|
||||
{{ this.$t('common.tree.Loading') }}...
|
||||
</ul>
|
||||
@@ -22,6 +25,7 @@
|
||||
import $ from '@/utils/jquery-vendor.js'
|
||||
import '@ztree/ztree_v3/js/jquery.ztree.all.min.js'
|
||||
import '@/styles/ztree.css'
|
||||
import axiosRetry from 'axios-retry'
|
||||
|
||||
const defaultObject = {
|
||||
type: Object,
|
||||
@@ -39,7 +43,9 @@ export default {
|
||||
iZTreeID: `zTree_${this._uid}`,
|
||||
iRMenuID: `rMenu_${this._uid}`,
|
||||
zTree: '',
|
||||
rMenu: ''
|
||||
rMenu: '',
|
||||
init: false,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -52,11 +58,30 @@ export default {
|
||||
// $('.treebox').css('height', window.innerHeight - 60)
|
||||
},
|
||||
beforeDestroy() {
|
||||
$.fn.zTree.destroy()
|
||||
$.fn.zTree.destroy(this.iZTreeID)
|
||||
},
|
||||
methods: {
|
||||
initTree: function() {
|
||||
this.$axios.get(this.treeSetting.treeUrl).then(res => {
|
||||
const vm = this
|
||||
let treeUrl
|
||||
if (this.init) {
|
||||
this.loading = true
|
||||
}
|
||||
if (this.init && this.treeSetting.treeUrl.indexOf('/perms/') !== -1 && this.treeSetting.treeUrl.indexOf('rebuild_tree') === -1) {
|
||||
treeUrl = (this.treeSetting.treeUrl.indexOf('?') === -1) ? `${this.treeSetting.treeUrl}?rebuild_tree=1` : `${this.treeSetting.treeUrl}&rebuild_tree=1`
|
||||
} else {
|
||||
treeUrl = this.treeSetting.treeUrl
|
||||
}
|
||||
this.$axios.get(treeUrl, {
|
||||
'axios-retry': {
|
||||
retries: 20,
|
||||
retryCondition: e => {
|
||||
return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 409
|
||||
},
|
||||
shouldResetTimeout: true,
|
||||
retryDelay: () => { return 5000 }
|
||||
}
|
||||
}).then(res => {
|
||||
if (!res) {
|
||||
res = []
|
||||
}
|
||||
@@ -65,6 +90,10 @@ export default {
|
||||
name: this.$t('common.tree.Empty')
|
||||
})
|
||||
}
|
||||
this.treeSetting.treeUrl = treeUrl
|
||||
if (this.init) {
|
||||
vm.zTree.destroy()
|
||||
}
|
||||
this.zTree = $.fn.zTree.init($(`#${this.iZTreeID}`), this.treeSetting, res)
|
||||
if (this.treeSetting.showRefresh) {
|
||||
this.rootNodeAddDom(
|
||||
@@ -79,6 +108,9 @@ export default {
|
||||
if (this.treeSetting.otherMenu) {
|
||||
$('.menu-actions').append(this.otherMenu)
|
||||
}
|
||||
}).finally(_ => {
|
||||
vm.loading = false
|
||||
vm.init = true
|
||||
})
|
||||
},
|
||||
rootNodeAddDom: function(ztree, callback) {
|
||||
@@ -95,7 +127,6 @@ export default {
|
||||
}
|
||||
const refreshIconRef = $('#tree-refresh')
|
||||
refreshIconRef.bind('click', function() {
|
||||
ztree.destroy()
|
||||
const result = callback()
|
||||
if (result && result.then) {
|
||||
result.finally(() => {
|
||||
@@ -158,7 +189,7 @@ export default {
|
||||
top: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
.ztree /deep/ .fa-refresh {
|
||||
.ztree ::v-deep .fa-refresh {
|
||||
font: normal normal normal 14px/1 FontAwesome !important;
|
||||
}
|
||||
.dropdown a:hover {
|
||||
|
||||
@@ -37,8 +37,8 @@ export default {
|
||||
showRemoveBtn: false,
|
||||
showRenameBtn: false,
|
||||
drag: {
|
||||
isCopy: true,
|
||||
isMove: true
|
||||
isCopy: false,
|
||||
isMove: false
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
@@ -72,7 +72,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
defaultCallback: function(action) {
|
||||
console.log(action)
|
||||
// console.log(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,17 +93,17 @@ export default {
|
||||
|
||||
<style lang='less' scoped>
|
||||
.datepicker{
|
||||
width: 240px;
|
||||
width: 233px;
|
||||
}
|
||||
.el-input__inner{
|
||||
border: 1px solid #dcdee2;
|
||||
border-radius: 3px;
|
||||
height: 36px;
|
||||
}
|
||||
/*.el-date-editor /deep/ .el-input__icon{*/
|
||||
/*.el-date-editor ::v-deep .el-input__icon{*/
|
||||
/* line-height: 28px;*/
|
||||
/*}*/
|
||||
.el-date-editor /deep/ .el-range-separator{
|
||||
.el-date-editor ::v-deep .el-range-separator{
|
||||
line-height: 28px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,7 +3,7 @@ export default {
|
||||
name: 'ItemValue',
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number, Function, Array, Object],
|
||||
type: [String, Number, Function, Array, Object, Boolean],
|
||||
default: ''
|
||||
},
|
||||
item: {
|
||||
@@ -15,10 +15,21 @@ export default {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toChoicesDisplay(value) {
|
||||
if (!value) {
|
||||
return this.$t('common.No')
|
||||
}
|
||||
return this.$t('common.Yes')
|
||||
}
|
||||
},
|
||||
render(h) {
|
||||
if (typeof this.formatter === 'function') {
|
||||
return this.formatter(this.item, this.value)
|
||||
}
|
||||
if (typeof this.value === 'boolean') {
|
||||
return <span>{this.toChoicesDisplay(this.value)}</span>
|
||||
}
|
||||
return <span>{this.value}</span>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
<template>
|
||||
<IBox :title="title" fa="fa-info-circle">
|
||||
<div class="content">
|
||||
<el-row v-if="this.$route.params.id" :gutter="10" class="item">
|
||||
<el-col :span="6"><div :style="{ 'text-align': align }" class="item-label"><label>ID: </label></div></el-col>
|
||||
<el-col :span="18"><div class="item-text">{{ this.$route.params.id }}</div></el-col>
|
||||
</el-row>
|
||||
<el-row v-for="item in items" :key="'card-' + item.key" :gutter="10" class="item">
|
||||
<el-col :span="6"><div :style="{ 'text-align': align }" class="item-label"><label>{{ item.key }}: </label></div></el-col>
|
||||
<el-col :span="18"><div class="item-text">
|
||||
<ItemValue :value="item.value" v-bind="item" />
|
||||
</div></el-col>
|
||||
<el-col :span="6">
|
||||
<div :style="{ 'text-align': align }" class="item-label"><label>{{ item.key }}: </label></div>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<div class="item-text">
|
||||
<ItemValue :value="item.value" v-bind="item" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<slot name="footer">
|
||||
<el-button v-if="showCancel" size="small" @click="onCancel">{{ cancelTitle }}</el-button>
|
||||
<el-button v-if="showConfirm" type="primary" size="small" @click="onConfirm">{{ confirmTitle }}</el-button>
|
||||
<el-button v-if="showConfirm" type="primary" size="small" :loading="loadingStatus" @click="onConfirm">{{ confirmTitle }}</el-button>
|
||||
</slot>
|
||||
</div>
|
||||
</el-dialog>
|
||||
@@ -42,6 +42,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
loadingStatus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
confirmTitle: {
|
||||
type: String,
|
||||
default() {
|
||||
|
||||
@@ -65,22 +65,24 @@ export default {
|
||||
sortable: true,
|
||||
formatterArgs: {
|
||||
route: 'AssetDetail'
|
||||
}
|
||||
},
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'ip',
|
||||
label: this.$t('assets.IP'),
|
||||
width: '140px',
|
||||
sortable: 'custom'
|
||||
},
|
||||
{
|
||||
prop: 'systemUsers',
|
||||
label: this.$t('assets.SystemUsers'),
|
||||
align: 'center',
|
||||
width: '200px',
|
||||
formatter: SystemUserFormatter,
|
||||
formatterArgs: {
|
||||
getUrl: this.getShowUrl.bind(this)
|
||||
}
|
||||
},
|
||||
showOverflowTooltip: true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
<template>
|
||||
<Dialog v-if="showExportDialog" :title="$t('common.Export')" :visible.sync="showExportDialog" :destroy-on-close="true" @confirm="handleExportConfirm()" @cancel="handleExportCancel()">
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item class="export-form" :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportOption">
|
||||
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
@@ -51,7 +56,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
showExportDialog: false,
|
||||
exportOption: '',
|
||||
exportOption: 'all',
|
||||
exportTypeOption: 'csv',
|
||||
meta: {}
|
||||
}
|
||||
},
|
||||
@@ -67,6 +73,8 @@ export default {
|
||||
const query = listTableRef.dataTable.getQuery()
|
||||
delete query['limit']
|
||||
delete query['offset']
|
||||
delete query['date_from']
|
||||
delete query['date_to']
|
||||
return query
|
||||
},
|
||||
tableHasQuery() {
|
||||
@@ -77,7 +85,7 @@ export default {
|
||||
{
|
||||
label: this.$t('common.imExport.ExportAll'),
|
||||
value: 'all',
|
||||
can: this.canExportAll
|
||||
can: this.canExportAll && !this.tableHasQuery
|
||||
},
|
||||
{
|
||||
label: this.$t('common.imExport.ExportOnlySelectedItems'),
|
||||
@@ -90,6 +98,20 @@ export default {
|
||||
can: this.tableHasQuery && this.canExportFiltered
|
||||
}
|
||||
]
|
||||
},
|
||||
exportTypeOptions() {
|
||||
return [
|
||||
{
|
||||
label: 'CSV',
|
||||
value: 'csv',
|
||||
can: true
|
||||
},
|
||||
{
|
||||
label: 'Excel',
|
||||
value: 'xlsx',
|
||||
can: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -121,7 +143,7 @@ export default {
|
||||
// delete query['limit']
|
||||
// delete query['offset']
|
||||
}
|
||||
query['format'] = 'csv'
|
||||
query['format'] = this.exportTypeOption
|
||||
const queryStr =
|
||||
(url.indexOf('?') > -1 ? '&' : '?') +
|
||||
queryUtil.stringify(query, '=', '&')
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
<template>
|
||||
<Dialog :title="$t('common.Import')" :visible.sync="showImportDialog" :destroy-on-close="true" @confirm="handleImportConfirm" @cancel="handleImportCancel()">
|
||||
<Dialog
|
||||
:title="$t('common.Import')"
|
||||
:visible.sync="showImportDialog"
|
||||
:destroy-on-close="true"
|
||||
:loading-status="loadStatus"
|
||||
@confirm="handleImportConfirm"
|
||||
@cancel="handleImportCancel()"
|
||||
>
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="importTypeOption">
|
||||
<el-radio v-for="option of importTypeOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('common.Import' )" :label-width="'100px'">
|
||||
<el-radio v-model="importOption" class="export-item" label="1">{{ this.$t('common.Create') }}</el-radio>
|
||||
<el-radio v-model="importOption" class="export-item" label="2">{{ this.$t('common.Update') }}</el-radio>
|
||||
@@ -26,7 +38,7 @@
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<el-button size="mini" type="default">{{ this.$t('common.SelectFile') }}</el-button>
|
||||
<div slot="tip" :class="uploadHelpTextClass" style="line-height: 1.5">{{ this.$t('common.imExport.onlyCSVFilesTips') }}</div>
|
||||
<!-- <div slot="tip" :class="uploadHelpTextClass" style="line-height: 1.5">{{ this.$t('common.imExport.onlyCSVFilesTips') }}</div>-->
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -63,19 +75,36 @@ export default {
|
||||
showImportDialog: false,
|
||||
importOption: '1',
|
||||
isCsv: true,
|
||||
errorMsg: ''
|
||||
errorMsg: '',
|
||||
loadStatus: false,
|
||||
importTypeOption: 'csv'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasSelected() {
|
||||
return this.selectedRows.length > 0
|
||||
},
|
||||
importTypeOptions() {
|
||||
return [
|
||||
{
|
||||
label: 'CSV',
|
||||
value: 'csv',
|
||||
can: true
|
||||
},
|
||||
{
|
||||
label: 'Excel',
|
||||
value: 'xlsx',
|
||||
can: true
|
||||
}
|
||||
]
|
||||
},
|
||||
upLoadUrl() {
|
||||
return this.url
|
||||
},
|
||||
downloadImportTempUrl() {
|
||||
const baseUrl = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)
|
||||
return baseUrl + '?format=csv&template=import&limit=1'
|
||||
const format = this.importTypeOption === 'csv' ? 'format=csv&template=import&limit=1' : 'format=xlsx&template=import&limit=1'
|
||||
const url = (this.url.indexOf('?') === -1) ? `${this.url}?${format}` : `${this.url}&${format}`
|
||||
return url
|
||||
},
|
||||
uploadHelpTextClass() {
|
||||
const cls = ['el-upload__tip']
|
||||
@@ -95,24 +124,28 @@ export default {
|
||||
this.$axios.put(
|
||||
this.upLoadUrl,
|
||||
item.file,
|
||||
{ headers: { 'Content-Type': 'text/csv' }, disableFlashErrorMsg: true }
|
||||
{ headers: { 'Content-Type': this.importTypeOption === 'csv' ? 'text/csv' : 'text/xlsx' }, disableFlashErrorMsg: true }
|
||||
).then((data) => {
|
||||
const msg = this.$t('common.imExport.updateSuccessMsg', { count: data.length })
|
||||
this.onSuccess(msg)
|
||||
}).catch(error => {
|
||||
this.catchError(error)
|
||||
}).finally(() => {
|
||||
this.loadStatus = false
|
||||
})
|
||||
},
|
||||
performCreate(item) {
|
||||
this.$axios.post(
|
||||
this.upLoadUrl,
|
||||
item.file,
|
||||
{ headers: { 'Content-Type': 'text/csv' }, disableFlashErrorMsg: true }
|
||||
{ headers: { 'Content-Type': this.importTypeOption === 'csv' ? 'text/csv' : 'text/xlsx' }, disableFlashErrorMsg: true }
|
||||
).then((data) => {
|
||||
const msg = this.$t('common.imExport.createSuccessMsg', { count: data.length })
|
||||
this.onSuccess(msg)
|
||||
}).catch(error => {
|
||||
this.catchError(error)
|
||||
}).finally(() => {
|
||||
this.loadStatus = false
|
||||
})
|
||||
},
|
||||
catchError(error) {
|
||||
@@ -149,6 +182,7 @@ export default {
|
||||
window.URL.revokeObjectURL(url)
|
||||
},
|
||||
handleImport(item) {
|
||||
this.loadStatus = true
|
||||
if (this.importOption === '1') {
|
||||
this.performCreate(item)
|
||||
} else {
|
||||
@@ -163,7 +197,8 @@ export default {
|
||||
}
|
||||
const spm = await createSourceIdCache(resources)
|
||||
const baseUrl = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)
|
||||
const url = `${baseUrl}?format=csv&template=update&spm=` + spm.spm
|
||||
const format = this.importTypeOption === 'csv' ? '?format=csv&template=update&spm=' : '?format=xlsx&template=update&spm='
|
||||
const url = `${baseUrl}${format}` + spm.spm
|
||||
return this.downloadCsv(url)
|
||||
},
|
||||
async handleImportConfirm() {
|
||||
@@ -173,7 +208,12 @@ export default {
|
||||
this.showImportDialog = false
|
||||
},
|
||||
beforeUpload(file) {
|
||||
this.isCsv = _.endsWith(file.name, 'csv')
|
||||
this.isCsv = this.importTypeOption === 'csv' ? _.endsWith(file.name, 'csv') : _.endsWith(file.name, 'xlsx')
|
||||
if (!this.isCsv) {
|
||||
this.$message.error(
|
||||
this.$t('common.NeedSpecifiedFile')
|
||||
)
|
||||
}
|
||||
return this.isCsv
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ export default {
|
||||
return v.id
|
||||
})
|
||||
const data = await createSourceIdCache(ids)
|
||||
const url = `${this.tableUrl}?spm=` + data.spm
|
||||
const url = (this.tableUrl.indexOf('?') === -1) ? `${this.tableUrl}?spm=` + data.spm : `${this.tableUrl}&spm=` + data.spm
|
||||
return this.$axios.delete(url)
|
||||
},
|
||||
handleBulkUpdate({ selectedRows }) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ActionsGroup :is-fa="true" :actions="rightSideActions" class="right-side-actions right-side-item" />
|
||||
<ImExportDialog :selected-rows="selectedRows" :url="tableUrl" />
|
||||
<ImExportDialog :selected-rows="selectedRows" :url="tableUrl" v-bind="$attrs" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -54,9 +54,9 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
defaultRightSideActions: [
|
||||
{ name: 'actionExport', fa: 'fa-download', has: this.hasExport, callback: this.handleExport.bind(this) },
|
||||
{ name: 'actionImport', fa: 'fa-upload', has: this.hasImport, callback: this.handleImport.bind(this) },
|
||||
{ name: 'actionRefresh', fa: 'fa-refresh', has: this.hasRefresh, callback: this.handleRefresh }
|
||||
{ name: 'actionExport', fa: 'fa-download', tip: this.$t('common.Export'), has: this.hasExport, callback: this.handleExport.bind(this) },
|
||||
{ name: 'actionImport', fa: 'fa-upload', tip: this.$t('common.Import'), has: this.hasImport, callback: this.handleImport.bind(this) },
|
||||
{ name: 'actionRefresh', fa: 'fa-refresh', tip: this.$t('common.Refresh'), has: this.hasRefresh, callback: this.handleRefresh }
|
||||
],
|
||||
dialogExportVisible: false,
|
||||
exportValue: 2
|
||||
@@ -119,7 +119,7 @@ export default {
|
||||
}
|
||||
|
||||
.right-side-actions >>> .el-button:hover {
|
||||
background-color: rgb(0, 0, 0, 0.05);
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.action-search >>> .el-input__suffix i {
|
||||
|
||||
@@ -24,6 +24,19 @@ const defaultUpdateCallback = function({ row, col }) {
|
||||
this.$router.push(route)
|
||||
}
|
||||
|
||||
const defaultCloneCallback = function({ row, col }) {
|
||||
const id = row.id
|
||||
let route = { query: { clone_from: id }}
|
||||
const cloneRoute = this.colActions.cloneRoute
|
||||
|
||||
if (typeof cloneRoute === 'object') {
|
||||
route = Object.assign(route, cloneRoute)
|
||||
} else {
|
||||
route.name = cloneRoute
|
||||
}
|
||||
this.$router.push(route)
|
||||
}
|
||||
|
||||
const defaultDeleteCallback = function({ row, col, cellValue, reload }) {
|
||||
let msg = this.$t('common.deleteWarningMsg')
|
||||
const name = row.name || row.hostname
|
||||
@@ -71,10 +84,14 @@ export default {
|
||||
canUpdate: true, // can set function(row, value)
|
||||
hasDelete: true, // can set function(row, value)
|
||||
canDelete: true,
|
||||
hasClone: false,
|
||||
canClone: true,
|
||||
updateRoute: this.$route.name.replace('List', 'Update'),
|
||||
cloneRoute: this.$route.name.replace('List', 'Create'),
|
||||
performDelete: defaultPerformDelete,
|
||||
onUpdate: defaultUpdateCallback,
|
||||
onDelete: defaultDeleteCallback,
|
||||
onClone: defaultCloneCallback,
|
||||
extraActions: [] // format see defaultActions
|
||||
}
|
||||
}
|
||||
@@ -89,7 +106,8 @@ export default {
|
||||
type: 'primary',
|
||||
has: colActions.hasUpdate,
|
||||
can: colActions.canUpdate,
|
||||
callback: colActions.onUpdate
|
||||
callback: colActions.onUpdate,
|
||||
order: 10
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
@@ -97,7 +115,17 @@ export default {
|
||||
type: 'danger',
|
||||
has: colActions.hasDelete,
|
||||
can: colActions.canDelete,
|
||||
callback: colActions.onDelete
|
||||
callback: colActions.onDelete,
|
||||
order: 20
|
||||
},
|
||||
{
|
||||
name: 'clone',
|
||||
title: this.$t('common.Clone'),
|
||||
type: 'info',
|
||||
has: colActions.hasClone,
|
||||
can: colActions.canClone,
|
||||
callback: colActions.onClone,
|
||||
order: 30
|
||||
}
|
||||
]
|
||||
return {
|
||||
@@ -115,9 +143,11 @@ export default {
|
||||
v.has = this.cleanBoolean(v, 'has')
|
||||
v.can = this.cleanBoolean(v, 'can')
|
||||
v.callback = this.cleanCallback(v)
|
||||
v.order = v.order || 100
|
||||
return v
|
||||
})
|
||||
actions = actions.filter((v) => v.has)
|
||||
actions.sort((a, b) => a.order - b.order)
|
||||
return actions
|
||||
},
|
||||
actions() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip v-if="formatterArgs.hasTips" :content="tips" placement="bottom" effect="dark">
|
||||
<el-tooltip v-if="formatterArgs.hasTips" placement="bottom" effect="dark">
|
||||
<div slot="content">{{ tipStatus }}<br>{{ tipTime }}</div>
|
||||
<i :class="'fa ' + iconClass" />
|
||||
</el-tooltip>
|
||||
<i v-else :class="'fa ' + iconClass" />
|
||||
@@ -9,6 +10,7 @@
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
export default {
|
||||
name: 'ChoicesFormatter',
|
||||
extends: BaseFormatter,
|
||||
@@ -25,8 +27,14 @@ export default {
|
||||
return !!val
|
||||
},
|
||||
hasTips: false,
|
||||
tips(val) {
|
||||
return val.datetime
|
||||
tipStatus(val, vm) {
|
||||
if (val.status === 0) {
|
||||
return vm.$t('assets.Unreachable')
|
||||
} else if (val.status === 1) {
|
||||
return vm.$t('assets.Reachable')
|
||||
} else if (val.status === 2) {
|
||||
return vm.$t('assets.Unknown')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,8 +50,12 @@ export default {
|
||||
const key = this.formatterArgs.typeChange(this.cellValue)
|
||||
return this.formatterArgs.iconChoices[key]
|
||||
},
|
||||
tips() {
|
||||
return this.formatterArgs.tips(this.cellValue)
|
||||
tipStatus() {
|
||||
const vm = this
|
||||
return this.formatterArgs.tipStatus(this.cellValue, vm)
|
||||
},
|
||||
tipTime() {
|
||||
return toSafeLocalDateStr(this.cellValue.datetime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log(this.col)
|
||||
// console.log(this.col)
|
||||
},
|
||||
methods: {
|
||||
checkBool(item, attr, defaults) {
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
export default {
|
||||
name: 'DateFormatter',
|
||||
extends: BaseFormatter,
|
||||
data() {
|
||||
let value
|
||||
if (this.cellValue) {
|
||||
const dt = new Date(this.cellValue)
|
||||
value = this.$d(dt, 'medium')
|
||||
value = toSafeLocalDateStr(this.cellValue)
|
||||
} else {
|
||||
value = ''
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@ export default {
|
||||
}
|
||||
},
|
||||
iCanDelete() {
|
||||
if (this.col.objects === 'all') {
|
||||
return false
|
||||
}
|
||||
return this.col.objects.indexOf(this.cellValue) === -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<TableAction :table-url="iTableConfig.url" :search-table="search" :date-pick="handleDateChange" v-bind="headerActions" :selected-rows="selectedRows" :reload-table="reloadTable" />
|
||||
<IBox class="table-content">
|
||||
<AutoDataTable ref="dataTable" :config="iTableConfig" @selection-change="handleSelectionChange" v-on="$listeners" />
|
||||
<AutoDataTable ref="dataTable" :filter-table="filter" :config="iTableConfig" @selection-change="handleSelectionChange" v-on="$listeners" />
|
||||
</IBox>
|
||||
</div>
|
||||
</template>
|
||||
@@ -44,8 +44,31 @@ export default {
|
||||
dataTable() {
|
||||
return this.$refs.dataTable.$refs.dataTable
|
||||
},
|
||||
hasCreateAction() {
|
||||
const hasLeftAction = this.headerActions.hasLeftActions
|
||||
if (hasLeftAction === false) {
|
||||
return false
|
||||
}
|
||||
const hasCreate = this.headerActions.hasCreate
|
||||
if (hasCreate === false) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
hasCloneAction() {
|
||||
const hasClone = _.get(this.tableConfig, 'columnsMeta.actions.formatterArgs.hasClone', null)
|
||||
if (hasClone) {
|
||||
return true
|
||||
}
|
||||
if (this.hasCreateAction && hasClone == null) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
iTableConfig() {
|
||||
const config = deepmerge(this.tableConfig, { extraQuery: this.extraQuery })
|
||||
this.$log.debug('Header actions', this.headerActions)
|
||||
_.set(config, 'columnsMeta.actions.formatterArgs.hasClone', this.hasCloneAction)
|
||||
this.$log.debug('ListTable: iTableConfig change', config)
|
||||
return config
|
||||
}
|
||||
@@ -58,8 +81,6 @@ export default {
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
handleSelectionChange(val) {
|
||||
this.selectedRows = val
|
||||
@@ -68,9 +89,10 @@ export default {
|
||||
this.dataTable.getList()
|
||||
},
|
||||
search(attrs) {
|
||||
if (Object.keys(attrs).length > 0) {
|
||||
return this.dataTable.search(attrs, true)
|
||||
}
|
||||
return this.dataTable.search(attrs, true)
|
||||
},
|
||||
filter(attrs) {
|
||||
this.$refs.dataTable.$refs.dataTable.search(attrs, true)
|
||||
},
|
||||
handleDateChange(attrs) {
|
||||
this.$set(this.extraQuery, 'date_from', attrs[0].toISOString())
|
||||
@@ -94,28 +116,28 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.table-content {
|
||||
margin-top: 10px;
|
||||
.table-content {
|
||||
margin-top: 10px;
|
||||
|
||||
& >>> .el-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
& >>> .el-table__header thead > tr > th {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/*& >>> .el-table--striped .el-table__body tr.el-table__row--striped td {*/
|
||||
/*background: white;*/
|
||||
/*}*/
|
||||
|
||||
/*& >>> .el-table th, .el-table tr {*/
|
||||
/*background-color: red;*/
|
||||
/*!*background-color: #FAFAFA;*!*/
|
||||
/*}*/
|
||||
& >>> .el-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
& >>> .el-table__header thead > tr > th {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
//修改颜色
|
||||
// .el-button--text{
|
||||
// color: #409EFF;
|
||||
// }
|
||||
/*& >>> .el-table--striped .el-table__body tr.el-table__row--striped td {*/
|
||||
/*background: white;*/
|
||||
/*}*/
|
||||
|
||||
/*& >>> .el-table th, .el-table tr {*/
|
||||
/*background-color: red;*/
|
||||
/*!*background-color: #FAFAFA;*!*/
|
||||
/*}*/
|
||||
}
|
||||
|
||||
//修改颜色
|
||||
// .el-button--text{
|
||||
// color: #409EFF;
|
||||
// }
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<IBox fa="fa-edit" :title="title" v-bind="$attrs">
|
||||
<div class="quick-actions">
|
||||
<div v-for="action of actions" :key="action.title" class="quick-actions">
|
||||
<table>
|
||||
<ActionItem v-for="action of actions" :key="action.title" :action="action" />
|
||||
<ActionItem v-if="action.has === undefined || action.has" :action="action" />
|
||||
</table>
|
||||
</div>
|
||||
</IBox>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<Select2 ref="select2" v-model="select2.value" v-bind="select2" />
|
||||
</td>
|
||||
</tr>
|
||||
<slot />
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<el-button :type="type" size="small" :loading="submitLoading" @click="addObjects">{{ $t('common.Add') }}</el-button>
|
||||
@@ -21,7 +22,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<tr v-if="params.hasMore" class="item">
|
||||
<tr v-if="params.hasMore && showHasMore" class="item">
|
||||
<td colspan="2">
|
||||
<el-button :type="type" size="small" style="width: 100%" @click="loadMore">
|
||||
<i class="fa fa-arrow-down" />
|
||||
@@ -37,7 +38,6 @@
|
||||
import Select2 from '../Select2'
|
||||
import IBox from '../IBox'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
|
||||
export default {
|
||||
name: 'RelationCard',
|
||||
components: {
|
||||
@@ -83,6 +83,10 @@ export default {
|
||||
type: [Array, Number, String],
|
||||
default: () => []
|
||||
},
|
||||
showHasMore: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
performDelete: {
|
||||
type: Function,
|
||||
default: (obj, that) => {}
|
||||
@@ -220,7 +224,7 @@ export default {
|
||||
)
|
||||
},
|
||||
addObjects() {
|
||||
const objects = this.$refs.select2.getOptionsByValues(this.select2.value)
|
||||
const objects = this.$refs.select2.$refs.select.selected.map(item => ({ label: item.label, value: item.value }))
|
||||
if (objects.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
:remote-method="filterOptions"
|
||||
:multiple="multiple"
|
||||
filterable
|
||||
:clearable="clearable"
|
||||
popper-append-to-body
|
||||
class="select2"
|
||||
v-bind="$attrs"
|
||||
@@ -69,6 +70,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 初始化值,也就是选中的值
|
||||
value: {
|
||||
type: [Array, String, Number, Boolean],
|
||||
@@ -172,6 +177,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async loadMore(load) {
|
||||
if (!this.iAjax.url) {
|
||||
return
|
||||
}
|
||||
if (!this.params.hasMore) {
|
||||
return
|
||||
}
|
||||
@@ -240,7 +248,6 @@ export default {
|
||||
this.params.spm = data.spm
|
||||
await this.getInitialOptions()
|
||||
}
|
||||
this.$log.debug('Start get select2 options')
|
||||
await this.getOptions()
|
||||
if (this.iOptions.length === 0) {
|
||||
this.remote = false
|
||||
|
||||
@@ -1,14 +1,35 @@
|
||||
<template>
|
||||
|
||||
<div class="filter-field">
|
||||
<el-cascader ref="Cascade" :options="options" :props="config" @change="handleMenuItemChange" />
|
||||
<el-tag v-for="(v, k) in filterTags" :key="k" :name="k" closable size="small" class="filter-tag" type="info" @close="handleTagClose(k)">
|
||||
<el-tag
|
||||
v-for="(v, k) in filterTags"
|
||||
:key="k"
|
||||
:name="k"
|
||||
closable
|
||||
size="small"
|
||||
class="filter-tag"
|
||||
type="info"
|
||||
:disable-transitions="true"
|
||||
@close="handleTagClose(k)"
|
||||
@click="handleTagClick(v,k)"
|
||||
>
|
||||
<strong v-if="v.label">{{ v.label + ':' }}</strong>
|
||||
<span v-if="v.valueLabel">{{ v.valueLabel }}</span>
|
||||
<span v-else>{{ v.value }}</span>
|
||||
</el-tag>
|
||||
<span v-if="keyLabel" slot="prefix" class="filterTitle">{{ keyLabel + ':' }}</span>
|
||||
<el-input ref="SearchInput" v-model="filterValue" :placeholder="placeholder" class="search-input" @blur="focus = false" @focus="focus = true" @change="handleConfirm" />
|
||||
<el-input
|
||||
ref="SearchInput"
|
||||
v-model="filterValue"
|
||||
:placeholder="placeholder"
|
||||
class="search-input"
|
||||
@blur="focus = false"
|
||||
@focus="focus = true"
|
||||
@change="handleConfirm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -65,16 +86,21 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
filterTags: {
|
||||
handler(val) {
|
||||
this.$nextTick(() => this.$emit('tagSearch', this.filterMaps))
|
||||
// this.$emit('tagSearch', this.filterMaps)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
// filterTags: {
|
||||
// handler(val) {
|
||||
// this.$nextTick(() => this.$emit('tagSearch', this.filterMaps))
|
||||
// // this.$emit('tagSearch', this.filterMaps)
|
||||
// },
|
||||
// deep: true
|
||||
// }
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(() => this.$emit('tagSearch', this.filterMaps), 400)
|
||||
setTimeout(() => {
|
||||
if (Object.keys(this.filterMaps).length > 0) {
|
||||
return this.$emit('tagSearch', this.filterMaps)
|
||||
}
|
||||
}
|
||||
, 400)
|
||||
// this.$nextTick(() => this.$emit('tagSearch', this.filterMaps))
|
||||
},
|
||||
methods: {
|
||||
@@ -95,7 +121,6 @@ export default {
|
||||
return ''
|
||||
},
|
||||
handleMenuItemChange(keys) {
|
||||
this.$log.debug('Tag search keys: ', keys)
|
||||
if (keys.length === 0) {
|
||||
return
|
||||
}
|
||||
@@ -112,6 +137,7 @@ export default {
|
||||
},
|
||||
handleTagClose(evt) {
|
||||
this.$delete(this.filterTags, evt)
|
||||
this.$emit('tagSearch', this.filterMaps)
|
||||
return true
|
||||
},
|
||||
handleConfirm() {
|
||||
@@ -120,9 +146,33 @@ export default {
|
||||
}
|
||||
const tag = { key: this.filterKey, label: this.keyLabel, value: this.filterValue, valueLabel: this.valueLabel }
|
||||
this.$set(this.filterTags, this.filterKey, tag)
|
||||
this.$emit('tagSearch', this.filterMaps)
|
||||
this.filterKey = ''
|
||||
this.filterValue = ''
|
||||
this.valueLabel = ''
|
||||
},
|
||||
handleTagClick(v, k) {
|
||||
let unableChange = false
|
||||
for (const field of this.options) {
|
||||
if (field.value === v.key) {
|
||||
if (field.type === 'choice') {
|
||||
unableChange = true
|
||||
}
|
||||
if (field.type === 'boolean') {
|
||||
unableChange = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unableChange) {
|
||||
return
|
||||
}
|
||||
if (this.filterValue.length !== 0) {
|
||||
this.handleConfirm()
|
||||
}
|
||||
this.$delete(this.filterTags, k)
|
||||
this.filterKey = v.key
|
||||
this.filterValue = v.value
|
||||
this.$refs.SearchInput.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<el-collapse-transition>
|
||||
<div style="display: flex;justify-items: center; flex-wrap: nowrap;justify-content:space-between;">
|
||||
<div v-show="iShowTree" :style="iShowTree?('width:250px;'):('width:0;')" class="transition-box">
|
||||
<div v-show="iShowTree" :style="iShowTree?('width:20%;'):('width:0;')" class="transition-box">
|
||||
<component
|
||||
:is="component"
|
||||
ref="AutoDataZTree"
|
||||
@@ -14,7 +14,7 @@
|
||||
</div>
|
||||
</component>
|
||||
</div>
|
||||
<div :style="iShowTree?('display: flex;width: calc(100% - 250px);'):('display: flex;width:100%;')">
|
||||
<div :style="iShowTree?('display: flex;width: calc(100% - 20%);'):('display: flex;width:100%;')">
|
||||
<div class="mini">
|
||||
<div style="display:block" class="mini-button" @click="iShowTree=!iShowTree">
|
||||
<i v-show="iShowTree" class="fa fa-angle-left fa-x" /><i v-show="!iShowTree" class="fa fa-angle-right fa-x" />
|
||||
@@ -98,6 +98,7 @@ export default {
|
||||
color: #FFFFFF;
|
||||
border-radius: 3px;
|
||||
line-height: 1.428;
|
||||
cursor:pointer;
|
||||
}
|
||||
.el-tree{
|
||||
background-color: inherit !important;
|
||||
|
||||
@@ -2,6 +2,22 @@
|
||||
"": "",
|
||||
"applications": {
|
||||
"": "",
|
||||
"applicationsType": {
|
||||
"chrome": "Chrome",
|
||||
"mysql_workbench": "MySQL Workbench",
|
||||
"vmware_client":"Vmware Client",
|
||||
"custom":"Custom",
|
||||
"mysql": "MySQL",
|
||||
"oracle": "Oracle",
|
||||
"postgresql": "PostgreSQL",
|
||||
"mariadb": "MariaDB",
|
||||
"k8s": "Kubernetes"
|
||||
},
|
||||
"applicationsCategory": {
|
||||
"remote_app": "远程应用",
|
||||
"db": "数据库应用",
|
||||
"cloud": "云应用"
|
||||
},
|
||||
"appPath": "应用路径",
|
||||
"appType": "应用类型",
|
||||
"asset": "资产",
|
||||
@@ -16,6 +32,7 @@
|
||||
"chrome_password": "登录密码",
|
||||
"mysql_workbench": "MySQL Workbench",
|
||||
"mysql_workbench_ip": "数据库IP",
|
||||
"mysql_workbench_port": "数据库端口",
|
||||
"mysql_workbench_name": "数据库名",
|
||||
"mysql_workbench_username": "数据库账号",
|
||||
"mysql_workbench_password": "数据库密码",
|
||||
@@ -28,7 +45,11 @@
|
||||
"custom_target": "目标地址",
|
||||
"custom_username": "登录账号",
|
||||
"custom_password": "登录密码",
|
||||
"Custom": "自定义"
|
||||
"Custom": "自定义",
|
||||
"cluster": "集群",
|
||||
"kubernetes":"Kubernetes",
|
||||
"clusterHelpTextMessage": "例如:https://172.16.8.8:8443",
|
||||
"DBInfo": "数据库信息"
|
||||
},
|
||||
"assets": {
|
||||
"Action": "动作",
|
||||
@@ -40,10 +61,14 @@
|
||||
"AssetDetail": "资产详情",
|
||||
"AssetList": "资产列表",
|
||||
"AssetListHelpMessage": "左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的,右侧是属于该节点下的资产\n",
|
||||
"TestGatewayTestConnection":"测试连接网关",
|
||||
"TestGatewayHelpMessage": "如果使用了nat端口映射,请设置为ssh真实监听的端口",
|
||||
"SshPort": "SSH 端口",
|
||||
"AssetNumber": "资产编号",
|
||||
"AssetUserList": "资产用户列表",
|
||||
"Assets": "资产",
|
||||
"Auth": "认证",
|
||||
"AccountList": "账号列表",
|
||||
"AutoGenerateKey": "自动生成密钥",
|
||||
"AutoPush": "自动推送",
|
||||
"BasePlatform": "基础平台",
|
||||
@@ -63,6 +88,8 @@
|
||||
"DateUpdated": "更新日期",
|
||||
"DeactiveSelected": "禁用所选",
|
||||
"Disk": "硬盘",
|
||||
"AdDomain": "AD域名",
|
||||
"AdDomainHelpText": "提供给域用户登录的AD域名",
|
||||
"Domain": "网域",
|
||||
"DomainDetail": "网域详情",
|
||||
"DomainHelpMessage": "网域功能是为了解决部分环境(如:混合云)无法直接连接而新增的功能,原理是通过网关服务器进行跳转登录。JMS => 网域网关 => 目标资产",
|
||||
@@ -87,6 +114,7 @@
|
||||
"OnlyLatestVersion": "仅最新版本",
|
||||
"Os": "操作系统",
|
||||
"Other": "其它",
|
||||
"Hardware": "硬件信息",
|
||||
"Password": "密码",
|
||||
"PasswordWithoutSpecialCharHelpText": "不能包含特殊字符",
|
||||
"Pending": "等待",
|
||||
@@ -100,10 +128,13 @@
|
||||
"PushSystemUserNow": "推送系统用户",
|
||||
"QuickUpdate": "快速更新",
|
||||
"Reachable": "可连接",
|
||||
"Unreachable": "不可连接",
|
||||
"Unknown": "未知",
|
||||
"Refresh": "刷新",
|
||||
"RefreshHardware": "更新硬件信息",
|
||||
"RemoteAppListHelpMessage": "使用此功能前,请确保已将应用加载器上传到应用服务器并成功发布为一个 RemoteApp 应用 <b><a href='https://github.com/jumpserver/Jmservisor/releases'>下载应用加载器</a></b>",
|
||||
"RemoteApps": "远程应用",
|
||||
"Applications": "应用",
|
||||
"RemoteType": "应用类型",
|
||||
"RemoveFromCurrentNode": "从节点移除",
|
||||
"ReplaceNodeAssetsAdminUserWithThis": "替换资产的管理员",
|
||||
@@ -111,6 +142,7 @@
|
||||
"SFTPHelpMessage": "SFTP的起始路径,tmp目录, 用户home目录或者自定义",
|
||||
"SerialNumber": "序列号",
|
||||
"SudoHelpMessage": "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig",
|
||||
"PasswordHelpMessage": "密码或密钥密码",
|
||||
"SystemUser": "系统用户",
|
||||
"SystemUserDetail": "系统用户详情",
|
||||
"SystemUserListHelpMessage": "系统用户是 JumpServer 跳转登录资产时使用的用户,可以理解为登录资产用户,如 web,sa,dba(`ssh web@some-host`),而不是使用某个用户的用户名跳转登录服务器(`ssh xiaoming@some-host`); 简单来说是用户使用自己的用户名登录 JumpServer,JumpServer 使用系统用户登录资产。 系统用户创建时,如果选择了自动推送,JumpServer 会使用 Ansible 自动推送系统用户到资产中,如果资产(交换机)不支持 Ansible,请手动填写账号密码。\n",
|
||||
@@ -120,6 +152,7 @@
|
||||
"TestConnection": "测试连接",
|
||||
"Type": "类型",
|
||||
"UnselectedAssets": "未选择资产",
|
||||
"UnselectedNodes": "未选择节点",
|
||||
"UpdateAssetUserToken": "更新资产用户认证信息",
|
||||
"Username": "用户名",
|
||||
"UsernameHelpMessage": "用户名是动态的,登录资产时使用当前用户的用户名登录",
|
||||
@@ -129,7 +162,13 @@
|
||||
"command_filter_list": "命令过滤器列表",
|
||||
"date_joined": "创建日期",
|
||||
"ip": "IP",
|
||||
"sshkey": "sshkey"
|
||||
"sshkey": "sshkey",
|
||||
"GroupsHelpMessage": "请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)",
|
||||
"HomeHelpMessage": "默认家目录 /home/系统用户名: /home/username",
|
||||
"Home": "家目录",
|
||||
"LinuxUserAffiliateGroup": "用户附属组",
|
||||
"ipDomain": "IP(域名)"
|
||||
|
||||
},
|
||||
"audits": {
|
||||
"Hosts": "主机",
|
||||
@@ -143,14 +182,23 @@
|
||||
},
|
||||
"common": {
|
||||
"Action": "动作",
|
||||
"RequestTickets": "申请工单",
|
||||
"Actions": "操作",
|
||||
"Activate": "激活",
|
||||
"NeedSpecifiedFile": "需上传指定格式文件",
|
||||
"TestPortErrorMsg":"端口错误,请重新输入",
|
||||
"Active": "激活中",
|
||||
"actionsTips":"剪切板权限控制目前仅支持 RDP/VNC 协议的连接",
|
||||
"Add": "添加",
|
||||
"UpdateAssetDetail": "配置更多信息",
|
||||
"AddSuccessMsg": "添加成功",
|
||||
"Auth": "认证",
|
||||
"PushSelected":"推送所选",
|
||||
"BadRequestErrorMsg": "请求错误,请检查填写内容",
|
||||
"BadRoleErrorMsg": "请求错误,无该操作权限",
|
||||
"BadConflictErrorMsg": "正在刷新中,请稍后再试",
|
||||
"Basic": "基本",
|
||||
"PleaseAgreeToTheTerms": "请同意条款",
|
||||
"BasicInfo": "基本信息",
|
||||
"Cancel": "取消",
|
||||
"Close": "关闭",
|
||||
@@ -161,6 +209,7 @@
|
||||
"CreatedBy": "创建者",
|
||||
"CrontabHelpTips": "eg:每周日 03:05 执行 <5 3 * * 0> <br> 提示: 使用5位 Linux crontab 表达式 <分 时 日 月 星期> (<a href='https://tool.lu/crontab/' target='_blank'>在线工具</a>) <br>注意: 如果同时设置了定期执行和周期执行,优先使用定期执行",
|
||||
"DateEnd": "结束日期",
|
||||
"Resource": "资源",
|
||||
"DateLast24Hours": "最近一天",
|
||||
"DateLast3Months": "最近三月",
|
||||
"DateLastMonth": "最近一月",
|
||||
@@ -174,11 +223,13 @@
|
||||
"EnterForSearch": "按回车进行搜索",
|
||||
"Export": "导出",
|
||||
"Import": "导入",
|
||||
"Refresh": "刷新",
|
||||
"Info": "提示",
|
||||
"MFAConfirm": "MFA 认证",
|
||||
"MFARequireForSecurity": "为了安全请输入MFA",
|
||||
"Members": "成员",
|
||||
"More": "更多",
|
||||
"Message": "消息",
|
||||
"MoreActions": "更多操作",
|
||||
"Name": "名称",
|
||||
"No": "否",
|
||||
@@ -196,11 +247,13 @@
|
||||
"SelectFile": "选择文件",
|
||||
"Show": "显示",
|
||||
"Submit": "提交",
|
||||
"SaveAndAddAnother": "保存并继续添加",
|
||||
"Test": "测试",
|
||||
"TestSuccessMsg": "测试成功",
|
||||
"To": "至",
|
||||
"Update": "更新",
|
||||
"Upload": "上传",
|
||||
"Clone": "克隆",
|
||||
"Username": "用户名",
|
||||
"Validity": "有效",
|
||||
"Invalidity": "无效",
|
||||
@@ -211,13 +264,17 @@
|
||||
"bulkDeleteErrorMsg": "批量删除失败: ",
|
||||
"bulkDeleteSuccessMsg": "批量删除成功",
|
||||
"bulkRemoveErrorMsg": "批量移除失败: ",
|
||||
"NeedAssetsAndSystemUserErrMsg": "请先选择授权的系统用户和资产",
|
||||
"bulkRemoveSuccessMsg": "批量移除成功",
|
||||
"createBy": "创建者",
|
||||
"cloneFrom": "克隆自",
|
||||
"createErrorMsg": "创建失败",
|
||||
"createSuccessMsg": "创建成功",
|
||||
"saveSuccessContinueMsg": "创建成功,更新内容后可以继续添加",
|
||||
"createdBy": "创建人",
|
||||
"dateCreated": "创建日期",
|
||||
"dateExpired": "失效日期",
|
||||
"dateFinished": "完成日期",
|
||||
"dateStart": "开始日期",
|
||||
"deleteErrorMsg": "删除失败",
|
||||
"deleteFailedMsg": "删除失败",
|
||||
@@ -239,6 +296,7 @@
|
||||
"onlyCSVFilesTips": "仅支持csv文件导入",
|
||||
"updateSuccessMsg": "导入更新成功,总共:{count}"
|
||||
},
|
||||
"fileType": "文件类型",
|
||||
"isValid": "有效",
|
||||
"nav": {
|
||||
"APIKey": "API Key",
|
||||
@@ -353,7 +411,7 @@
|
||||
},
|
||||
"perms": {
|
||||
"": "",
|
||||
"Actions": "动作",
|
||||
"Actions": "权限",
|
||||
"Asset": "资产",
|
||||
"Basic": "基本",
|
||||
"Exclude": "不包含",
|
||||
@@ -364,10 +422,15 @@
|
||||
"SystemUser": "系统用户",
|
||||
"User": "用户",
|
||||
"UserGroups": "用户组",
|
||||
"DatabaseAppPermission": "数据库授权",
|
||||
"RemoteAppPermission": "远程应用授权",
|
||||
"addApplicationToThisPermission": "添加应用",
|
||||
"KubernetesAppPermission": "Kubernetes授权",
|
||||
"addAssetToThisPermission": "添加资产",
|
||||
"addDatabaseAppToThisPermission": "添加数据库应用",
|
||||
"addNodeToThisPermission": "添加节点",
|
||||
"addRemoteAppToThisPermission": "添加远程应用",
|
||||
"addK8sAppToThisPermission": "添加Kubernetes应用",
|
||||
"addSystemUserToThisPermission": "添加系统用户",
|
||||
"addUserGroupToThisPermission": "添加用户组",
|
||||
"addUserToThisPermission": "添加用户",
|
||||
@@ -376,6 +439,7 @@
|
||||
"assetCount": "资产数量",
|
||||
"connect": "连接",
|
||||
"databaseApp": "数据库应用",
|
||||
"KubernetesApp": "Kubernetes",
|
||||
"dateStart": "开始日期",
|
||||
"downloadFile": "下载文件",
|
||||
"hostName": "主机名",
|
||||
@@ -386,15 +450,23 @@
|
||||
"refreshSuccess": "刷新成功",
|
||||
"remoteApp": "远程应用",
|
||||
"remoteAppCount": "远程应用数量",
|
||||
"appsCount": "应用数量",
|
||||
"appsList":"应用列表",
|
||||
"DatabaseAppCount": "数据库应用数量",
|
||||
"KubernetesAppCount": "Kubernetes应用数量",
|
||||
"systemUserCount": "系统用户数量",
|
||||
"upDownload": "上传下载",
|
||||
"uploadFile": "上传文件",
|
||||
"clipboardCopyPaste":"复制粘贴",
|
||||
"clipboardCopy":"剪切板复制",
|
||||
"clipboardPaste":"剪切板粘贴",
|
||||
"userCount": "用户数量",
|
||||
"userGroupCount": "用户组数量",
|
||||
"usersAndUserGroups": "用户或用户组"
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"Ticket":"工单",
|
||||
"AdminUserCreate": "创建管理用户",
|
||||
"AdminUserDetail": "管理用户详情",
|
||||
"AdminUserList": "管理用户",
|
||||
@@ -425,7 +497,7 @@
|
||||
"CreateCommandStorage": "创建命令存储",
|
||||
"CreateReplayStorage": "创建录像存储",
|
||||
"Dashboard": "仪表盘",
|
||||
"DatabaseApp": "数据库应用",
|
||||
"DatabaseApp": "数据库",
|
||||
"DatabaseAppCreate": "创建数据库应用",
|
||||
"DatabaseAppDetail": "数据库详情",
|
||||
"DatabaseAppPermission": "数据库授权",
|
||||
@@ -433,6 +505,15 @@
|
||||
"DatabaseAppPermissionDetail": "数据库授权详情",
|
||||
"DatabaseAppPermissionUpdate": "更新数据库授权规则",
|
||||
"DatabaseAppUpdate": "数据库应用更新",
|
||||
"KubernetesApp": "Kubernetes",
|
||||
"KubernetesAppCreate": "创建Kubernetes",
|
||||
"KubernetesAppDetail": "Kubernetes详情",
|
||||
"KubernetesAppPermission": "Kubernetes授权",
|
||||
"KubernetesAppPermissionCreate": "创建Kubernetes授权规则",
|
||||
"KubernetesAppPermissionDetail": "Kubernetes授权详情",
|
||||
"KubernetesAppPermissionUpdate": "更新Kubernetes授权规则",
|
||||
"KubernetesAppUpdate": "更新Kubernetes",
|
||||
|
||||
"DomainCreate": "创建网域",
|
||||
"DomainDetail": "网域详情",
|
||||
"DomainList": "网域列表",
|
||||
@@ -462,6 +543,11 @@
|
||||
"RemoteAppPermissionCreate": "创建远程应用授权规则",
|
||||
"RemoteAppPermissionDetail": "远程应用授权详情",
|
||||
"RemoteAppPermissionUpdate": "更新远程应用授权规则",
|
||||
"ApplicationDetail": "应用详情",
|
||||
"ApplicationPermission": "应用授权",
|
||||
"ApplicationPermissionCreate": "创建应用授权规则",
|
||||
"ApplicationPermissionDetail": "应用授权详情",
|
||||
"ApplicationPermissionUpdate": "更新应用授权规则",
|
||||
"RemoteAppUpdate": "更新远程应用",
|
||||
"ReplayStorageUpdate": "更新录像存储",
|
||||
"SessionDetail": "会话详情",
|
||||
@@ -478,6 +564,7 @@
|
||||
"TaskMonitor": "任务监控",
|
||||
"Terminal": "终端管理",
|
||||
"TicketDetail": "工单详情",
|
||||
"TicketCreate": "创建工单",
|
||||
"Tickets": "工单管理",
|
||||
"UserCreate": "创建用户",
|
||||
"UserDetail": "用户详情",
|
||||
@@ -501,6 +588,7 @@
|
||||
"active": "激活中",
|
||||
"alive": "在线",
|
||||
"asset": "资产",
|
||||
"target": "目标",
|
||||
"bucket": "桶名称",
|
||||
"command": "命令",
|
||||
"commandStorage": "命令存储",
|
||||
@@ -528,6 +616,10 @@
|
||||
"name": "名称",
|
||||
"protocol": "协议",
|
||||
"region": "地域",
|
||||
"sessionActiveCount": "在线会话数量",
|
||||
"systemCpuLoad": "CPU负载",
|
||||
"systemDiskUsedPercent": "硬盘使用率",
|
||||
"systemMemoryUsedPercent": "内存使用率",
|
||||
"remoteAddr": "远端地址",
|
||||
"replay": "回放",
|
||||
"replaySession": "回放会话",
|
||||
@@ -539,7 +631,9 @@
|
||||
"systemUser": "系统用户",
|
||||
"terminalDetail": "终端详情",
|
||||
"terminalUpdate": "更新终端",
|
||||
"terminalUpdateStorage": "更新终端存储",
|
||||
"terminate": "终断",
|
||||
"sessionTerminate": "会话终断",
|
||||
"test": "测试",
|
||||
"type": "类型",
|
||||
"user": "用户",
|
||||
@@ -547,7 +641,15 @@
|
||||
"common": "普通"
|
||||
},
|
||||
"Monitor": "监控",
|
||||
"TerminateTaskSendSuccessMsg": "终断任务已下发,请稍后刷新表格查看"
|
||||
"sessionMonitor": "监控",
|
||||
"TerminateTaskSendSuccessMsg": "终断任务已下发,请稍后刷新查看",
|
||||
"helpText": {
|
||||
"esUrl": "提示:如果有多台主机,请使用逗号 ( , ) 进行分割。(eg: http://www.jumpserver.a.com,http://www.jumpserver.b.com)",
|
||||
"esIndex": "es提供默认index:jumpserver",
|
||||
"esDocType": "es默认文档类型:command",
|
||||
"s3Endpoint": "S3 格式: http://s3.{REGION_NAME}.amazonaws.com<br>S3(China) 格式: http://s3.{REGION_NAME}.amazonaws.com.cn<br>如: http://s3.cn-north-1.amazonaws.com.cn",
|
||||
"ossEndpoint": "OSS 格式: http://{REGION_NAME}.aliyuncs.com<br>如: http://oss-cn-hangzhou.aliyuncs.com"
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"ApiKeyList": "API Key 列表",
|
||||
@@ -578,6 +680,7 @@
|
||||
"authLdapSearchOu": "用户OU",
|
||||
"authLdapServerUri": "LDAP地址",
|
||||
"authLdapUserAttrMap": "LDAP属性映射",
|
||||
"unselectedUser": "没有选择用户",
|
||||
"auto": "自动",
|
||||
"basicSetting": "基本设置",
|
||||
"communityEdition": "社区版",
|
||||
@@ -597,6 +700,9 @@
|
||||
"emailTest": "测试连接",
|
||||
"emailUserSSL": "使用SSL",
|
||||
"emailUserTLS": "使用TLS",
|
||||
"SecurityInsecureCommand": "危险命令告警",
|
||||
"Insecure_Command_Alert": "危险命令告警",
|
||||
"SecurityInsecureCommandEmailReceiver": "告警接收邮件",
|
||||
"helpText": {
|
||||
"ApiKeyList": "使用api key签名请求头,每个请求的头部是不一样的, 请查阅使用文档",
|
||||
"authLdapSearchFilter": "可能的选项是(cn或uid或sAMAccountName=%(user)s)",
|
||||
@@ -617,7 +723,8 @@
|
||||
"terminalHeartbeatInterval": "单位: 秒",
|
||||
"terminalSessionKeepDuration": "单位:天。 会话、录像、命令记录超过该时长将会被删除(仅影响数据库存储, oss等不受影响)",
|
||||
"terminalTelnetRegex": "登录telnet服务器成功后的提示正则表达式,如: Last\\s*login|success|成功",
|
||||
"userGuideUrl": "用户第一次登录,修改profile后重定向到地址"
|
||||
"userGuideUrl": "用户第一次登录,修改profile后重定向到地址",
|
||||
"SecurityInsecureCommandEmailReceiver": "多个邮箱时,以半角逗号','分隔"
|
||||
},
|
||||
"helpTip": {
|
||||
"emailUserSSL": "如果SMTP端口是465,通常需要启用SSL",
|
||||
@@ -628,7 +735,13 @@
|
||||
"securityPasswordNumber": "开启后,用户密码修改、重置必须包含数字字符",
|
||||
"securityPasswordSpecialChar": "开启后,用户密码修改、重置必须包含特殊字符",
|
||||
"securityPasswordUpperCase": "开启后,用户密码修改、重置必须包含大写字母",
|
||||
"securityServiceAccountRegistration": "允许使用bootstrap token注册终端, 当终端注册成功后可以禁止"
|
||||
"securityServiceAccountRegistration": "允许使用bootstrap token注册终端, 当终端注册成功后可以禁止",
|
||||
"SecurityInsecureCommand": "开启后,当资产上有危险命令执行时,会发送邮件告警通知"
|
||||
},
|
||||
"validatorMessage": {
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo3": "请确保该值大于或者等于 3",
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo5": "请确保该值大于或者等于 5",
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo6": "请确保该值大于或者等于 6"
|
||||
},
|
||||
"import": "导入",
|
||||
"importLdapUserTip": "请先提交LDAP配置再进行导入",
|
||||
@@ -664,19 +777,24 @@
|
||||
"testLdapLoginTitle": "测试LDAP 用户登录",
|
||||
"userGuideUrl": "用户向导URL",
|
||||
"username": "用户名",
|
||||
"usernamePlaceholder": "请输入用户名"
|
||||
"usernamePlaceholder": "请输入用户名",
|
||||
"refreshLdapCache":"刷新Ldap缓存,请稍后",
|
||||
"LicenseExpired": "许可证已经过期",
|
||||
"LicenseWillBe": "许可证即将在 ",
|
||||
"Expire": " 过期"
|
||||
},
|
||||
"settings": {
|
||||
"setting": "设置"
|
||||
},
|
||||
"tickets": {
|
||||
"Accept": "接受",
|
||||
"AssignedMe": "待处理",
|
||||
"Accept": "同意",
|
||||
"AssignedMe": "待我审批",
|
||||
"Assignee": "处理人",
|
||||
"Assignees": "待处理人",
|
||||
"Close": "关闭",
|
||||
"Comment": "备注",
|
||||
"MyTickets": "我的工单",
|
||||
"MyTickets": "我发起的",
|
||||
"RequestPerm":"授权申请",
|
||||
"Reject": "拒绝",
|
||||
"date": "日期",
|
||||
"reply": "回复",
|
||||
@@ -685,7 +803,24 @@
|
||||
"type": "类型",
|
||||
"user": "用户",
|
||||
"Status": "状态",
|
||||
"Open": "打开"
|
||||
"Open": "待处理",
|
||||
"OpenTicket": "创建工单",
|
||||
"HandleTicket": "处理工单",
|
||||
"FinishedTicket": "完成工单",
|
||||
"IP": "IP",
|
||||
"Hostname": "主机名",
|
||||
"Asset": "资产",
|
||||
"SystemUser": "系统用户",
|
||||
"RequestAssetPerm": "申请资产授权",
|
||||
"Applicant": "申请人",
|
||||
"Pending": "待处理",
|
||||
"Approved": "已同意",
|
||||
"Rejected": "已拒绝",
|
||||
"Closed": "已完成",
|
||||
"helpText": {
|
||||
"ips": "请输入逗号分割的IP地址组",
|
||||
"fuzzySearch": "支持模糊搜索"
|
||||
}
|
||||
},
|
||||
"tree": {
|
||||
"AddAssetToNode": "添加资产到节点",
|
||||
@@ -695,6 +830,7 @@
|
||||
"RenameNode": "重命名节点",
|
||||
"ShowAssetAllChildrenNode": "显示所有子节点资产",
|
||||
"ShowAssetOnlyCurrentNode": "仅显示当前节点资产",
|
||||
"CheckAssetsAmount": "校对资产数量",
|
||||
"ShowNodeInfo": "显示节点详情",
|
||||
"TestNodeAssetConnectivity": "测试资产节点可连接性",
|
||||
"UpdateNodeAssetHardwareInfo": "更新节点资产硬件信息"
|
||||
@@ -713,11 +849,18 @@
|
||||
"Email": "邮件",
|
||||
"FingerPrint": "指纹",
|
||||
"FirstLogin": "首次登录",
|
||||
"OrgUser": "组织用户",
|
||||
"OrgAdmin": "组织管理员",
|
||||
"OrgAuditor": "组织审计员",
|
||||
"InviteUser": "邀请用户",
|
||||
"Invite": "邀请",
|
||||
"InviteUserInOrg": "邀请用户加入此组织",
|
||||
"Guide": "向导",
|
||||
"HelpText": {
|
||||
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "启用多因子认证,使账号更加安全。<br/> 启用之后您将会在下次登录时进入多因子认证绑定流程;您也可以在(个人信息->快速修改->更改多因子设置)中直接绑定!",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:设置复杂密码,并启用多因子认证)",
|
||||
"SSHKeyOfProfileSSHUpdatePage": "复制你的公钥到这里"
|
||||
"SSHKeyOfProfileSSHUpdatePage": "复制你的公钥到这里",
|
||||
"OrgRoleHelpText": "组织角色是用户在当前组织中的角色"
|
||||
},
|
||||
"IAgree": "我同意",
|
||||
"ImprovePersonalInformation": "完善个人信息",
|
||||
@@ -725,6 +868,7 @@
|
||||
"LoginConfirm": "登录复核",
|
||||
"LoginPasswordSetting": "登录密码设置",
|
||||
"MFA": "MFA",
|
||||
"Existing":"已存在",
|
||||
"MfaLevel": "多因子认证",
|
||||
"Name": "姓名",
|
||||
"NewPassword": "新密码",
|
||||
@@ -736,6 +880,8 @@
|
||||
"ResetAndDownloadSSHKey": "重置并下载密钥",
|
||||
"ResetPublicKeyAndDownload": "重置并下载SSH密钥",
|
||||
"Role": "角色",
|
||||
"SuperRole": "系统角色",
|
||||
"OrgRole": "组织角色",
|
||||
"SSHKey": "SSH公钥",
|
||||
"SSHKeySetting": "SSH公钥设置",
|
||||
"Secure": "安全",
|
||||
@@ -766,14 +912,21 @@
|
||||
"tabs": {
|
||||
"assetPermissionRules": "资产授权规则",
|
||||
"databasePermissionRules": "数据库授权规则",
|
||||
"k8sPermissionRules": "Kubernetes授权规则",
|
||||
"grantedAssets": "授权的资产",
|
||||
"grantedK8Ss": "授权的Kubernetes",
|
||||
"grantedDatabases": "授权的数据库",
|
||||
"grantedRemoteApps": "授权的远程应用",
|
||||
"grantedApplications": "授权的应用",
|
||||
"ApplicationPermissionRules": "应用授权规则",
|
||||
"remoteAppPermissionRules": "远程应用授权规则"
|
||||
},
|
||||
"dateLastLogin": "最后登录日期",
|
||||
"UpdatePassword": "更新密码",
|
||||
"SetPublicKey": "设置SSH公钥"
|
||||
"SetPublicKey": "设置SSH公钥",
|
||||
"passwordExpired": "密码过期了",
|
||||
"passwordWillExpiredPrefixMsg": "密码即将在 ",
|
||||
"passwordWillExpiredSuffixMsg": "天 后过期,请尽快修改您的密码。"
|
||||
},
|
||||
"xpack": {
|
||||
"Admin": "管理员",
|
||||
@@ -798,6 +951,9 @@
|
||||
"ExecutionDetail": "执行详情",
|
||||
"ExecutionList": "执行列表",
|
||||
"ExecutionTimes": "执行次数",
|
||||
"validatorMessage": {
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo1": "请确保该值大于或者等于 1"
|
||||
},
|
||||
"HelpText": {
|
||||
"CrontabOfCreateUpdatePage": "例如:每周日 03:05 执行 <5 3 * * 0> <br/> 使用5位 Linux crontab 表达式 <分 时 日 月 星期> (<a href=\"https://tool.lu/crontab/\" target=\"_blank\">在线工具</a>) <br/> 如果同时设置了定期执行和周期执行,优先使用定期执行",
|
||||
"IntervalOfCreateUpdatePage": "单位:时",
|
||||
@@ -819,17 +975,54 @@
|
||||
"Username": "用户名"
|
||||
},
|
||||
"Cloud": {
|
||||
"Aliyun": "阿里云",
|
||||
"Qcloud": "腾讯云",
|
||||
"AWS_China": "AWS(中国)",
|
||||
"AWS_Int": "AWS(国际)",
|
||||
"HuaweiCloud": "华为云",
|
||||
"Azure":"Azure(中国)",
|
||||
"HostnameStrategy": "用于生成资产主机名。例如:1. 实例名称 (instanceDemo);2. 实例名称和部分IP(后两位) (instanceDemo-250.1)",
|
||||
"IsAlwaysUpdate": "资产信息保持最新",
|
||||
"AccountCreate": "创建账户",
|
||||
"AccountList": "账户列表",
|
||||
"AccountUpdate": "更新账户",
|
||||
"AccountDetail": "账户详情",
|
||||
"Cloud": "云管中心",
|
||||
"CloudCenter": "云管中心",
|
||||
"CoveredAlwaysHelpTips": "资产信息总是被覆盖",
|
||||
"Provider": "云服务商",
|
||||
"Validity": "有效",
|
||||
"IsAlwaysUpdateHelpTips": "每次执行同步任务时,是否同步更新资产的信息,包括主机名、IP、系统平台、管理用户",
|
||||
"SyncInstanceTaskCreate": "创建同步实例任务",
|
||||
"SyncInstanceTaskList": "同步实例任务列表",
|
||||
"SyncInstanceTaskDetail": "同步实例任务详情",
|
||||
"SyncInstanceTaskUpdate": "更新同步实例任务",
|
||||
"SyncInstanceTaskHistoryList": "同步历史列表",
|
||||
"SyncInstanceTaskHistoryAssetList": "同步实例列表",
|
||||
"CloudSource": "同步源",
|
||||
"SaveSetting": "同步设置"
|
||||
"SaveSetting": "同步设置",
|
||||
"Name": "名称",
|
||||
"Account":"账户",
|
||||
"Node": "节点",
|
||||
"AdminUser":"管理用户",
|
||||
"Periodic":"执行周期",
|
||||
"PeriodicPerform":"定时执行",
|
||||
"RegularlyPerform": "定期执行",
|
||||
"CyclePerform": "周期执行",
|
||||
"DateLastSync":"最后同步日期",
|
||||
"DateCreated":"创建日期",
|
||||
"Region": "地域",
|
||||
"Comment": "备注",
|
||||
"RunTaskManually":"手动执行",
|
||||
"True":"是",
|
||||
"False": "否",
|
||||
"NewCount": "新增",
|
||||
"UnSyncCount": "未同步",
|
||||
"SyncedCount": "已同步",
|
||||
"ReleasedCount": "已释放",
|
||||
"DateSync": "同步日期",
|
||||
"Status": "状态",
|
||||
"Log": "日志",
|
||||
"DeleteReleasedAssets": "删除已释放资产"
|
||||
},
|
||||
"Corporation": "公司",
|
||||
"Edition": "版本",
|
||||
@@ -847,6 +1040,13 @@
|
||||
"ImportLicenseTip": "请导入许可证",
|
||||
"InterfaceSettings": "界面设置",
|
||||
"License": "许可证",
|
||||
"SystemMonitor": "系统监控",
|
||||
"ServiceRatio": "组件负载统计",
|
||||
"LoadStatus":"组件状态",
|
||||
"NormalLoad":"正常",
|
||||
"HighLoad":"较高",
|
||||
"CriticalLoad":"严重",
|
||||
"Offline": "离线",
|
||||
"LicenseDetail": "许可证详情",
|
||||
"LicenseFile": "许可证文件",
|
||||
"NoLicense": "暂无许可证",
|
||||
@@ -855,7 +1055,13 @@
|
||||
"OrganizationCreate": "创建组织",
|
||||
"OrganizationDetail": "组织详情",
|
||||
"OrganizationList": "组织管理",
|
||||
"OrganizationUpdate": "更新组织"
|
||||
"OrganizationUpdate": "更新组织",
|
||||
"OrganizationMembership": "组织成员",
|
||||
"DeleteOrgTitle": "请确保组织内的以下信息已删除",
|
||||
"DeleteOrgMsg": "用户列表、用户组、资产列表、网域列表、管理用户、系统用户、标签管理、资产授权规则",
|
||||
"OrgRole": "组织角色",
|
||||
"CreateOrgMsg": "请去组织详情内添加用户",
|
||||
"AddOrgMembers": "添加组织成员"
|
||||
},
|
||||
"RestoreButton": "恢复默认",
|
||||
"SubscriptionID": "订阅授权ID",
|
||||
@@ -872,9 +1078,9 @@
|
||||
"loginImageTip": "提示:将会显示在企业版用户登录页面(建议图片大小为: 492*472px)",
|
||||
"loginTitle": "登录页面标题",
|
||||
"loginTitleTip": "提示:将会显示在企业版用户登录页面(eg: 欢迎使用JumpServer开源堡垒机)",
|
||||
"logoIndex": "管理页面logo",
|
||||
"logoIndex": "Logo (带文字)",
|
||||
"logoIndexTip": "提示:将会显示在管理页面左上方(建议图片大小为: 185px*55px)",
|
||||
"logoLogout": "退出页面logo",
|
||||
"logoLogout": "Logo (不带文字)",
|
||||
"logoLogoutTip": "提示:将会显示在企业版用户退出页面(建议图片大小为:82px*82px)",
|
||||
"restoreDialogMessage": "您确定要恢复默认初始化吗?",
|
||||
"restoreDialogTitle": "你确认吗",
|
||||
|
||||
@@ -2,6 +2,22 @@
|
||||
"": "",
|
||||
"applications": {
|
||||
"": "",
|
||||
"applicationsType": {
|
||||
"chrome": "Chrome",
|
||||
"mysql_workbench": "MySQL Workbench",
|
||||
"vmware_client":"Vmware Client",
|
||||
"custom":"Custom",
|
||||
"mysql": "MySQL",
|
||||
"oracle": "Oracle",
|
||||
"postgresql": "PostgreSQL",
|
||||
"mariadb": "MariaDB",
|
||||
"k8s": "kubernetes"
|
||||
},
|
||||
"applicationsCategory": {
|
||||
"remote_app": "Remote app",
|
||||
"db": "Database app",
|
||||
"cloud": "Cloud app"
|
||||
},
|
||||
"appPath": "App path",
|
||||
"appType": "App type",
|
||||
"asset": "Asset",
|
||||
@@ -17,6 +33,7 @@
|
||||
"mysql_workbench": "MySQL Workbench",
|
||||
"mysql_workbench_ip": "DB IP",
|
||||
"mysql_workbench_name": "DB Name",
|
||||
"mysql_workbench_port": "DB Port",
|
||||
"mysql_workbench_username": "DB Account",
|
||||
"mysql_workbench_password": "DB Password",
|
||||
"vmware_client": "vSphere Client",
|
||||
@@ -28,7 +45,11 @@
|
||||
"custom_target": "target URL",
|
||||
"custom_username": "Account",
|
||||
"custom_password": "Password",
|
||||
"Custom": "Custom"
|
||||
"Custom": "Custom",
|
||||
"cluster": "Cluster",
|
||||
"kubernetes":"Kubernetes",
|
||||
"clusterHelpTextMessage": "Tips: https://172.16.8.8:8443",
|
||||
"DBInfo": "Database Info"
|
||||
},
|
||||
"assets": {
|
||||
"Action": "Action",
|
||||
@@ -37,11 +58,16 @@
|
||||
"AdminUserDetail": "Admin user detail",
|
||||
"AdminUserListHelpMessage": "Admin users are asset (charged server) on the root, or have NOPASSWD: ALL sudo permissions users, JumpServer users of the system using the user to `push system user`, `get assets hardware information`, etc.\n",
|
||||
"Asset": "Asset",
|
||||
"Hardware": "Hardware",
|
||||
"AccountList": "Account list",
|
||||
"AssetDetail": "Asset detail",
|
||||
"AssetList": "Asset list",
|
||||
"AssetListHelpMessage": "The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node\n",
|
||||
"AssetNumber": "Asset number",
|
||||
"AssetUserList": "Asset user list",
|
||||
"TestGatewayTestConnection":"Test gateway test connection",
|
||||
"TestGatewayHelpMessage": "If use nat, set the ssh real port",
|
||||
"SshPort": "SSH Port",
|
||||
"Assets": "Assets",
|
||||
"Auth": "Auth",
|
||||
"AutoGenerateKey": "Auto generate ssh key",
|
||||
@@ -63,6 +89,8 @@
|
||||
"DateUpdated": "Date updated",
|
||||
"DeactiveSelected": "Deactive selected",
|
||||
"Disk": "Disk",
|
||||
"AdDomain": "AD Domain",
|
||||
"AdDomainHelpText": "AD domain provided to domain users for login",
|
||||
"Domain": "Domain",
|
||||
"DomainDetail": "Domain detail",
|
||||
"DomainHelpMessage": "The domain function is added to address the fact that some environments (such as the hybrid cloud) cannot be connected directly by jumping on the gateway server.\nJMS => Domain gateway => Target assets",
|
||||
@@ -100,10 +128,13 @@
|
||||
"PushSystemUserNow": "Push system user now",
|
||||
"QuickUpdate": "Quick update",
|
||||
"Reachable": "Reachable",
|
||||
"Unreachable": "Unreachable",
|
||||
"Unknown": "Unknown",
|
||||
"Refresh": "Refresh",
|
||||
"RefreshHardware": "Refresh hardware",
|
||||
"RemoteAppListHelpMessage": "Before using this feature, make sure that the application loader has been uploaded to the application server and successfully published as a RemoteApp application <b><a href='https://github.com/jumpserver/Jmservisor/releases'> Download application loader</a></b>",
|
||||
"RemoteApps": "Remote apps",
|
||||
"Applications": "Applications",
|
||||
"RemoteType": "Remote type",
|
||||
"RemoveFromCurrentNode": "Remove from node",
|
||||
"ReplaceNodeAssetsAdminUserWithThis": "Replace node assets admin user with this",
|
||||
@@ -111,6 +142,7 @@
|
||||
"SFTPHelpMessage": "SFTP root dir, tmp, home or custom",
|
||||
"SerialNumber": "Serial number",
|
||||
"SudoHelpMessage": "Use comma split multi command, ex: /bin/whoami,/bin/ifconfig",
|
||||
"PasswordHelpMessage": "Password or private key password",
|
||||
"SystemUser": "System user",
|
||||
"SystemUserDetail": "System user detail",
|
||||
"SystemUserListHelpMessage": "System user is JumpServer jump login assets used by the users, can be understood as the user login assets, such as web, sa, the dba (` ssh web@some-host `), rather than using a user the username login server jump (` ssh xiaoming@some-host `); In simple terms, users log into JumpServer using their own username, and JumpServer uses system users to log into assets. When system users are created, if you choose auto push JumpServer to use Ansible push system users into the asset, if the asset (Switch) does not support ansible, please manually fill in the account password.\n",
|
||||
@@ -120,6 +152,7 @@
|
||||
"TestConnection": "Test connection",
|
||||
"Type": "Type",
|
||||
"UnselectedAssets": "Unselected assets",
|
||||
"UnselectedNodes": "Unselected nodes",
|
||||
"UpdateAssetUserToken": "Update asset user auth",
|
||||
"Username": "Username",
|
||||
"UsernameHelpMessage": "Username is dynamic, When connect asset, using current user's username",
|
||||
@@ -129,7 +162,12 @@
|
||||
"command_filter_list": "Command filter list",
|
||||
"date_joined": "Date joined",
|
||||
"ip": "IP",
|
||||
"sshkey": "sshkey"
|
||||
"sshkey": "sshkey",
|
||||
"GroupsHelpMessage": "Please fill in user groups, separated by commas if there are multiple user groups(Please fill in the existing user groups)",
|
||||
"HomeHelpMessage": "Default home directory: /home/system username",
|
||||
"Home": "Home",
|
||||
"LinuxUserAffiliateGroup": "Linux user affiliate group",
|
||||
"ipDomain": "IP(Domain)"
|
||||
},
|
||||
"audits": {
|
||||
"Hosts": "Host",
|
||||
@@ -144,13 +182,22 @@
|
||||
"common": {
|
||||
"Nothing": "Nothing",
|
||||
"Action": "Action",
|
||||
"RequestTickets": "Request tickets",
|
||||
"Actions": "Actions",
|
||||
"NeedSpecifiedFile": "Required to upload the specified format file",
|
||||
"TestPortErrorMsg":"Port Error, please check",
|
||||
"Activate": "Activate",
|
||||
"actionsTips":"Clipboard's copy and paste control only support RDP/VNC protocol.",
|
||||
"Active": "Active",
|
||||
"Add": "Add",
|
||||
"PleaseAgreeToTheTerms": "Please agree to the terms",
|
||||
"PushSelected":"Push selected",
|
||||
"UpdateAssetDetail": "Update more detail",
|
||||
"AddSuccessMsg": "Add success",
|
||||
"Auth": "Authorization",
|
||||
"BadRequestErrorMsg": "Bad request, please check again",
|
||||
"BadRoleErrorMsg": "Bad request, no permission for this operation",
|
||||
"BadConflictErrorMsg": "Refreshing, please try again later",
|
||||
"Basic": "Basic",
|
||||
"BasicInfo": "Basic info",
|
||||
"Cancel": "Cancel",
|
||||
@@ -162,6 +209,7 @@
|
||||
"CreatedBy": "Created by",
|
||||
"CrontabHelpTips": "eg: Every Sunday 03:05 run <5 3 * * 0> <br>Tips:Using 5 digits linux crontab expressions<min hour day month week> (<a href='https://tool.lu/crontab/' target='_blank'>Online tools</a>) <br>Note:If both Regularly perform and Cycle perform are set,give priority to Regularly perform",
|
||||
"DateEnd": "End date",
|
||||
"Resource": "Resource",
|
||||
"DateLast24Hours": "Last 24 hours",
|
||||
"DateLast3Months": "Last 3 months",
|
||||
"DateLastMonth": "Last month",
|
||||
@@ -175,11 +223,13 @@
|
||||
"EnterForSearch": "Press enter to search",
|
||||
"Export": "Export",
|
||||
"Import": "Import",
|
||||
"Refresh": "Refresh",
|
||||
"Info": "Info",
|
||||
"MFAConfirm": "MFA Confirm",
|
||||
"MFARequireForSecurity": "MFA required for security",
|
||||
"Members": "Members",
|
||||
"More": "More",
|
||||
"Message": "Message",
|
||||
"MoreActions": "Actions",
|
||||
"Name": "Name",
|
||||
"No": "No",
|
||||
@@ -202,6 +252,7 @@
|
||||
"To": "To",
|
||||
"Update": "Update",
|
||||
"Upload": "Upload",
|
||||
"Clone": "Clone",
|
||||
"Username": "Username",
|
||||
"Validity": "Validity",
|
||||
"Invalidity": "Invalidity",
|
||||
@@ -213,11 +264,15 @@
|
||||
"bulkDeleteSuccessMsg": "Bulk delete success",
|
||||
"bulkRemoveErrorMsg": "Bulk remove failed: ",
|
||||
"bulkRemoveSuccessMsg": "Bulk remove success",
|
||||
"NeedAssetsAndSystemUserErrMsg": "Need assets and systemuser",
|
||||
"createBy": "Create by",
|
||||
"cloneFrom": "Clone from",
|
||||
"createErrorMsg": "Create error",
|
||||
"createSuccessMsg": "Create success",
|
||||
"saveSuccessContinueMsg": "Create success, you may add another",
|
||||
"createdBy": "Created by",
|
||||
"dateCreated": "Date created",
|
||||
"dateFinished": "Date finished",
|
||||
"dateExpired": "Date expired",
|
||||
"dateStart": "Date start",
|
||||
"deleteErrorMsg": "Delete failed",
|
||||
@@ -228,6 +283,7 @@
|
||||
"disableSelected": "Disable selected",
|
||||
"fieldRequiredError": "This field is required",
|
||||
"getErrorMsg": "Get failed",
|
||||
"fileType": "File type",
|
||||
"imExport": {
|
||||
"ExportAll": "Export all",
|
||||
"ExportOnlyFiltered": "Export only filtered",
|
||||
@@ -353,7 +409,7 @@
|
||||
},
|
||||
"perms": {
|
||||
"": "",
|
||||
"Actions": "Actions",
|
||||
"Actions": "Permission",
|
||||
"Asset": "Asset",
|
||||
"Basic": "Basic",
|
||||
"Exclude": "Exclude",
|
||||
@@ -364,8 +420,13 @@
|
||||
"SystemUser": "System user",
|
||||
"User": "User",
|
||||
"UserGroups": "UserGroups",
|
||||
"DatabaseAppPermission": "Databases permissions",
|
||||
"RemoteAppPermission": "Remote apps permissions",
|
||||
"KubernetesAppPermission": "Kubernetes permissions",
|
||||
"addAssetToThisPermission": "Add asset to this permission",
|
||||
"addDatabaseAppToThisPermission": "Add DatabaseApp to this permission",
|
||||
"addK8sAppToThisPermission": "Add KubernetesApp to this permission",
|
||||
"addApplicationToThisPermission": "Add Application to this permission",
|
||||
"addNodeToThisPermission": "Add node to this permission",
|
||||
"addRemoteAppToThisPermission": "Add RemoteApp to this permission",
|
||||
"addSystemUserToThisPermission": "System user",
|
||||
@@ -376,6 +437,7 @@
|
||||
"assetCount": "Asset count",
|
||||
"connect": "Connect",
|
||||
"databaseApp": "DatabaseApp",
|
||||
"KubernetesApp": "KubernetesApp",
|
||||
"dateStart": "Date start",
|
||||
"downloadFile": "Download file",
|
||||
"hostName": "Hostname",
|
||||
@@ -386,15 +448,23 @@
|
||||
"refreshSuccess": "Refresh success",
|
||||
"remoteApp": "RemoteApp",
|
||||
"remoteAppCount": "RemoteApp count",
|
||||
"appsCount": "App count",
|
||||
"appsList":"App list",
|
||||
"DatabaseAppCount": "DatabaseApp count",
|
||||
"KubernetesAppCount": "KubernetesApp count",
|
||||
"systemUserCount": "System user count",
|
||||
"upDownload": "Upload download",
|
||||
"uploadFile": "Upload file",
|
||||
"clipboardCopyPaste":"Copy Paste",
|
||||
"clipboardCopy":"Clipboard copy",
|
||||
"clipboardPaste":"Clipboard paste",
|
||||
"userCount": "User count",
|
||||
"userGroupCount": "User group count",
|
||||
"usersAndUserGroups": "Users and user groups"
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"Ticket": "Tickets",
|
||||
"AdminUserCreate": "Admin user create",
|
||||
"AdminUserDetail": "Admin user detail",
|
||||
"AdminUserList": "Admin users",
|
||||
@@ -433,6 +503,14 @@
|
||||
"DatabaseAppPermissionDetail": "Databases permissions detail",
|
||||
"DatabaseAppPermissionUpdate": "Databases permissions update",
|
||||
"DatabaseAppUpdate": "Database app update",
|
||||
"KubernetesApp": "Kubernetes apps",
|
||||
"KubernetesAppCreate": "Kubernetes app create",
|
||||
"KubernetesAppDetail": "Kubernetes app detail",
|
||||
"KubernetesAppPermission": "Kubernetes permissions",
|
||||
"KubernetesAppPermissionCreate": "Kubernetes permissions create",
|
||||
"KubernetesAppPermissionDetail": "Kubernetes permissions detail",
|
||||
"KubernetesAppPermissionUpdate": "Kubernetes permissions update",
|
||||
"KubernetesAppUpdate": "Kubernetes app update",
|
||||
"DomainCreate": "Domain create",
|
||||
"DomainDetail": "Domain detail",
|
||||
"DomainList": "Domains",
|
||||
@@ -459,6 +537,7 @@
|
||||
"RemoteApp": "Remote apps",
|
||||
"RemoteAppDetail": "Remote app detail",
|
||||
"RemoteAppPermission": "Remote apps permissions",
|
||||
"ApplicationPermission": "Application permissions",
|
||||
"RemoteAppPermissionCreate": "Remote apps permission create",
|
||||
"RemoteAppPermissionDetail": "Remote apps permissions detail",
|
||||
"RemoteAppPermissionUpdate": "Remote app permission update",
|
||||
@@ -478,6 +557,7 @@
|
||||
"TaskMonitor": "Task monitor",
|
||||
"Terminal": "Terminal",
|
||||
"TicketDetail": "Ticket detail",
|
||||
"TicketCreate": "Ticket create",
|
||||
"Tickets": "Tickets",
|
||||
"UserCreate": "User create",
|
||||
"UserDetail": "User detail",
|
||||
@@ -501,6 +581,7 @@
|
||||
"active": "active",
|
||||
"alive": "alive",
|
||||
"asset": "Asset",
|
||||
"target": "Target",
|
||||
"bucket": "Bucket",
|
||||
"command": "Command",
|
||||
"commandStorage": "Command storage",
|
||||
@@ -516,6 +597,10 @@
|
||||
"duration": "Duration",
|
||||
"endPoint": "Endpoint",
|
||||
"endpointSuffix": "Endpoint suffix",
|
||||
"sessionActiveCount": "session active count",
|
||||
"systemCpuLoad": "cpu load",
|
||||
"systemDiskUsedPercent": "disk used percent",
|
||||
"systemMemoryUsedPercent": "memory used percent",
|
||||
"go": "Go",
|
||||
"goto": "Goto",
|
||||
"hosts": "Hosts",
|
||||
@@ -533,13 +618,15 @@
|
||||
"replaySession": "Replay session",
|
||||
"replayStorage": "Replay storage",
|
||||
"riskLevel": "Risk level",
|
||||
"session": "session",
|
||||
"session": "Session",
|
||||
"sshPort": "SSH port",
|
||||
"storage": "Storage",
|
||||
"systemUser": "System user",
|
||||
"terminalDetail": "Terminal detail",
|
||||
"terminalUpdate": "Update terminal",
|
||||
"terminalUpdateStorage": "Update terminal storage",
|
||||
"terminate": "Terminate",
|
||||
"sessionTerminate": "Session Terminate",
|
||||
"test": "Test",
|
||||
"type": "Type",
|
||||
"user": "Use",
|
||||
@@ -547,7 +634,15 @@
|
||||
"common": "common"
|
||||
},
|
||||
"Monitor": "Monitor",
|
||||
"TerminateTaskSendSuccessMsg": "Terminate task has been send, Please check later"
|
||||
"sessionMonitor": "Session Monitor",
|
||||
"TerminateTaskSendSuccessMsg": "Terminate task has been send, Please check later",
|
||||
"helpText": {
|
||||
"esUrl": "Tip: If you have multiple hosts, use comma (,) to split (eg: http://www.jumpserver.a.com,http://www.jumpserver.b.com)",
|
||||
"esIndex":"Es provides the default index: jumpserver",
|
||||
"esDocType": "Es provides the default document type: command",
|
||||
"s3Endpoint": "S3: http://s3.{REGION_NAME}.amazonaws.com<br>S3(China): http://s3.{REGION_NAME}.amazonaws.com.cn<br>Example: http://s3.cn-north-1.amazonaws.com.cn",
|
||||
"ossEndpoint": "OSS: http://{REGION_NAME}.aliyuncs.com<br>Example: http://oss-cn-hangzhou.aliyuncs.com"
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"ApiKeyList": "Api key list",
|
||||
@@ -578,6 +673,7 @@
|
||||
"authLdapSearchOu": "User OU",
|
||||
"authLdapServerUri": "LDAP server",
|
||||
"authLdapUserAttrMap": "User attr map",
|
||||
"unselectedUser": "Unselected user",
|
||||
"auto": "Auto",
|
||||
"basicSetting": "Basic setting",
|
||||
"communityEdition": "Community edition",
|
||||
@@ -630,6 +726,11 @@
|
||||
"securityPasswordUpperCase": "After opening, the user password changes and resets must contain uppercase letters",
|
||||
"securityServiceAccountRegistration": "Allow using bootstrap token register service account, when terminal setup, can disable it"
|
||||
},
|
||||
"validatorMessage": {
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo3": "Ensure this value is greater than or equal to 3",
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo5": "Ensure this value is greater than or equal to 5",
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo6": "Ensure this value is greater than or equal to 6"
|
||||
},
|
||||
"import": "Import",
|
||||
"importLdapUserTip": "Please submit the LDAP configuration before import",
|
||||
"importLdapUserTitle": "LDAP user list",
|
||||
@@ -664,7 +765,11 @@
|
||||
"testLdapLoginTitle": "Test LDAP user login",
|
||||
"userGuideUrl": "User Guide URL",
|
||||
"username": "Username",
|
||||
"usernamePlaceholder": "Please input username"
|
||||
"usernamePlaceholder": "Please input username",
|
||||
"refreshLdapCache":"Refreshing Ldap cache ",
|
||||
"LicenseExpired": "License expired",
|
||||
"LicenseWillBe": "License will expire at ",
|
||||
"Expire": ""
|
||||
},
|
||||
"settings": {
|
||||
"setting": "Setting"
|
||||
@@ -673,6 +778,10 @@
|
||||
"Accept": "Accept",
|
||||
"AssignedMe": "Assigned me",
|
||||
"Assignee": "Assignee",
|
||||
"RequestPerm":"Request Perm",
|
||||
"OpenTicket": "Open Ticket",
|
||||
"HandleTicket": "Handle Ticket",
|
||||
"FinishedTicket": "Finished Ticket",
|
||||
"Assignees": "Assignees",
|
||||
"Close": "Close",
|
||||
"Comment": "Comment",
|
||||
@@ -685,7 +794,21 @@
|
||||
"type": "Type",
|
||||
"user": "User",
|
||||
"Status": "Status",
|
||||
"Open": "Open"
|
||||
"Open": "Open",
|
||||
"IP": "IP",
|
||||
"Hostname": "Hostname",
|
||||
"Asset": "Asset",
|
||||
"SystemUser": "System user",
|
||||
"Applicant": "Applicant",
|
||||
"RequestAssetPerm": "Request asset perm",
|
||||
"Pending": "Open",
|
||||
"Approved": "Approved",
|
||||
"Rejected": "Rejected",
|
||||
"Closed": "Closed",
|
||||
"helpText": {
|
||||
"ips": "Enter the IP address group, separated by commas",
|
||||
"fuzzySearch": "Support for fuzzy search"
|
||||
}
|
||||
},
|
||||
"tree": {
|
||||
"AddAssetToNode": "Add asset to node",
|
||||
@@ -695,12 +818,14 @@
|
||||
"RenameNode": "Rename node",
|
||||
"ShowAssetAllChildrenNode": "Show asset all children node",
|
||||
"ShowAssetOnlyCurrentNode": "Show asset only current node",
|
||||
"CheckAssetsAmount": "Check assets amount",
|
||||
"ShowNodeInfo": "Show node information",
|
||||
"TestNodeAssetConnectivity": "Test node asset connectivity",
|
||||
"UpdateNodeAssetHardwareInfo": "Update node asset hardware information"
|
||||
},
|
||||
"users": {
|
||||
"Account": "Account",
|
||||
"Existing":"Existing",
|
||||
"Authentication": "Account",
|
||||
"Comment": "Comment",
|
||||
"ConfirmPassword": "Confirm password",
|
||||
@@ -713,11 +838,19 @@
|
||||
"Email": "Email",
|
||||
"FingerPrint": "Fingerprint",
|
||||
"FirstLogin": "First login",
|
||||
"InviteUser": "Invite user",
|
||||
"InviteUserInOrg": "Invite user in this org",
|
||||
"Invite": "Invite",
|
||||
"Guide": "Guide",
|
||||
"OrgUser": "Org User",
|
||||
"OrgAdmin": "Org Admin",
|
||||
"OrgAuditor": "Org Auditor",
|
||||
|
||||
"HelpText": {
|
||||
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "Enable multi-factor authentication to make the account more secure <br/> After is enabled, you will enter the multi-factor authentication binding process on your next login <br/> You can also bind directly in (personal information -> fast modifier -> modifier multiple factor Settings)",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "To protect the security of you and the company <br/> please properly keep your account, password, key and other important and sensitive information <br/> (e.g., set a complex password and enable multi-factor authentication)",
|
||||
"SSHKeyOfProfileSSHUpdatePage": "Copy your public key here"
|
||||
"SSHKeyOfProfileSSHUpdatePage": "Copy your public key here",
|
||||
"OrgRoleHelpText": "Organizational roles are the user's role in the current organization"
|
||||
},
|
||||
"IAgree": "I agree",
|
||||
"ImprovePersonalInformation": "Improve personal information",
|
||||
@@ -736,6 +869,8 @@
|
||||
"ResetAndDownloadSSHKey": "Reset and download SSH Key",
|
||||
"ResetPublicKeyAndDownload": "Reset public key and download",
|
||||
"Role": "Role",
|
||||
"SuperRole": "Super role",
|
||||
"OrgRole": "Org role",
|
||||
"SSHKey": "SSH Key",
|
||||
"SSHKeySetting": "SSH Key setting",
|
||||
"Secure": "Secure",
|
||||
@@ -766,13 +901,20 @@
|
||||
"tabs": {
|
||||
"assetPermissionRules": "Asset permission rules",
|
||||
"databasePermissionRules": "Database Permission rules",
|
||||
"k8sPermissionRules": "Kubernetes Permission rules",
|
||||
"grantedAssets": "Granted assets",
|
||||
"grantedK8Ss":"Granted K8Ss",
|
||||
"grantedDatabases": "Granted databases",
|
||||
"grantedRemoteApps": "Granted remote apps",
|
||||
"grantedApplications": "Granted applications",
|
||||
"ApplicationPermissionRules": "Application permission rules",
|
||||
"remoteAppPermissionRules": "Remote app permission rules"
|
||||
},
|
||||
"UpdatePassword": "",
|
||||
"UpdatePublicKey": ""
|
||||
"UpdatePublicKey": "",
|
||||
"passwordExpired": "Password expired",
|
||||
"passwordWillExpiredPrefixMsg": "The password will expire in ",
|
||||
"passwordWillExpiredSuffixMsg": " days.Please change your password as soon as possible."
|
||||
},
|
||||
"xpack": {
|
||||
"Admin": "Admin",
|
||||
@@ -797,6 +939,9 @@
|
||||
"ExecutionDetail": "Execution detail",
|
||||
"ExecutionList": "Execution list",
|
||||
"ExecutionTimes": "Execution times",
|
||||
"validatorMessage": {
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo1": "Ensure this value is greater than or equal to 1"
|
||||
},
|
||||
"HelpText": {
|
||||
"CrontabOfCreateUpdatePage": "For example: every Sunday at 03:05 execute <5 3 * * 0> <br/> Using the 5-bit Linux crontab expression <minute hour day month week> (<a href=\"https://tool.lu/crontab/\" target=\"_blank\"> Online tool </a>) <br/> If both regularly perform and cycle perform execution are set, use regularly perform first",
|
||||
"IntervalOfCreateUpdatePage": "Unit: hour",
|
||||
@@ -818,17 +963,54 @@
|
||||
"Username": "Username"
|
||||
},
|
||||
"Cloud": {
|
||||
"Aliyun": "Ali Cloud",
|
||||
"Qcloud": "Tencent Cloud",
|
||||
"AWS_China": "AWS(China)",
|
||||
"AWS_Int": "AWS(International)",
|
||||
"HuaweiCloud": "Huawei Cloud",
|
||||
"Azure":"Azure(China)",
|
||||
"HostnameStrategy": "Used to produce the asset hostname. For example, 1. Instance name (instanceDemo);2. Instance name and Partial IP (instanceDemo-250.1)",
|
||||
"IsAlwaysUpdate": "Asset info is kept up-to-date",
|
||||
"AccountCreate": "Create account",
|
||||
"AccountList": "Account list",
|
||||
"AccountUpdate": "Update account",
|
||||
"AccountDetail": "Account detail",
|
||||
"Cloud": "Cloud center",
|
||||
"CloudCenter": "Cloud center",
|
||||
"CoveredAlwaysHelpTips": "The asset information is always covered",
|
||||
"Provider": "Provider",
|
||||
"Validity": "Validity",
|
||||
"IsAlwaysUpdateHelpTips": "Whether the asset information, including Hostname, IP, Platform, and AdminUser, is updated synchronously each time a synchronization task is performed",
|
||||
"SyncInstanceTaskCreate": "Create sync instance task",
|
||||
"SyncInstanceTaskList": "Sync instance task list",
|
||||
"SyncInstanceTaskDetail": "Sync instance task detail",
|
||||
"SyncInstanceTaskUpdate": "Update sync instance task",
|
||||
"SyncInstanceTaskHistoryList": "Sync task history",
|
||||
"SyncInstanceTaskHistoryAssetList": "Sync instance list",
|
||||
"CloudSource": "Cloud source",
|
||||
"SaveSetting": "Save setting"
|
||||
"SaveSetting": "Save setting",
|
||||
"Name": "Name",
|
||||
"Account":"Account",
|
||||
"Node": "Node",
|
||||
"AdminUser":"Admin user",
|
||||
"PeriodicPerform":"Periodic perform",
|
||||
"Periodic":"Periodic",
|
||||
"RegularlyPerform": "Regularly perform",
|
||||
"CyclePerform": "Cycle perform",
|
||||
"DateLastSync":"Date last sync",
|
||||
"DateCreated":"Date created",
|
||||
"Region": "Region",
|
||||
"RunTaskManually":"Run task manually",
|
||||
"Comment": "Comment",
|
||||
"True":"True",
|
||||
"False": "False",
|
||||
"NewCount": "New count",
|
||||
"UnSyncCount": "Unsync count",
|
||||
"SyncedCount": "Synced count",
|
||||
"ReleasedCount": "Released count",
|
||||
"DateSync": "Date sync",
|
||||
"Status": "Status",
|
||||
"Log": "Log",
|
||||
"DeleteReleasedAssets": "Delete released assets"
|
||||
},
|
||||
"Corporation": "Corporation",
|
||||
"Edition": "Edition",
|
||||
@@ -847,6 +1029,13 @@
|
||||
"InterfaceSettings": "Interface setting",
|
||||
"License": "License",
|
||||
"LicenseDetail": "License detail",
|
||||
"SystemMonitor": "System Monitor",
|
||||
"ServiceRatio": "Service ratio",
|
||||
"LoadStatus":"Status",
|
||||
"NormalLoad":"Normal",
|
||||
"HighLoad":"High",
|
||||
"Offline": "Offline",
|
||||
"CriticalLoad":"Critical",
|
||||
"LicenseFile": "License file",
|
||||
"NoLicense": "No License",
|
||||
"Node": "Node",
|
||||
@@ -854,7 +1043,13 @@
|
||||
"OrganizationCreate": "Create organization",
|
||||
"OrganizationDetail": "Org detail",
|
||||
"OrganizationList": "Organlizations",
|
||||
"OrganizationUpdate": "Update org"
|
||||
"OrganizationUpdate": "Update org",
|
||||
"OrganizationMembership": "Organization membership",
|
||||
"DeleteOrgTitle":"Please ensure that the following information in the organization has been deleted",
|
||||
"DeleteOrgMsg":"User list、User group、Asset list、Domain list、Admin user、System user、Labels、Asset permission",
|
||||
"OrgRole": "Org role",
|
||||
"CreateOrgMsg": "Please go to Organization Details to add users",
|
||||
"AddOrgMembers": "Add organization members"
|
||||
},
|
||||
"RestoreButton": "Restore Default",
|
||||
"SubscriptionID": "Subscription ID",
|
||||
@@ -871,9 +1066,9 @@
|
||||
"loginImageTip": "Tips: This will be displayed on the enterprise user login page. (suggest image size: 492px*472px)",
|
||||
"loginTitle": "Title of login page",
|
||||
"loginTitleTip": "Tips: This will be displayed on the enterprise user login page. (eg: Welcome to the JumpServer open source fortress)",
|
||||
"logoIndex": "Logo of management page",
|
||||
"logoIndex": "Logo (It contains text)",
|
||||
"logoIndexTip": "Tips: This will appear at the top left of the administration page. (suggest image size: 185px*55px)",
|
||||
"logoLogout": "Logo of logout page",
|
||||
"logoLogout": "Logo (It contains no text)",
|
||||
"logoLogoutTip": "Tips: This will be displayed on the enterprise user logout page. (suggest image size: 82px*82px)",
|
||||
"restoreDialogMessage": "This will restore default Settings of the interface !!!",
|
||||
"restoreDialogTitle": "Are you sure?",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="footer" :style="style">
|
||||
<div class="pull-right">
|
||||
Version <strong>2.0.0</strong> <span v-if="!publicSettings.XPACK_LICENSE_IS_VALID"> GPLv2. </span>
|
||||
Version <strong> dev </strong> <span v-if="!publicSettings.XPACK_LICENSE_IS_VALID"> GPLv2. </span>
|
||||
</div>
|
||||
<div v-if="!publicSettings.XPACK_LICENSE_IS_VALID" style="padding-left:20px;">
|
||||
<strong>Copyright</strong> FIT2CLOUD 飞致云 © 2014-2020
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
:method="method"
|
||||
:form="form"
|
||||
:url="iUrl"
|
||||
:has-save-continue="iHasSaveContinue"
|
||||
:has-reset="iHasReset"
|
||||
:is-submitting="isSubmitting"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@@ -13,50 +15,66 @@
|
||||
</template>
|
||||
<script>
|
||||
import AutoDataForm from '@/components/AutoDataForm'
|
||||
import deepmerge from 'deepmerge'
|
||||
export default {
|
||||
name: 'GenericCreateUpdateForm',
|
||||
components: {
|
||||
AutoDataForm
|
||||
},
|
||||
props: {
|
||||
// 创建对象的地址
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 更新的对象
|
||||
object: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
// form的默认值
|
||||
initial: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
// 提交前,清理form的值
|
||||
cleanFormValue: {
|
||||
type: Function,
|
||||
default: (value) => value
|
||||
},
|
||||
// 当提交的时候,怎么处理
|
||||
onSubmit: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
// 如何提交数据
|
||||
performSubmit: {
|
||||
type: Function,
|
||||
default(validValues) {
|
||||
return this.$axios[this.method](this.iUrl, validValues)
|
||||
}
|
||||
},
|
||||
// 创建成功的msg
|
||||
createSuccessMsg: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return this.$t('common.createSuccessMsg')
|
||||
}
|
||||
},
|
||||
// 更新成功的msg
|
||||
saveSuccessContinueMsg: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return this.$t('common.saveSuccessContinueMsg')
|
||||
}
|
||||
},
|
||||
updateSuccessMsg: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return this.$t('common.updateSuccessMsg')
|
||||
}
|
||||
},
|
||||
// 创建成功的跳转路由
|
||||
createSuccessNextRoute: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
@@ -64,6 +82,7 @@ export default {
|
||||
return { name: routeName }
|
||||
}
|
||||
},
|
||||
// 更新成功的跳转路由
|
||||
updateSuccessNextRoute: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
@@ -71,12 +90,21 @@ export default {
|
||||
return { name: routeName }
|
||||
}
|
||||
},
|
||||
objectDetailRoute: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
const routeName = this.$route.name.replace('Update', 'Detail').replace('Create', 'Detail')
|
||||
return { name: routeName }
|
||||
}
|
||||
},
|
||||
// 获取下一个路由
|
||||
getNextRoute: {
|
||||
type: Function,
|
||||
default(res, method) {
|
||||
return method === 'post' ? this.createSuccessNextRoute : this.updateSuccessNextRoute
|
||||
}
|
||||
},
|
||||
// 获取提交的方法
|
||||
getMethod: {
|
||||
type: Function,
|
||||
default: function() {
|
||||
@@ -88,6 +116,7 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
// 获取创建和更新的url function
|
||||
getUrl: {
|
||||
type: Function,
|
||||
default: function() {
|
||||
@@ -101,12 +130,47 @@ export default {
|
||||
},
|
||||
onPerformSuccess: {
|
||||
type: Function,
|
||||
default(res, method, vm) {
|
||||
const msg = method === 'post' ? this.createSuccessMsg : this.updateSuccessMsg
|
||||
default(res, method, vm, addContinue) {
|
||||
let msg = method === 'post' ? this.createSuccessMsg : this.updateSuccessMsg
|
||||
if (addContinue) {
|
||||
msg = this.saveSuccessContinueMsg
|
||||
}
|
||||
let msgLinkName = this.$t('common.Resource')
|
||||
if (res.name) {
|
||||
msgLinkName = res.name
|
||||
} else if (res.hostname) {
|
||||
msgLinkName = res.hostname
|
||||
}
|
||||
const detailRoute = this.objectDetailRoute
|
||||
detailRoute['params'] = { 'id': res.id }
|
||||
const route = this.getNextRoute(res, method)
|
||||
this.$emit('submitSuccess', res)
|
||||
this.$message.success(msg)
|
||||
setTimeout(() => this.$router.push(route), 100)
|
||||
const h = this.$createElement
|
||||
this.$log.debug('router is: ', detailRoute)
|
||||
if (this.hasDetailInMsg) {
|
||||
this.$message({
|
||||
message: h('p', null, [
|
||||
h('el-link', {
|
||||
on: {
|
||||
click: () => this.$router.push(detailRoute)
|
||||
},
|
||||
style: { 'vertical-align': 'top' }
|
||||
}, msgLinkName),
|
||||
h('span', { style: {
|
||||
'padding-left': '5px',
|
||||
'height': '18px',
|
||||
'line-height': '18px',
|
||||
'font-size': '13.5px',
|
||||
'font-weight': ' 400' }}, msg)
|
||||
]),
|
||||
type: 'success'
|
||||
})
|
||||
} else {
|
||||
this.$message.success(msg)
|
||||
}
|
||||
if (!addContinue) {
|
||||
setTimeout(() => this.$router.push(route), 100)
|
||||
}
|
||||
}
|
||||
},
|
||||
onPerformError: {
|
||||
@@ -125,13 +189,22 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hasSaveContinue: {
|
||||
type: Boolean,
|
||||
default: null
|
||||
},
|
||||
hasDetailInMsg: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
loading: true,
|
||||
isSubmitting: false
|
||||
isSubmitting: false,
|
||||
clone: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -140,44 +213,77 @@ export default {
|
||||
},
|
||||
iUrl() {
|
||||
return this.getUrl()
|
||||
},
|
||||
iHasSaveContinue() {
|
||||
if (this.hasSaveContinue != null) {
|
||||
return this.hasSaveContinue
|
||||
}
|
||||
return this.method === 'post'
|
||||
},
|
||||
iHasReset() {
|
||||
if (this.hasReset != null) {
|
||||
return this.hasReset
|
||||
}
|
||||
return this.method === 'put'
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$log.debug('Object init is: ', this.object)
|
||||
this.loading = true
|
||||
try {
|
||||
const values = await this.getFormValue()
|
||||
this.$log.debug('Final object is: ', values)
|
||||
this.form = Object.assign(this.form, values)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSubmit(values) {
|
||||
handleSubmit(values, formName, addContinue) {
|
||||
let handler = this.onSubmit || this.defaultOnSubmit
|
||||
handler = handler.bind(this)
|
||||
values = this.cleanFormValue(values)
|
||||
return handler(values)
|
||||
return handler(values, formName, addContinue)
|
||||
},
|
||||
defaultOnSubmit(validValues) {
|
||||
defaultOnSubmit(validValues, formName, addContinue) {
|
||||
this.isSubmitting = true
|
||||
this.performSubmit(validValues)
|
||||
.then((res) => this.onPerformSuccess.bind(this)(res, this.method, this))
|
||||
.then((res) => this.onPerformSuccess.bind(this)(res, this.method, this, addContinue))
|
||||
.catch((error) => this.onPerformError(error, this.method, this))
|
||||
.finally(() => { this.isSubmitting = false })
|
||||
},
|
||||
async getFormValue() {
|
||||
if (this.method !== 'put') {
|
||||
const cloneFrom = this.$route.query['clone_from']
|
||||
if (this.method !== 'put' && !cloneFrom) {
|
||||
return Object.assign(this.form, this.initial)
|
||||
}
|
||||
let object = this.object
|
||||
if (object === null) {
|
||||
object = await this.getObjectDetail()
|
||||
if (!object) {
|
||||
if (cloneFrom) {
|
||||
this.$log.debug('Clone from: ', cloneFrom)
|
||||
const url = `${this.url}${cloneFrom}/`
|
||||
object = await this.getObjectDetail(url)
|
||||
if (object['name']) {
|
||||
object.name = this.$t('common.cloneFrom') + ' ' + object.name
|
||||
} else if (object['hostname']) {
|
||||
object.hostname = this.$t('common.cloneFrom') + ' ' + object.hostname
|
||||
}
|
||||
} else {
|
||||
object = await this.getObjectDetail(this.iUrl)
|
||||
}
|
||||
}
|
||||
if (object) {
|
||||
if (object['attrs']) {
|
||||
object = deepmerge(object, object['attrs'])
|
||||
}
|
||||
this.$log.debug('Object is: ', object)
|
||||
this.$emit('update:object', object)
|
||||
}
|
||||
return object
|
||||
},
|
||||
async getObjectDetail() {
|
||||
return this.$axios.get(this.iUrl)
|
||||
async getObjectDetail(url) {
|
||||
this.$log.debug('Get object detail: ', url)
|
||||
return this.$axios.get(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Page>
|
||||
<Page v-bind="$attrs">
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm v-bind="$attrs" v-on="$listeners" />
|
||||
<GenericCreateUpdateForm ref="createUpdateForm" v-bind="$attrs" v-on="$listeners" />
|
||||
</IBox>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<Page v-bind="$attrs">
|
||||
<ListTable v-bind="$attrs" />
|
||||
<ListTable ref="ListTable" v-bind="$attrs" />
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-if="dialogSetting.dialogVisible"
|
||||
:title="this.$t('common.updateSelected')"
|
||||
:visible.sync="dialogSetting.dialogVisible"
|
||||
width="70%"
|
||||
@@ -99,6 +98,7 @@ export default {
|
||||
const url = this.url
|
||||
const msg = this.updateSuccessMsg
|
||||
this.$axios.patch(url, validValues).then((res) => {
|
||||
vm.$emit('update')
|
||||
this.$message.success(msg)
|
||||
vm.dialogSetting.dialogVisible = false
|
||||
}).catch(error => {
|
||||
|
||||
@@ -8,14 +8,10 @@
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item icon="el-icon-user" command="profile">{{ $t('common.nav.Profile') }}</el-dropdown-item>
|
||||
<div v-if="currentOrgRoles.length > 1 || hasAdminOrg ">
|
||||
<el-dropdown-item v-if="isInAdminRole " icon="el-icon-guide" command="userPage">
|
||||
{{ $t('common.nav.UserPage') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-else icon="el-icon-guide" command="adminPage">
|
||||
{{ $t('common.nav.AdminPage') }}
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
<template v-if="currentOrgRoles.length > 1 || hasAdminOrg ">
|
||||
<el-dropdown-item v-if="isInAdminRole " icon="el-icon-guide" command="userPage">{{ $t('common.nav.UserPage') }}</el-dropdown-item>
|
||||
<el-dropdown-item v-else icon="el-icon-guide" command="adminPage">{{ $t('common.nav.AdminPage') }}</el-dropdown-item>
|
||||
</template>
|
||||
<el-dropdown-item icon="el-icon-key" command="apiKey">{{ $t('common.nav.APIKey') }}</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logout">{{ $t('common.nav.Logout') }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@@ -86,7 +82,7 @@ export default {
|
||||
case 'userPage':
|
||||
if (this.currentOrgUsePagePerm) {
|
||||
this.$store.dispatch('users/setCurrentRole', rolec.USER)
|
||||
console.log('Switch to: ', rolec.USER)
|
||||
// console.log('Switch to: ', rolec.USER)
|
||||
window.location.href = `/ui/`
|
||||
}
|
||||
break
|
||||
@@ -101,7 +97,7 @@ export default {
|
||||
},
|
||||
logout() {
|
||||
// Clean Status
|
||||
const statusList = ['currentOrg', 'currentRole', 'jms_current_org', 'jms_current_role', 'sidebarStatus', 'django_language', 'X-JMS-ORG', 'activeTab']
|
||||
const statusList = ['currentOrg', 'currentRole', 'jms_current_org', 'jms_current_role', 'sidebarStatus', 'X-JMS-ORG', 'activeTab']
|
||||
for (const i in statusList) {
|
||||
this.$cookie.delete(statusList[i])
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<Dialog width="60%" :visible.sync="showDialog" :title="this.$t('setting.ApiKeyList')" :show-cancel="false" :show-confirm="false">
|
||||
<Dialog :destroy-on-close="true" width="60%" :visible.sync="showDialog" :title="this.$t('setting.ApiKeyList')" :show-cancel="false" :show-confirm="false">
|
||||
<div>
|
||||
<el-alert type="success"> {{ helpMessage }} </el-alert>
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
|
||||
@@ -31,25 +31,46 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
supportedLangMapper() {
|
||||
return this.supportLanguages.reduce((map, obj) => {
|
||||
map[obj.code] = obj
|
||||
return map
|
||||
})
|
||||
},
|
||||
currentLang() {
|
||||
const cookieCode = this.$cookie.get(this.LANG_COOKIE_NAME)
|
||||
let lang = this.supportLanguages.find((v) => v.cookieCode === cookieCode)
|
||||
const langCode = this.getLangCode()
|
||||
let lang = this.supportedLangMapper[langCode]
|
||||
if (!lang) {
|
||||
lang = this.supportLanguages[0]
|
||||
this.changeLangTo(lang)
|
||||
}
|
||||
if (lang.code !== this.$i18n.locale) {
|
||||
this.changeLangTo(lang)
|
||||
}
|
||||
return lang
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.currentLang.code !== this.$i18n.locale) {
|
||||
this.changeLangTo(this.currentLang)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeLangTo(item) {
|
||||
this.$i18n.locale = item.code
|
||||
localStorage.setItem('lang', item.code)
|
||||
this.$cookie.set(this.LANG_COOKIE_NAME, item.cookieCode)
|
||||
window.location.reload()
|
||||
},
|
||||
getLangCode() {
|
||||
let langCode = localStorage.lang
|
||||
if (!langCode) {
|
||||
langCode = this.$cookie.get(this.LANG_COOKIE_NAME)
|
||||
}
|
||||
if (!langCode) {
|
||||
langCode = navigator.language || navigator.userLanguage
|
||||
}
|
||||
langCode = langCode.substr(0, 2)
|
||||
langCode = langCode.replace('zh', 'cn')
|
||||
if (langCode) {
|
||||
return langCode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
46
src/layout/components/NavHeader/Tickets.vue
Normal file
46
src/layout/components/NavHeader/Tickets.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <el-link class="el-link" target="_blank" @click="goToTickets">{{ $t('route.Ticket') }}</el-link>-->
|
||||
<el-badge :value="assignedTicketCount" :hidden="assignedTicketCount===0" size="mini" type="primary">
|
||||
<el-link class="el-link" target="_blank" @click="goToTickets">{{ $t('route.Ticket') }}</el-link>
|
||||
</el-badge>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getTicketOpenCount } from '@/api/ticket'
|
||||
|
||||
export default {
|
||||
name: 'WebTerminal',
|
||||
data() {
|
||||
return {
|
||||
assignedTicketCount: 0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.ticketsOpenedCount()
|
||||
},
|
||||
methods: {
|
||||
|
||||
ticketsOpenedCount() {
|
||||
getTicketOpenCount(1).then(data => {
|
||||
this.assignedTicketCount = data.count
|
||||
})
|
||||
},
|
||||
goToTickets() {
|
||||
this.$router.push({ name: 'TicketList' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-link {
|
||||
color: #606266 !important;
|
||||
font-size: 13px;
|
||||
font-weight: 400
|
||||
}
|
||||
.el-badge ::v-deep .el-badge__content.is-fixed{
|
||||
top:10px;
|
||||
}
|
||||
</style>
|
||||
24
src/layout/components/NavHeader/WebTerminal.vue
Normal file
24
src/layout/components/NavHeader/WebTerminal.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<el-link class="el-link" :href="webTerminalUrl" target="_blank">{{ $t('route.WebTerminal') }}</el-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BASE_URL } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'WebTerminal',
|
||||
computed: {
|
||||
webTerminalUrl() {
|
||||
return `${BASE_URL}/luna/?_=${Date.now()}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-link {
|
||||
color: #606266 !important;
|
||||
font-size: 13px;
|
||||
font-weight: 400
|
||||
}
|
||||
</style>
|
||||
@@ -10,6 +10,19 @@
|
||||
<div class="header-item">
|
||||
<Language />
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
publicSettings.TICKETS_ENABLED
|
||||
&& publicSettings.XPACK_LICENSE_IS_VALID
|
||||
&& !isOrgAuditor
|
||||
"
|
||||
class="header-item"
|
||||
>
|
||||
<Tickets />
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<WebTerminal />
|
||||
</div>
|
||||
<div class="header-item header-profile">
|
||||
<AccountDropdown />
|
||||
</div>
|
||||
@@ -23,6 +36,9 @@ import Hamburger from '@/components/Hamburger'
|
||||
import AccountDropdown from './AccountDropdown'
|
||||
import Help from './Help'
|
||||
import Language from './Language'
|
||||
import WebTerminal from './WebTerminal'
|
||||
import Tickets from './Tickets'
|
||||
import rolc from '@/utils/role'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -30,7 +46,9 @@ export default {
|
||||
Hamburger,
|
||||
AccountDropdown,
|
||||
Language,
|
||||
Help
|
||||
Help,
|
||||
Tickets,
|
||||
WebTerminal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -38,8 +56,11 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'sidebar'
|
||||
])
|
||||
'sidebar', 'publicSettings', 'currentOrgRoles'
|
||||
]),
|
||||
isOrgAuditor() {
|
||||
return rolc.getRolesDisplay(this.currentOrgRoles).includes('OrgAuditor') || rolc.getRolesDisplay(this.currentOrgRoles).includes('Auditor')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleSideBar() {
|
||||
|
||||
64
src/layout/components/Page/LicenseExpireTip.vue
Normal file
64
src/layout/components/Page/LicenseExpireTip.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div v-if="!loading">
|
||||
<el-alert v-if="isExpire" type="error">
|
||||
{{ isExpire }}
|
||||
</el-alert>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'LicenseExpireTip',
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
licenseData: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'publicSettings',
|
||||
'currentUser'
|
||||
]),
|
||||
isExpire() {
|
||||
if (!this.publicSettings.XPACK_ENABLED || this.currentUser.role !== 'Admin') {
|
||||
return false
|
||||
}
|
||||
const intervalDays = this.getIntervalDays(this.licenseData.date_expired)
|
||||
if (intervalDays < 0) {
|
||||
return this.$t('setting.LicenseExpired')
|
||||
}
|
||||
if (intervalDays < 7) {
|
||||
return this.$t('setting.LicenseWillBe') + this.licenseData.date_expired + this.$t('setting.Expire')
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.publicSettings.XPACK_ENABLED && this.currentUser.role === 'Admin') {
|
||||
this.$axios.get('/api/v1/xpack/license/detail').then(res => {
|
||||
this.licenseData = res
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
} else {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getIntervalDays(date) {
|
||||
const dateExpired = new Date(toSafeLocalDateStr(date))
|
||||
const dateNow = new Date()
|
||||
const intervalTime = dateExpired.getTime() - dateNow.getTime()
|
||||
return Math.floor(intervalTime / (24 * 3600 * 1000))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,22 +1,34 @@
|
||||
<template>
|
||||
<div class="page-heading">
|
||||
<el-row :gutter="0">
|
||||
<el-col :span="16" class="page-heading-left">
|
||||
<slot><h2>{{ title }}</h2></slot>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="page-heading-right">
|
||||
<slot name="rightSide" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div>
|
||||
<slot name="globalNotification">
|
||||
<LicenseExpireTip />
|
||||
<PasswordExpireTip />
|
||||
</slot>
|
||||
<div class="page-heading">
|
||||
<el-row :gutter="0">
|
||||
<el-col :span="16" class="page-heading-left">
|
||||
<slot><h2>{{ title }}</h2></slot>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="page-heading-right">
|
||||
<slot name="rightSide" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <Breadcrumb />-->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LicenseExpireTip from '@/layout/components/Page/LicenseExpireTip'
|
||||
import PasswordExpireTip from '@/layout/components/Page/PasswordExpireTip'
|
||||
export default {
|
||||
name: 'PageHeading',
|
||||
components: {
|
||||
LicenseExpireTip,
|
||||
PasswordExpireTip
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
|
||||
55
src/layout/components/Page/PasswordExpireTip.vue
Normal file
55
src/layout/components/Page/PasswordExpireTip.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert v-if="isExpire" type="error">
|
||||
{{ isExpire }}
|
||||
</el-alert>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'PasswordExpireTip',
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
securityData: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'publicSettings',
|
||||
'currentUser'
|
||||
]),
|
||||
isExpire() {
|
||||
// 用户来源不是Local时不显示密码过期提示
|
||||
if (this.currentUser.source !== 'local') {
|
||||
return false
|
||||
}
|
||||
const intervalTime = this.getIntervalDays(this.currentUser.date_password_last_updated)
|
||||
const securityPasswordExpirationTime = this.publicSettings.SECURITY_PASSWORD_EXPIRATION_TIME
|
||||
if (intervalTime >= securityPasswordExpirationTime) {
|
||||
return this.$t('users.passwordExpired')
|
||||
}
|
||||
if (securityPasswordExpirationTime - intervalTime <= 5) {
|
||||
return this.$t('users.passwordWillExpiredPrefixMsg') + (securityPasswordExpirationTime - intervalTime) + this.$t('users.passwordWillExpiredSuffixMsg')
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getIntervalDays(date) {
|
||||
const dateExpired = new Date(toSafeLocalDateStr(date))
|
||||
const dateNow = new Date()
|
||||
const intervalTime = dateNow.getTime() - dateExpired.getTime()
|
||||
return Math.floor(intervalTime / (24 * 3600 * 1000))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -9,7 +9,14 @@
|
||||
|
||||
<div>
|
||||
<el-tabs v-if="submenu.length > 0" slot="submenu" v-model="iActiveMenu" class="page-submenu" @tab-click="handleTabClick">
|
||||
<el-tab-pane v-for="item in submenu" :key="item.name" :label="item.title" :label-content="item.labelContent" :name="item.name" />
|
||||
<template v-for="item in submenu">
|
||||
<el-tab-pane :key="item.name" :label-content="item.labelContent" :name="item.name">
|
||||
<span slot="label">
|
||||
{{ item.title }}
|
||||
<slot name="badge" :tab="item.name" />
|
||||
</span>
|
||||
</el-tab-pane>
|
||||
</template>
|
||||
</el-tabs>
|
||||
<transition name="fade-transform" mode="out-in">
|
||||
<slot />
|
||||
|
||||
@@ -77,7 +77,7 @@ export default {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
/deep/ {
|
||||
::v-deep {
|
||||
.el-scrollbar__bar {
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
@@ -1,32 +1,41 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
import empty from '@/layout/empty'
|
||||
|
||||
export default [
|
||||
{
|
||||
path: 'remote-apps',
|
||||
name: 'RemoteAppList',
|
||||
component: () => import('@/views/applications/RemoteApp/RemoteAppList'),
|
||||
meta: { title: i18n.t('route.RemoteApp'), licenseRequired: true }
|
||||
},
|
||||
{
|
||||
path: 'remote-apps/create',
|
||||
name: 'RemoteAppCreate',
|
||||
component: () => import('@/views/applications/RemoteApp/RemoteAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.RemoteApp'), activeMenu: '/applications/remote-apps', action: 'create', licenseRequired: true },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'remote-apps/:id/update',
|
||||
name: 'RemoteAppUpdate',
|
||||
component: () => import('@/views/applications/RemoteApp/RemoteAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.RemoteAppUpdate'), activeMenu: '/applications/remote-apps', action: 'update', licenseRequired: true },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'remote-apps/:id',
|
||||
name: 'RemoteAppDetail',
|
||||
component: () => import('@/views/applications/RemoteApp/RemoteAppDetail/index'),
|
||||
meta: { title: i18n.t('route.RemoteAppDetail'), activeMenu: '/applications/remote-apps', licenseRequired: true },
|
||||
hidden: true
|
||||
redirect: '',
|
||||
component: empty,
|
||||
meta: { title: i18n.t('route.RemoteApp'), licenseRequired: true },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'RemoteAppList',
|
||||
meta: { title: i18n.t('route.RemoteApp'), activeMenu: '/applications/remote-apps' },
|
||||
component: () => import('@/views/applications/RemoteApp/RemoteAppList')
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
name: 'RemoteAppCreate',
|
||||
component: () => import('@/views/applications/RemoteApp/RemoteAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.RemoteApp'), activeMenu: '/applications/remote-apps', action: 'create' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
name: 'RemoteAppUpdate',
|
||||
component: () => import('@/views/applications/RemoteApp/RemoteAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.RemoteAppUpdate'), activeMenu: '/applications/remote-apps', action: 'update' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
name: 'RemoteAppDetail',
|
||||
component: () => import('@/views/applications/RemoteApp/RemoteAppDetail/index'),
|
||||
meta: { title: i18n.t('route.RemoteAppDetail'), activeMenu: '/applications/remote-apps' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'database-apps',
|
||||
@@ -54,5 +63,32 @@ export default [
|
||||
component: () => import('@/views/applications/DatabaseApp/DatabaseAppDetail/index'),
|
||||
meta: { title: i18n.t('route.DatabaseAppDetail'), activeMenu: '/applications/database-apps' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'kubernetes-apps',
|
||||
name: 'KubernetesAppList',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppList'),
|
||||
meta: { title: i18n.t('route.KubernetesApp') }
|
||||
},
|
||||
{
|
||||
path: 'kubernetes-apps/create',
|
||||
name: 'KubernetesAppCreate',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.KubernetesAppCreate'), activeMenu: '/applications/kubernetes-apps', action: 'create' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'kubernetes-apps/:id/update',
|
||||
name: 'KubernetesAppUpdate',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.KubernetesAppUpdate'), activeMenu: '/applications/kubernetes-apps', action: 'update' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'kubernetes-apps/:id',
|
||||
name: 'KubernetesAppDetail',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppDetail/index'),
|
||||
meta: { title: i18n.t('route.KubernetesAppDetail'), activeMenu: '/applications/kubernetes-apps' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -12,7 +12,7 @@ export default [
|
||||
path: '',
|
||||
name: 'AssetList',
|
||||
component: () => import('@/views/assets/Asset/AssetList.vue'),
|
||||
meta: { title: i18n.t('route.AssetList') }
|
||||
meta: { title: i18n.t('route.AssetList'), activeMenu: '/assets/assets' }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
@@ -34,6 +34,13 @@ export default [
|
||||
component: () => import('@/views/assets/Asset/AssetCreateUpdate.vue'),
|
||||
meta: { title: i18n.t('route.AssetUpdate'), activeMenu: '/assets/assets' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'detail/:id/update',
|
||||
name: 'AssetMoreInformationEdit',
|
||||
component: () => import('@/views/assets/Asset/AssetMoreInformationEdit.vue'),
|
||||
meta: { title: i18n.t('common.UpdateAssetDetail'), activeMenu: '/assets/assets' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -47,7 +54,7 @@ export default [
|
||||
path: '',
|
||||
name: 'DomainList',
|
||||
component: () => import('@/views/assets/Domain/DomainList.vue'),
|
||||
meta: { title: i18n.t('route.DomainList') }
|
||||
meta: { title: i18n.t('route.DomainList'), activeMenu: '/assets/domains' }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
@@ -104,7 +111,7 @@ export default [
|
||||
path: '',
|
||||
name: 'AdminUserList',
|
||||
component: () => import('@/views/assets/AdminUser/AdminUserList'),
|
||||
meta: { title: i18n.t('route.AdminUserList') }
|
||||
meta: { title: i18n.t('route.AdminUserList'), activeMenu: '/assets/admin-users' }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
@@ -174,7 +181,7 @@ export default [
|
||||
path: '',
|
||||
name: 'CommandFilterList',
|
||||
component: () => import('@/views/assets/CommandFilter/CommandFilterList.vue'),
|
||||
meta: { title: i18n.t('route.CommandFilterList') }
|
||||
meta: { title: i18n.t('route.CommandFilterList'), activeMenu: '/assets/cmd-filters' }
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
@@ -265,7 +272,7 @@ export default [
|
||||
path: '',
|
||||
name: 'LabelList',
|
||||
component: () => import('@/views/assets/Label/LabelList.vue'),
|
||||
meta: { title: i18n.t('route.LabelList') }
|
||||
meta: { title: i18n.t('route.LabelList'), activeMenu: '/assets/labels' }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
|
||||
@@ -105,6 +105,7 @@ export const allRoleRoutes = [
|
||||
component: Layout,
|
||||
redirect: '/applications/remote-apps/',
|
||||
name: 'applications',
|
||||
alwaysShow: true,
|
||||
meta: { title: i18n.t('route.Applications'), icon: 'th' },
|
||||
children: ApplicationsRoute
|
||||
},
|
||||
@@ -129,6 +130,7 @@ export const allRoleRoutes = [
|
||||
component: Layout,
|
||||
redirect: '/ops/tasks/',
|
||||
name: 'JobCenter',
|
||||
alwaysShow: true,
|
||||
meta: { title: i18n.t('route.JobCenter'), icon: 'coffee' },
|
||||
children: OpsRoutes
|
||||
},
|
||||
@@ -138,8 +140,12 @@ export const allRoleRoutes = [
|
||||
component: Layout,
|
||||
redirect: '/tickets/tickets/',
|
||||
children: TicketsRoutes,
|
||||
hidden: true,
|
||||
meta: {
|
||||
licenseRequired: true
|
||||
// hidden: ({ settings }) => {
|
||||
// return !settings.TICKETS_ENABLED
|
||||
// }
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
import rolec from '@/utils/role'
|
||||
import { BASE_URL } from '@/utils/common'
|
||||
|
||||
export default [
|
||||
{
|
||||
@@ -32,7 +34,7 @@ export default [
|
||||
path: 'command-executions/create',
|
||||
name: 'BatchCommand',
|
||||
component: () => import('@/views/ops/CommandExecution'),
|
||||
meta: { title: i18n.t('route.BatchCommand'), commandExecutionRequired: true }
|
||||
meta: { title: i18n.t('route.BatchCommand'), hidden: ({ settings }) => !settings.SECURITY_COMMAND_EXECUTION }
|
||||
},
|
||||
// {
|
||||
// path: 'celery/task/:id',
|
||||
@@ -42,9 +44,9 @@ export default [
|
||||
// meta: { title: i18n.t('route.CeleryTaskLog') }
|
||||
// },
|
||||
{
|
||||
path: 'task/monitor',
|
||||
path: `${BASE_URL}/core/flower?_=${Date.now()}`,
|
||||
name: 'TaskMonitor',
|
||||
component: () => window.open(`/core/flower?_=${Date.now()}`),
|
||||
meta: { title: i18n.t('route.TaskMonitor') }
|
||||
// component: () => window.open(`/core/flower?_=${Date.now()}`),
|
||||
meta: { title: i18n.t('route.TaskMonitor'), permissions: [rolec.PERM_SUPER] }
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
import empty from '@/layout/empty'
|
||||
|
||||
const assetPermissionRoutes = [
|
||||
{
|
||||
@@ -30,68 +31,45 @@ const assetPermissionRoutes = [
|
||||
}
|
||||
]
|
||||
|
||||
const remoteAppPermissionRoutes = [
|
||||
const ApplicationPermissionRoutes = [
|
||||
{
|
||||
path: 'remote-app-permissions',
|
||||
name: 'RemoteAppPermissionList',
|
||||
component: () => import('@/views/perms/RemoteAppPermission/RemoteAppPermissionList'),
|
||||
meta: { title: i18n.t('route.RemoteAppPermission') }
|
||||
},
|
||||
{
|
||||
path: 'remote-app-permissions/create',
|
||||
component: () => import('@/views/perms/RemoteAppPermission/RemoteAppPermissionCreateUpdate'),
|
||||
name: 'RemoteAppPermissionCreate',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.RemoteAppPermissionCreate'), activeMenu: '/perms/remote-app-permissions', action: 'create' }
|
||||
},
|
||||
{
|
||||
path: 'remote-app-permissions/update',
|
||||
component: () => import('@/views/perms/RemoteAppPermission/RemoteAppPermissionCreateUpdate'),
|
||||
name: 'RemoteAppPermissionUpdate',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.RemoteAppPermissionUpdate'), activeMenu: '/perms/remote-app-permissions', action: 'update' }
|
||||
},
|
||||
{
|
||||
path: 'remote-app-permissions/:id',
|
||||
component: () => import('@/views/perms/RemoteAppPermission/RemoteAppPermissionDetail/index'),
|
||||
name: 'RemoteAppPermissionDetail',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.RemoteAppPermissionDetail'), activeMenu: '/perms/remote-app-permissions' }
|
||||
path: 'app-permissions',
|
||||
component: empty,
|
||||
meta: { title: i18n.t('route.ApplicationPermission') },
|
||||
redirect: '',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'ApplicationPermissionList',
|
||||
component: () => import('@/views/perms/ApplicationPermission/ApplicationPermissionList'),
|
||||
meta: { title: i18n.t('route.ApplicationPermission'), activeMenu: '/perms/app-permissions' }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/perms/ApplicationPermission/ApplicationPermissionCreateUpdate'),
|
||||
name: 'ApplicationPermissionCreate',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.ApplicationPermissionCreate'), activeMenu: '/perms/app-permissions', action: 'create' }
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: () => import('@/views/perms/ApplicationPermission/ApplicationPermissionDetail/index'),
|
||||
name: 'ApplicationPermissionDetail',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.ApplicationPermissionDetail'), activeMenu: '/perms/remote-app-permissions' }
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/perms/ApplicationPermission/ApplicationPermissionCreateUpdate'),
|
||||
name: 'ApplicationPermissionUpdate',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.ApplicationPermissionUpdate'), activeMenu: '/perms/app-permissions', action: 'update' }
|
||||
}]
|
||||
}
|
||||
]
|
||||
|
||||
const databasePermissionRoutes = [
|
||||
{
|
||||
path: 'database-app-permissions',
|
||||
name: 'DatabaseAppPermissionList',
|
||||
component: () => import('@/views/perms/DatabaseAppPermission/DatabaseAppPermissionList'),
|
||||
meta: { title: i18n.t('route.DatabaseAppPermission') }
|
||||
},
|
||||
{
|
||||
path: 'database-app-permissions/create',
|
||||
component: () => import('@/views/perms/DatabaseAppPermission/DatabaseAppPermissionCreateUpdate'), // Parent router-view
|
||||
name: 'DatabaseAppPermissionCreate',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.DatabaseAppPermissionCreate'), activeMenu: '/perms/database-app-permissions' }
|
||||
},
|
||||
{
|
||||
path: 'database-app-permissions/update',
|
||||
component: () => import('@/views/perms/DatabaseAppPermission/DatabaseAppPermissionCreateUpdate'), // Parent router-view
|
||||
name: 'DatabaseAppPermissionUpdate',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.DatabaseAppPermissionUpdate'), activeMenu: '/perms/database-app-permissions', action: 'update' }
|
||||
},
|
||||
{
|
||||
path: 'database-app-permissions/:id',
|
||||
component: () => import('@/views/perms/DatabaseAppPermission/DatabaseAppPermissionDetail/index'),
|
||||
name: 'DatabaseAppPermissionDetail',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('route.DatabaseAppPermissionDetail'), activeMenu: '/perms/database-app-permissions' }
|
||||
}
|
||||
]
|
||||
|
||||
export default [
|
||||
... assetPermissionRoutes,
|
||||
... remoteAppPermissionRoutes,
|
||||
... databasePermissionRoutes
|
||||
... ApplicationPermissionRoutes
|
||||
]
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
import rolec from '@/utils/role'
|
||||
import empty from '@/layout/empty'
|
||||
import { BASE_URL } from '@/utils/common'
|
||||
|
||||
export default [
|
||||
{
|
||||
path: 'session',
|
||||
@@ -21,70 +24,79 @@ export default [
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'luna',
|
||||
path: `${BASE_URL}/luna/?_=${Date.now()}`,
|
||||
name: 'WebTerminal',
|
||||
component: () => window.open(`/luna/?_=${Date.now()}`),
|
||||
// component: () => window.open(`/luna/?_=${Date.now()}`),
|
||||
meta: { title: i18n.t('route.WebTerminal') }
|
||||
// hidden: true
|
||||
},
|
||||
{
|
||||
path: 'sftp',
|
||||
path: `${BASE_URL}/koko/elfinder/sftp/?`,
|
||||
name: 'FileManager',
|
||||
component: () => window.open(`/koko/elfinder/sftp/?`),
|
||||
// component: () => window.open(`/koko/elfinder/sftp/?`),
|
||||
meta: { title: i18n.t('route.FileManager') }
|
||||
},
|
||||
{
|
||||
path: 'terminal',
|
||||
name: 'TerminalList',
|
||||
component: () => import('@/views/sessions/TerminalList'),
|
||||
meta: { title: i18n.t('route.Terminal') }
|
||||
},
|
||||
{
|
||||
path: 'terminals/:id',
|
||||
name: 'TerminalDetail',
|
||||
component: () => import('@/views/sessions/TerminalDetail'),
|
||||
meta: { title: i18n.t('route.Terminal'), activeMenu: '/terminal/terminal' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'terminals/:id/update',
|
||||
name: 'TerminalUpdate',
|
||||
component: () => import('@/views/sessions/TerminalUpdate'),
|
||||
meta: { title: i18n.t('route.Terminal'), activeMenu: '/terminal/terminal' },
|
||||
hidden: true
|
||||
component: empty,
|
||||
redirect: '',
|
||||
meta: { title: i18n.t('route.Terminal'), permissions: [rolec.PERM_SUPER] },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'TerminalList',
|
||||
component: () => import('@/views/sessions/TerminalList'),
|
||||
meta: { title: i18n.t('route.Terminal') }
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
name: 'TerminalDetail',
|
||||
component: () => import('@/views/sessions/TerminalDetail'),
|
||||
meta: { title: i18n.t('route.Terminal'), activeMenu: '/terminal/terminal' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
name: 'TerminalUpdate',
|
||||
component: () => import('@/views/sessions/TerminalUpdate'),
|
||||
meta: { title: i18n.t('route.Terminal'), activeMenu: '/terminal/terminal' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'storages',
|
||||
name: 'Storage',
|
||||
component: () => import('@/views/sessions/Storage/index'),
|
||||
meta: { activeMenu: '/terminal/terminal' },
|
||||
meta: { activeMenu: '/terminal/terminal', permissions: [rolec.PERM_SUPER] },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'replay-storage/create',
|
||||
name: 'CreateReplayStorage',
|
||||
component: () => import('@/views/sessions/ReplayStorageCreateUpdate'),
|
||||
meta: { title: i18n.t('route.CreateReplayStorage'), activeMenu: '/terminal/terminal' },
|
||||
meta: { title: i18n.t('route.CreateReplayStorage'), activeMenu: '/terminal/terminal', permissions: [rolec.PERM_SUPER] },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'command-storage/create',
|
||||
name: 'CreateCommandStorage',
|
||||
component: () => import('@/views/sessions/CommandStorageCreateUpdate'),
|
||||
meta: { title: i18n.t('route.CreateCommandStorage'), activeMenu: '/terminal/terminal' },
|
||||
meta: { title: i18n.t('route.CreateCommandStorage'), activeMenu: '/terminal/terminal', permissions: [rolec.PERM_SUPER] },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'replay-storage/:id/update',
|
||||
name: 'ReplayStorageUpdate',
|
||||
component: () => import('@/views/sessions/ReplayStorageCreateUpdate'),
|
||||
meta: { title: i18n.t('route.ReplayStorageUpdate'), activeMenu: '/terminal/terminal' },
|
||||
meta: { title: i18n.t('route.ReplayStorageUpdate'), activeMenu: '/terminal/terminal', permissions: [rolec.PERM_SUPER] },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'command-storage/:id/update',
|
||||
name: 'CommandStorageUpdate',
|
||||
component: () => import('@/views/sessions/CommandStorageCreateUpdate'),
|
||||
meta: { title: i18n.t('route.CommandStorageUpdate'), activeMenu: '/terminal/terminal' },
|
||||
meta: { title: i18n.t('route.CommandStorageUpdate'), activeMenu: '/terminal/terminal', permissions: [rolec.PERM_SUPER] },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -4,7 +4,8 @@ export default [
|
||||
path: 'tickets',
|
||||
name: 'TicketList',
|
||||
component: () => import('@/views/tickets/TicketList'),
|
||||
meta: { title: i18n.t('route.Tickets'), icon: 'check-square-o' }
|
||||
meta: { title: i18n.t('route.Tickets'), icon: 'check-square-o', activeMenu: '/tickets/tickets' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tickets/:id',
|
||||
@@ -12,5 +13,19 @@ export default [
|
||||
component: () => import('@/views/tickets/TicketDetail/index'),
|
||||
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tickets/request-asset-perm/create',
|
||||
name: 'RequestAssetPermTicketCreateUpdate',
|
||||
component: () => import('@/views/tickets/RequestAssetPerm/RequestAssetPermTicketCreateUpdate'),
|
||||
meta: { title: i18n.t('route.TicketCreate'), activeMenu: '/tickets/tickets' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tickets/request-asset-perm/:id',
|
||||
name: 'AssetsTicketDetail',
|
||||
component: () => import('@/views/tickets/RequestAssetPerm/Detail/index'),
|
||||
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import Layout from '@/layout/index'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import rolec from '@/utils/role'
|
||||
|
||||
const scheme = document.location.protocol
|
||||
const port = document.location.port ? ':' + document.location.port : ''
|
||||
const URL = scheme + '//' + document.location.hostname + port
|
||||
import { BASE_URL } from '@/utils/common'
|
||||
|
||||
export default [
|
||||
// 404 page must be placed at the end !!!
|
||||
@@ -33,19 +30,26 @@ export default [
|
||||
name: 'Apps',
|
||||
component: Layout,
|
||||
redirect: '/apps/remoteapp',
|
||||
alwaysShow: true,
|
||||
meta: { title: i18n.t('route.MyApps'), icon: 'th', permissions: [rolec.PERM_USE] },
|
||||
children: [
|
||||
{
|
||||
path: '/apps/remoteapp',
|
||||
name: 'MyRemoteApps',
|
||||
component: () => import('@/userviews/apps/RemoteApp'),
|
||||
meta: { title: i18n.t('route.RemoteApp'), permissions: [rolec.PERM_USE] }
|
||||
meta: { title: i18n.t('route.RemoteApp'), permissions: [rolec.PERM_USE], licenseRequired: true }
|
||||
},
|
||||
{
|
||||
path: '/apps/database',
|
||||
name: 'MyDatebases',
|
||||
component: () => import('@/userviews/apps/DatabaseApp'),
|
||||
meta: { title: i18n.t('route.DatabaseApp'), permissions: [rolec.PERM_USE] }
|
||||
},
|
||||
{
|
||||
path: '/apps/kubernetes',
|
||||
name: 'MyKubernetes',
|
||||
component: () => import('@/userviews/apps/KubernetesApp'),
|
||||
meta: { title: i18n.t('route.KubernetesApp'), permissions: [rolec.PERM_USE] }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -54,7 +58,7 @@ export default [
|
||||
component: Layout,
|
||||
meta: {
|
||||
permissions: [rolec.PERM_USE],
|
||||
commandExecutionRequired: true
|
||||
hidden: ({ settings }) => !settings.SECURITY_COMMAND_EXECUTION
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -65,6 +69,49 @@ export default [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/tickets',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('route.Tickets'),
|
||||
icon: 'history',
|
||||
permissions: [rolec.PERM_USE],
|
||||
licenseRequired: true,
|
||||
hidden: ({ settings }) => {
|
||||
return !settings.TICKETS_ENABLED
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'TicketList',
|
||||
component: () => import('@/views/tickets/TicketList'),
|
||||
meta: { title: i18n.t('route.Tickets'), icon: 'check-square-o', activeMenu: '/tickets', permissions: [rolec.PERM_USE] }
|
||||
},
|
||||
{
|
||||
path: 'tickets/request-asset-perm/create',
|
||||
name: 'RequestAssetPermTicketCreateUpdate',
|
||||
component: () => import('@/views/tickets/RequestAssetPerm/RequestAssetPermTicketCreateUpdate'),
|
||||
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets', permissions: [rolec.PERM_USE] },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tickets/request-asset-perm/:id',
|
||||
name: 'AssetsTicketDetail',
|
||||
component: () => import('@/views/tickets/RequestAssetPerm/Detail/index'),
|
||||
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets', permissions: [rolec.PERM_USE] },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tickets/:id',
|
||||
name: 'TicketDetail',
|
||||
component: () => import('@/views/tickets/TicketDetail/index'),
|
||||
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets', permissions: [rolec.PERM_USE] },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: `external-luna`,
|
||||
component: Layout,
|
||||
@@ -73,7 +120,7 @@ export default [
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: `${URL}/luna/`,
|
||||
path: `${BASE_URL}/luna/`,
|
||||
meta: { title: i18n.t('route.WebTerminal'), icon: 'window-maximize', activeMenu: '/assets', permissions: [rolec.PERM_USE] }
|
||||
}
|
||||
]
|
||||
@@ -86,7 +133,7 @@ export default [
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: `${URL}/koko/elfinder/sftp/`,
|
||||
path: `${BASE_URL}/koko/elfinder/sftp/`,
|
||||
meta: { title: i18n.t('route.WebFTP'), icon: 'file', activeMenu: '/assets', permissions: [rolec.PERM_USE] }
|
||||
}
|
||||
]
|
||||
|
||||
@@ -17,38 +17,26 @@ function hasPermission(roles, route) {
|
||||
// console.log('Has route permission: ', route.path, requirePermsSum, userRolesSum, ' => ', has, roles)
|
||||
return has
|
||||
}
|
||||
function hasLicense(licState, route) {
|
||||
if (licState) {
|
||||
return licState
|
||||
|
||||
function hasLicense(route, rootState) {
|
||||
const licenseIsValid = rootState.settings.publicSettings.XPACK_LICENSE_IS_VALID
|
||||
const licenseRequired = route.meta ? route.meta.licenseRequired : false
|
||||
if (!licenseIsValid && licenseRequired) {
|
||||
return false
|
||||
}
|
||||
let requireLic = route.meta ? route.meta.licenseRequired : null
|
||||
if (!requireLic) {
|
||||
requireLic = false
|
||||
}
|
||||
return licState === requireLic
|
||||
return true
|
||||
}
|
||||
|
||||
function hasCommand(cmdState, route) {
|
||||
if (cmdState) {
|
||||
return cmdState
|
||||
}
|
||||
let requireCmd = route.meta ? route.meta.commandExecutionRequired : null
|
||||
if (!requireCmd) {
|
||||
requireCmd = false
|
||||
}
|
||||
return cmdState === requireCmd
|
||||
}
|
||||
|
||||
export function filterLicRoutes(routes, roles) {
|
||||
export function filterLicenseRequiredRoutes(routes, rootState) {
|
||||
const res = []
|
||||
|
||||
routes.forEach(route => {
|
||||
const tmp = {
|
||||
...route
|
||||
}
|
||||
if (hasLicense(roles, tmp)) {
|
||||
if (hasLicense(route, rootState)) {
|
||||
if (tmp.children) {
|
||||
tmp.children = filterLicRoutes(tmp.children, roles)
|
||||
tmp.children = filterLicenseRequiredRoutes(tmp.children, rootState)
|
||||
}
|
||||
res.push(tmp)
|
||||
}
|
||||
@@ -57,16 +45,24 @@ export function filterLicRoutes(routes, roles) {
|
||||
return res
|
||||
}
|
||||
|
||||
export function filterCmdRoutes(routes, roles) {
|
||||
function isNeedHidden(route, rootState) {
|
||||
let hidden = route.meta ? route.meta.hidden : false
|
||||
if (typeof hidden === 'function') {
|
||||
hidden = hidden({ route: route, settings: rootState.settings.publicSettings })
|
||||
}
|
||||
return hidden
|
||||
}
|
||||
|
||||
export function filterHiddenRoutes(routes, rootState) {
|
||||
const res = []
|
||||
|
||||
routes.forEach(route => {
|
||||
const tmp = {
|
||||
...route
|
||||
}
|
||||
if (hasCommand(roles, tmp)) {
|
||||
if (!isNeedHidden(route, rootState)) {
|
||||
if (tmp.children) {
|
||||
tmp.children = filterCmdRoutes(tmp.children, roles)
|
||||
tmp.children = filterHiddenRoutes(tmp.children, rootState)
|
||||
}
|
||||
res.push(tmp)
|
||||
}
|
||||
@@ -83,18 +79,18 @@ export function filterCmdRoutes(routes, roles) {
|
||||
export function filterAsyncRoutes(routes, roles) {
|
||||
const res = []
|
||||
|
||||
routes.forEach(route => {
|
||||
for (const route of routes) {
|
||||
const tmp = {
|
||||
...route
|
||||
}
|
||||
|
||||
if (hasPermission(roles, tmp)) {
|
||||
if (tmp.children) {
|
||||
tmp.children = filterAsyncRoutes(tmp.children, roles)
|
||||
}
|
||||
res.push(tmp)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -113,10 +109,12 @@ const mutations = {
|
||||
const actions = {
|
||||
generateRoutes({ commit, rootState }, roles) {
|
||||
return new Promise(resolve => {
|
||||
console.log(rootState)
|
||||
let accessedRoutes = filterAsyncRoutes(allRoleRoutes, roles)
|
||||
accessedRoutes = filterCmdRoutes(accessedRoutes, rootState.settings.publicSettings.SECURITY_COMMAND_EXECUTION)
|
||||
accessedRoutes = filterLicRoutes(accessedRoutes, rootState.settings.publicSettings.XPACK_LICENSE_IS_VALID)
|
||||
accessedRoutes = filterHiddenRoutes(accessedRoutes, rootState)
|
||||
accessedRoutes = filterLicenseRequiredRoutes(accessedRoutes, rootState)
|
||||
if (accessedRoutes.length === 0) {
|
||||
// console.log('No route find')
|
||||
}
|
||||
commit('SET_ROUTES', accessedRoutes)
|
||||
resolve(accessedRoutes)
|
||||
})
|
||||
|
||||
@@ -35,6 +35,10 @@ const actions = {
|
||||
link.rel = 'shortcut icon'
|
||||
link.href = response.data.LOGO_URLS.favicon
|
||||
document.getElementsByTagName('head')[0].appendChild(link)
|
||||
|
||||
// 动态修改Title
|
||||
if (response.data.LOGIN_TITLE) { document.title = response.data.LOGIN_TITLE }
|
||||
|
||||
commit('SET_PUBLIC_SETTINGS', response.data)
|
||||
resolve(response)
|
||||
}).catch(error => {
|
||||
|
||||
@@ -37,6 +37,16 @@ const mutations = {
|
||||
SET_ORGS: (state, orgs) => {
|
||||
state.orgs = orgs
|
||||
},
|
||||
MODIFY_ORG: (state, org) => {
|
||||
// console.log(state.orgs)
|
||||
state.orgs = state.orgs.map(oldOrg => {
|
||||
if (oldOrg.id === org.id) {
|
||||
oldOrg.name = org.name
|
||||
}
|
||||
return oldOrg
|
||||
}
|
||||
)
|
||||
},
|
||||
ADD_ORG: (state, org) => {
|
||||
state.orgs.push(org)
|
||||
},
|
||||
@@ -89,7 +99,7 @@ const actions = {
|
||||
commit('SET_PROFILE', response)
|
||||
resolve(response)
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
// console.log(error)
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
@@ -125,6 +135,9 @@ const actions = {
|
||||
addAdminOrg({ commit, state }, org) {
|
||||
commit('ADD_ORG', org)
|
||||
},
|
||||
modifyOrg({ commit, state }, org) {
|
||||
commit('MODIFY_ORG', org)
|
||||
},
|
||||
// user logout
|
||||
logout({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -96,7 +96,7 @@ td .el-button.el-button--mini {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.el-button.el-button--default:focus:not(.is-disabled), .el-button.el-button--default:hover:not(.is-disabled) {
|
||||
.el-button.el-button--default:hover:not(.is-disabled) {
|
||||
color: #606266;
|
||||
border-color: #d2d2d2;
|
||||
background-color: #e6e6e6;
|
||||
@@ -214,6 +214,10 @@ td .el-button.el-button--mini {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.is-disabled:hover{
|
||||
color:#c0c4cc;
|
||||
}
|
||||
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after {
|
||||
color: $--color-primary;
|
||||
}
|
||||
@@ -431,7 +435,7 @@ a {
|
||||
|
||||
.el-button--danger.is-plain {
|
||||
color: $--color-danger;
|
||||
background: white;
|
||||
background: #ffffff;
|
||||
border-color: $--color-danger;
|
||||
}
|
||||
|
||||
|
||||
@@ -148,3 +148,12 @@ input[type=file] {
|
||||
font-size: 12px;
|
||||
opacity: 80;
|
||||
}
|
||||
|
||||
.el-table__body {
|
||||
width: 100%;
|
||||
table-layout: fixed !important;
|
||||
}
|
||||
|
||||
.el-table__column-filter-trigger i {
|
||||
color: #888888 !important;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ li.is-active {
|
||||
// line-height: 30px !important;
|
||||
//}
|
||||
////重置字体大小 菜单宽度
|
||||
//.el-submenu /deep/ .el-submenu__title, .submenu-title-noDropdown{
|
||||
//.el-submenu ::v-deep .el-submenu__title, .submenu-title-noDropdown{
|
||||
// height: 46px !important;
|
||||
// line-height: 46px !important;
|
||||
//}
|
||||
|
||||
@@ -21,7 +21,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: `/api/v1/perms/users/database-apps/`,
|
||||
url: `/api/v1/perms/users/applications/?category=db`,
|
||||
columns: [
|
||||
{
|
||||
prop: 'name',
|
||||
@@ -30,18 +30,19 @@ export default {
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
prop: 'get_type_display',
|
||||
prop: 'type_display',
|
||||
align: 'center',
|
||||
label: this.$t('assets.Type')
|
||||
},
|
||||
{
|
||||
prop: 'database',
|
||||
prop: 'attrs.database',
|
||||
align: 'center',
|
||||
label: this.$t('assets.Database')
|
||||
},
|
||||
{
|
||||
prop: 'comment',
|
||||
align: 'center',
|
||||
showOverflowTooltip: true,
|
||||
label: this.$t('assets.Comment')
|
||||
},
|
||||
{
|
||||
@@ -52,6 +53,7 @@ export default {
|
||||
formatterArgs: {
|
||||
hasDelete: false,
|
||||
hasUpdate: false,
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'connect',
|
||||
@@ -69,13 +71,13 @@ export default {
|
||||
headerActions: {
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasRefresh: false,
|
||||
hasRefresh: true,
|
||||
hasCreate: false,
|
||||
hasBulkDelete: false,
|
||||
hasBulkUpdate: false,
|
||||
hasLeftActions: false,
|
||||
hasSearch: true,
|
||||
hasRightActions: false
|
||||
hasRightActions: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
96
src/userviews/apps/KubernetesApp.vue
Normal file
96
src/userviews/apps/KubernetesApp.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<Page>
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
</Page>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import Page from '@/layout/components/Page/index'
|
||||
import { ActionsFormatter } from '@/components/ListTable/formatters'
|
||||
|
||||
export default {
|
||||
name: 'KubernetesApp',
|
||||
components: {
|
||||
ListTable,
|
||||
Page
|
||||
},
|
||||
props: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: `/api/v1/perms/users/applications/?category=cloud`,
|
||||
columns: [
|
||||
{
|
||||
prop: 'name',
|
||||
align: 'center',
|
||||
label: this.$t('assets.Name'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
prop: 'type_display',
|
||||
align: 'center',
|
||||
label: this.$t('assets.Type')
|
||||
},
|
||||
{
|
||||
prop: 'attrs.cluster',
|
||||
align: 'center',
|
||||
label: this.$t('applications.cluster')
|
||||
},
|
||||
{
|
||||
prop: 'comment',
|
||||
align: 'center',
|
||||
showOverflowTooltip: true,
|
||||
label: this.$t('assets.Comment')
|
||||
},
|
||||
{
|
||||
prop: 'id',
|
||||
align: 'center',
|
||||
label: this.$t('assets.Action'),
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasDelete: false,
|
||||
hasUpdate: false,
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'connect',
|
||||
fa: 'fa-terminal',
|
||||
type: 'primary',
|
||||
callback: function({ row, col, cellValue, reload }) {
|
||||
window.open(`/luna/?type=k8s_app&login_to=${cellValue}`, '_blank')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
headerActions: {
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasRefresh: true,
|
||||
hasCreate: false,
|
||||
hasBulkDelete: false,
|
||||
hasBulkUpdate: false,
|
||||
hasLeftActions: false,
|
||||
hasSearch: true,
|
||||
hasRightActions: true
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -20,7 +20,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: `/api/v1/perms/users/remote-apps/`,
|
||||
url: `/api/v1/perms/users/applications/?category=remote_app`,
|
||||
columns: [
|
||||
{
|
||||
prop: 'name',
|
||||
@@ -29,18 +29,19 @@ export default {
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
prop: 'get_type_display',
|
||||
prop: 'type_display',
|
||||
align: 'center',
|
||||
label: this.$t('assets.RemoteType')
|
||||
label: this.$t('assets.Type')
|
||||
},
|
||||
{
|
||||
prop: 'asset_info.hostname',
|
||||
prop: 'attrs.asset_info.hostname',
|
||||
align: 'center',
|
||||
label: this.$t('assets.Asset')
|
||||
},
|
||||
{
|
||||
prop: 'comment',
|
||||
align: 'center',
|
||||
showOverflowTooltip: true,
|
||||
label: this.$t('assets.Comment')
|
||||
},
|
||||
{
|
||||
@@ -51,6 +52,7 @@ export default {
|
||||
formatterArgs: {
|
||||
hasDelete: false,
|
||||
hasUpdate: false,
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'connect',
|
||||
@@ -68,13 +70,13 @@ export default {
|
||||
headerActions: {
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasRefresh: false,
|
||||
hasRefresh: true,
|
||||
hasCreate: false,
|
||||
hasBulkDelete: false,
|
||||
hasBulkUpdate: false,
|
||||
hasLeftActions: false,
|
||||
hasSearch: true,
|
||||
hasRightActions: false
|
||||
hasRightActions: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
return {
|
||||
allFavorites: [],
|
||||
treeSetting: {
|
||||
showMenu: true,
|
||||
showMenu: false,
|
||||
showRefresh: true,
|
||||
showAssets: false,
|
||||
url: '/api/v1/perms/users/assets/',
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
formatter: DialogDetailFormatter,
|
||||
showOverflowTooltip: true,
|
||||
formatterArgs: {
|
||||
getDialogTile: function({ col, row, cellValue }) { this.$t('assets.AssetDetail') }.bind(this),
|
||||
getDialogTitle: function({ col, row, cellValue }) { this.$t('assets.AssetDetail') }.bind(this),
|
||||
getDetailItems: function({ col, row, cellValue }) {
|
||||
return [
|
||||
{
|
||||
@@ -60,8 +60,12 @@ export default {
|
||||
value: row.platform
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Domain'),
|
||||
value: row.domain
|
||||
key: this.$t('common.Activate'),
|
||||
value: row.is_active
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Comment'),
|
||||
value: row.comment
|
||||
}
|
||||
]
|
||||
}.bind(this)
|
||||
@@ -86,6 +90,12 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'comment',
|
||||
label: this.$t('assets.Comment'),
|
||||
showOverflowTooltip: true,
|
||||
width: '180px'
|
||||
},
|
||||
{
|
||||
prop: 'id',
|
||||
align: 'center',
|
||||
@@ -95,12 +105,16 @@ export default {
|
||||
formatterArgs: {
|
||||
hasDelete: false,
|
||||
loading: true,
|
||||
hasClone: false,
|
||||
hasUpdate: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'connect',
|
||||
fa: 'fa-terminal',
|
||||
type: 'primary',
|
||||
can: (row, cellValue) => {
|
||||
return row.is_active
|
||||
},
|
||||
callback: function({ row, col, cellValue, reload }) {
|
||||
window.open(`/luna/?login_to=${cellValue}`, '_blank')
|
||||
}
|
||||
@@ -121,7 +135,12 @@ export default {
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
tableAttrs: {
|
||||
rowClassName({ row }) {
|
||||
return !row.is_active ? 'row_disabled' : ''
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasExport: false,
|
||||
@@ -136,10 +155,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
refreshAllFavorites() {
|
||||
this.tableConfig.columns[3].formatterArgs.loading = true
|
||||
this.tableConfig.columns[4].formatterArgs.loading = true
|
||||
this.$axios.get('/api/v1/assets/favorite-assets/').then(resp => {
|
||||
this.allFavorites = resp
|
||||
this.tableConfig.columns[3].formatterArgs.loading = false
|
||||
this.tableConfig.columns[4].formatterArgs.loading = false
|
||||
})
|
||||
},
|
||||
addOrDeleteFavorite(assetId) {
|
||||
@@ -172,4 +191,11 @@ export default {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-card {
|
||||
border: 0 !important;
|
||||
}
|
||||
.row_disabled,.row_disabled:hover,.row_disabled:hover > td{
|
||||
cursor: not-allowed;
|
||||
background-color:rgba(192,196,204,0.28) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
:update-success-next-route="updateSuccessNextRoute"
|
||||
:clean-form-value="cleanFormValue"
|
||||
:get-method="getMethod"
|
||||
:on-perform-success="onPerformSuccess"
|
||||
:perform-submit="performSubmit"
|
||||
/>
|
||||
</IBox>
|
||||
</template>
|
||||
@@ -15,6 +17,7 @@
|
||||
<script>
|
||||
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm/index'
|
||||
import { IBox } from '@/components'
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
|
||||
export default {
|
||||
name: 'ProfileUpdate',
|
||||
@@ -68,9 +71,7 @@ export default {
|
||||
label: this.$t('users.IAgree'),
|
||||
type: 'checkbox',
|
||||
checked: false,
|
||||
rules: [
|
||||
{ required: true }
|
||||
],
|
||||
rules: [Required],
|
||||
helpText: this.$t('users.HelpText.MFAOfUserFirstLoginUserGuidePage')
|
||||
}
|
||||
},
|
||||
@@ -87,6 +88,17 @@ export default {
|
||||
methods: {
|
||||
getMethod() {
|
||||
return 'put'
|
||||
},
|
||||
performSubmit(validValues) {
|
||||
if (!validValues.terms) {
|
||||
this.$message.error(this.$t('common.PleaseAgreeToTheTerms'))
|
||||
return Promise.reject()
|
||||
}
|
||||
return this.$axios['put'](this.url, validValues)
|
||||
},
|
||||
onPerformSuccess() {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
setTimeout(() => this.$router.push({ name: 'UserGuide' }), 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
:url="url"
|
||||
:get-method="getMethod"
|
||||
class="password-update"
|
||||
:update-success-next-route="updateSuccessNextRoute"
|
||||
/>
|
||||
</IBox>
|
||||
</template>
|
||||
@@ -50,6 +51,9 @@ export default {
|
||||
type: 'password'
|
||||
}
|
||||
}
|
||||
},
|
||||
updateSuccessNextRoute: {
|
||||
path: '/'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<QuickActions type="primary" :actions="quickActions" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>a
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import DetailCard from '@/components/DetailCard'
|
||||
@@ -54,12 +54,39 @@ export default {
|
||||
type: 'primary',
|
||||
label: this.$t('common.Update')
|
||||
},
|
||||
has: this.object.mfa_enabled,
|
||||
callbacks: {
|
||||
click: function() {
|
||||
window.location.href = `/core/auth/profile/otp/update/?next=${this.$route.fullPath}`
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('users.UpdatePassword'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('common.Update'),
|
||||
disabled: this.$store.state.users.profile.source !== 'local'
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$emit('update:activeMenu', 'PasswordUpdate')
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('users.UpdateSSHKey'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('common.Update'),
|
||||
disabled: this.$store.state.users.profile.source !== 'local'
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$emit('update:activeMenu', 'SSHUpdate')
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('users.ResetPublicKeyAndDownload'),
|
||||
attrs: {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm
|
||||
ref="GenericCreateUpdateForm"
|
||||
:fields="fields"
|
||||
:fields-meta="fieldsMeta"
|
||||
:initial="object"
|
||||
:url="url"
|
||||
:get-method="getMethod"
|
||||
:more-buttons="moreButtons"
|
||||
:on-perform-success="onPerformSuccess"
|
||||
/>
|
||||
</IBox>
|
||||
</template>
|
||||
@@ -46,7 +48,8 @@ export default {
|
||||
public_key: {
|
||||
el: {
|
||||
type: 'textarea',
|
||||
placeholder: 'ssh-rsa AAAA...'
|
||||
placeholder: 'ssh-rsa AAAA...',
|
||||
autosize: { minRows: 3 }
|
||||
},
|
||||
helpText: this.$t('users.HelpText.SSHKeyOfProfileSSHUpdatePage')
|
||||
}
|
||||
@@ -64,6 +67,10 @@ export default {
|
||||
methods: {
|
||||
getMethod() {
|
||||
return 'put'
|
||||
},
|
||||
onPerformSuccess() {
|
||||
this.$refs.GenericCreateUpdateForm.$refs.form.$refs.dataForm.resetForm('form')
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<GenericDetailPage :object.sync="user" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" :object="user" />
|
||||
<component :is="config.activeMenu" :object="user" @update:activeMenu="handleUpdate" />
|
||||
</keep-alive>
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
@@ -60,6 +60,9 @@ export default {
|
||||
])
|
||||
}
|
||||
return submenu
|
||||
},
|
||||
handleUpdate(value) {
|
||||
this.config.activeMenu = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export function getCurrentRoleFromCookie() {
|
||||
}
|
||||
|
||||
export function saveCurrentRoleToCookie(role) {
|
||||
console.log('Save current role to cookie: ', role)
|
||||
// console.log('Save current role to cookie: ', role)
|
||||
return VueCookie.set(CURRENT_ROLE_KEY, role, 14)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export function getCurrentOrgFromCookie() {
|
||||
try {
|
||||
org = JSON.parse(VueCookie.get(CURRENT_ORG_KEY))
|
||||
} catch (e) {
|
||||
console.log('Current org in cookie: ', org)
|
||||
// console.log('Current org in cookie: ', org)
|
||||
}
|
||||
return org
|
||||
}
|
||||
|
||||
@@ -65,12 +65,17 @@ function cleanDateStr(d) {
|
||||
case 1:
|
||||
d = d.split('+')[0].trimRight()
|
||||
break
|
||||
case 2:
|
||||
d = d.replace(/-/g, '/')
|
||||
}
|
||||
}
|
||||
return null
|
||||
return d
|
||||
}
|
||||
|
||||
export function toSafeLocalDateStr(d) {
|
||||
if (d === '') {
|
||||
return ''
|
||||
}
|
||||
const date = safeDate(d)
|
||||
// let date_s = date.toLocaleString(getUserLang(), {hour12: false});
|
||||
const date_s = date.toLocaleString(getUserLang(), { hourCycle: 'h23' })
|
||||
@@ -136,6 +141,20 @@ export function getDaysAgo(days, now) {
|
||||
return new Date(now.getTime() - 3600 * 1000 * 24 * days)
|
||||
}
|
||||
|
||||
export function getDaysFuture(days, now) {
|
||||
if (!now) {
|
||||
now = new Date()
|
||||
}
|
||||
return new Date(now.getTime() + 3600 * 1000 * 24 * days)
|
||||
}
|
||||
|
||||
export function getDayEnd(now) {
|
||||
if (!now) {
|
||||
now = new Date()
|
||||
}
|
||||
return new Date(new Date(now.toLocaleDateString()).getTime() + 24 * 60 * 60 * 1000 - 1)
|
||||
}
|
||||
|
||||
export function setUrlParam(url, name, value) {
|
||||
const urlArray = url.split('?')
|
||||
if (urlArray.length === 1) {
|
||||
@@ -157,3 +176,16 @@ export function setUrlParam(url, name, value) {
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
export function getDayFuture(days, now) {
|
||||
if (!now) {
|
||||
now = new Date()
|
||||
}
|
||||
return new Date(now.getTime() + 3600 * 1000 * 24 * days)
|
||||
}
|
||||
|
||||
const scheme = document.location.protocol
|
||||
const port = document.location.port ? ':' + document.location.port : ''
|
||||
const BASE_URL = scheme + '//' + document.location.hostname + port
|
||||
|
||||
export { BASE_URL }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { hasUUID } from '@/utils/common'
|
||||
import { hasUUID, BASE_URL } from '@/utils/common'
|
||||
import store from '@/store'
|
||||
|
||||
function getPropOrg() {
|
||||
@@ -36,17 +36,20 @@ function hasCurrentOrgPermission() {
|
||||
return orgInList
|
||||
}
|
||||
|
||||
function changeOrg(orgId) {
|
||||
async function changeOrg(orgId) {
|
||||
const org = getOrgIdMapper()[orgId]
|
||||
if (!org) {
|
||||
console.debug('Error: org not found')
|
||||
} else {
|
||||
console.debug('Change to org: ', org)
|
||||
}
|
||||
// 重置Role为空
|
||||
await store.dispatch('users/setCurrentRole', null)
|
||||
|
||||
store.dispatch('users/setCurrentOrg', org).then(() => {
|
||||
console.log('Set current org to: ', org)
|
||||
// console.log('Set current org to: ', org)
|
||||
if (hasUUID(location.href)) {
|
||||
location.href = process.env.VUE_APP_PUBLIC_PATH
|
||||
location.href = BASE_URL
|
||||
} else {
|
||||
window.location.reload(true)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import axios from 'axios'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import NProgress from 'nprogress' // progress bar
|
||||
|
||||
import { getTokenFromCookie } from '@/utils/auth'
|
||||
import { refreshSessionIdAge } from '@/api/users'
|
||||
import { Message, MessageBox } from 'element-ui'
|
||||
import store from '@/store'
|
||||
import axiosRetry from 'axios-retry'
|
||||
|
||||
// create an axios instance
|
||||
const service = axios.create({
|
||||
@@ -13,11 +14,12 @@ const service = axios.create({
|
||||
})
|
||||
|
||||
function beforeRequestAddToken(config) {
|
||||
if (store.getters.token) {
|
||||
const csrfToken = getTokenFromCookie()
|
||||
if (csrfToken) {
|
||||
// let each request carry token
|
||||
// ['X-Token'] is a custom headers key
|
||||
// please modify it according to the actual situation
|
||||
config.headers['X-CSRFToken'] = store.getters.token
|
||||
config.headers['X-CSRFToken'] = csrfToken
|
||||
}
|
||||
if (store.getters.currentOrg) {
|
||||
config.headers['X-JMS-ORG'] = store.getters.currentOrg.id
|
||||
@@ -36,7 +38,7 @@ function beforeRequestAddTimezone(config) {
|
||||
service.interceptors.request.use(
|
||||
config => {
|
||||
// do something before request is sent
|
||||
NProgress.start()
|
||||
// NProgress.start()
|
||||
beforeRequestAddToken(config)
|
||||
beforeRequestAddTimezone(config)
|
||||
return config
|
||||
@@ -49,9 +51,12 @@ service.interceptors.request.use(
|
||||
)
|
||||
|
||||
function ifUnauthorized({ response, error }) {
|
||||
if (response.status === 401 && response.request.url.indexOf('/users/profile') !== -1) {
|
||||
if (response.status === 401) {
|
||||
response.config.disableFlashErrorMsg = true
|
||||
// 未授权重定向到登录页面
|
||||
if (response.request.responseURL.indexOf('/users/profile/') !== -1) {
|
||||
window.location = '/core/auth/login/'
|
||||
return
|
||||
}
|
||||
const title = i18n.t('common.Info')
|
||||
const msg = i18n.t('auth.LoginRequiredMsg')
|
||||
MessageBox.confirm(msg, title, {
|
||||
@@ -68,6 +73,13 @@ function ifBadRequest({ response, error }) {
|
||||
if (response.status === 400) {
|
||||
error.message = i18n.t('common.BadRequestErrorMsg')
|
||||
}
|
||||
if (response.status === 403) {
|
||||
error.message = i18n.t('common.BadRoleErrorMsg')
|
||||
}
|
||||
if (response.status === 409) {
|
||||
error.response.status = 409
|
||||
error.message = i18n.t('common.BadConflictErrorMsg')
|
||||
}
|
||||
}
|
||||
|
||||
export function flashErrorMsg({ response, error }) {
|
||||
@@ -85,6 +97,19 @@ export function flashErrorMsg({ response, error }) {
|
||||
}
|
||||
}
|
||||
|
||||
let timer = null
|
||||
function refreshSessionAgeDelay(response) {
|
||||
if (response.request.responseURL.indexOf('/users/profile/') !== -1) {
|
||||
return
|
||||
}
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
timer = setTimeout(function() {
|
||||
refreshSessionIdAge()
|
||||
}, 60 * 10 * 1000)
|
||||
}
|
||||
|
||||
// response interceptor
|
||||
service.interceptors.response.use(
|
||||
/**
|
||||
@@ -98,7 +123,8 @@ service.interceptors.response.use(
|
||||
* You can also judge the status by HTTP Status Code
|
||||
*/
|
||||
response => {
|
||||
NProgress.done()
|
||||
// NProgress.done()
|
||||
refreshSessionAgeDelay(response)
|
||||
const res = response.data
|
||||
|
||||
if (response.config.raw === 1) {
|
||||
@@ -107,7 +133,7 @@ service.interceptors.response.use(
|
||||
return res
|
||||
},
|
||||
error => {
|
||||
NProgress.done()
|
||||
// NProgress.done()
|
||||
if (!error.response) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
@@ -120,4 +146,9 @@ service.interceptors.response.use(
|
||||
}
|
||||
)
|
||||
|
||||
axiosRetry(service, {
|
||||
// 默认不开启请求重试
|
||||
retries: 0
|
||||
})
|
||||
|
||||
export default service
|
||||
|
||||
@@ -7,9 +7,9 @@ const PERM_SUPER = 0b10000000
|
||||
const PERM_NONE = 0b00000000
|
||||
|
||||
const SUPER_ADMIN = PERM_SUPER | PERM_ADMIN | PERM_AUDIT
|
||||
const SUPER_AUDITOR = PERM_SUPER | PERM_AUDIT
|
||||
const SUPER_AUDITOR = PERM_SUPER | PERM_AUDIT | PERM_USE
|
||||
const ORG_ADMIN = PERM_ADMIN | PERM_AUDIT
|
||||
const ORG_AUDITOR = PERM_AUDIT
|
||||
const ORG_AUDITOR = PERM_AUDIT | PERM_USE
|
||||
const USER = PERM_USE
|
||||
const ANON = PERM_NONE
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ async function changeCurrentOrgIfNeed({ to, from, next }) {
|
||||
}
|
||||
const currentOrg = store.getters.currentOrg
|
||||
if (!currentOrg || typeof currentOrg !== 'object') {
|
||||
console.log('Not has current org')
|
||||
// console.log('Not has current org')
|
||||
orgUtil.change2PropOrg()
|
||||
return reject('change prop org')
|
||||
}
|
||||
|
||||
@@ -10,10 +10,11 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'type']],
|
||||
[this.$t('applications.mysql'), ['host', 'port', 'database']],
|
||||
[this.$t('common.Others'), ['comment']]
|
||||
[this.$t('common.Basic'), ['name', 'type', 'domain']],
|
||||
[this.$t('applications.DBInfo'), ['attrs']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
type: {
|
||||
@@ -23,14 +24,56 @@ export default {
|
||||
value: 'mysql'
|
||||
}],
|
||||
disabled: true
|
||||
},
|
||||
host: {
|
||||
type: 'input'
|
||||
},
|
||||
domain: {
|
||||
el: {
|
||||
multiple: false,
|
||||
clearable: true,
|
||||
ajax: {
|
||||
url: '/api/v1/assets/domains/'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
url: '/api/v1/applications/database-apps/'
|
||||
url: '/api/v1/applications/applications/',
|
||||
getUrl() {
|
||||
const params = this.$route.params
|
||||
let url = `/api/v1/applications/applications/`
|
||||
const method = this.getMethod()
|
||||
if (params.id) {
|
||||
url = `${url}${params.id}/`
|
||||
}
|
||||
return method === 'post' ? `${url}?type=${this.$route.query.type}` : `${url}?category=db`
|
||||
},
|
||||
performSubmit(validValues) {
|
||||
const params = this.$route.params
|
||||
const baseUrl = `/api/v1/applications/applications/`
|
||||
const url = (params.id) ? `${baseUrl}${params.id}/` : baseUrl
|
||||
const method = this.getMethod()
|
||||
validValues.attrs = {
|
||||
host: validValues.host,
|
||||
port: validValues.port,
|
||||
database: validValues.database
|
||||
}
|
||||
validValues.category = 'db'
|
||||
return this.$axios[method](`${url}?type=${validValues.type}`, validValues)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
initial() {
|
||||
return this.$route.query
|
||||
},
|
||||
getMethod() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,19 +33,19 @@ export default {
|
||||
},
|
||||
{
|
||||
key: this.$t('applications.type'),
|
||||
value: this.object.get_type_display
|
||||
value: this.object.type_display
|
||||
},
|
||||
{
|
||||
key: this.$t('applications.host'),
|
||||
value: this.object.host
|
||||
value: this.object.attrs.host
|
||||
},
|
||||
{
|
||||
key: this.$t('applications.port'),
|
||||
value: JSON.stringify(this.object.port)
|
||||
value: JSON.stringify(this.object.attrs.port)
|
||||
},
|
||||
{
|
||||
key: this.$t('applications.database'),
|
||||
value: this.object.database
|
||||
value: this.object.attrs.database
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateCreated'),
|
||||
|
||||
@@ -19,7 +19,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
DatabaseApp: {
|
||||
name: '', get_type_display: '', host: '', port: '', database: '', date_created: '', created_by: '', comment: ''
|
||||
name: '', get_type_display: '', host: '', port: '', database: '', date_created: '', created_by: '', comment: '', attrs: ''
|
||||
},
|
||||
config: {
|
||||
activeMenu: 'DatabaseAppDetail',
|
||||
@@ -28,7 +28,11 @@ export default {
|
||||
title: this.$t('route.DatabaseAppDetail'),
|
||||
name: 'DatabaseAppDetail'
|
||||
}
|
||||
]
|
||||
],
|
||||
actions: {
|
||||
detailApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
|
||||
deleteApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<GenericListPage ref="GenericListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -12,18 +13,49 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/applications/database-apps/',
|
||||
url: '/api/v1/applications/applications/?category=db',
|
||||
columns: [
|
||||
'name', 'get_type_display', 'host', 'port', 'database', 'comment', 'actions'
|
||||
'name', 'type_display', 'attrs.host', 'attrs.port', 'attrs.database', 'comment', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
get_type_display: {
|
||||
label: this.$t('applications.type')
|
||||
type_display: {
|
||||
label: this.$t('applications.type'),
|
||||
width: '120px'
|
||||
},
|
||||
'attrs.host': {
|
||||
label: this.$t('applications.host'),
|
||||
width: '140px'
|
||||
},
|
||||
'attrs.port': {
|
||||
label: this.$t('applications.port'),
|
||||
width: '80px'
|
||||
},
|
||||
'attrs.database': {
|
||||
label: this.$t('applications.database'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
actions: {
|
||||
prop: '',
|
||||
formatterArgs: {
|
||||
hasClone: true,
|
||||
performDelete: function({ row, col, cellValue, reload }) {
|
||||
this.$axios.delete(
|
||||
`/api/v1/applications/applications/${row.id}/`
|
||||
).then(res => {
|
||||
this.$refs.GenericListTable.$refs.ListTable.reloadTable()
|
||||
// this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$t('common.deleteErrorMsg' + ' ' + error))
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasCreate: false,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasBulkDelete: false,
|
||||
createRoute: 'DatabaseAppCreate',
|
||||
moreActionsTitle: this.$t('common.Create'),
|
||||
@@ -33,16 +65,55 @@ export default {
|
||||
name: 'MySQL',
|
||||
title: 'MySQL',
|
||||
type: 'primary',
|
||||
can: true,
|
||||
has: true,
|
||||
callback: this.createMysql.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'PostgreSQL',
|
||||
title: 'PostgreSQL',
|
||||
type: 'primary',
|
||||
has: this.isValidateLicense,
|
||||
callback: this.createPostgreSQL.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'MariaDB',
|
||||
title: 'MariaDB',
|
||||
type: 'primary',
|
||||
has: this.isValidateLicense,
|
||||
callback: this.createMariaDB.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'Oracle',
|
||||
title: 'Oracle',
|
||||
type: 'primary',
|
||||
has: this.isValidateLicense,
|
||||
callback: this.createOracle.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['publicSettings', 'currentOrg'])
|
||||
},
|
||||
methods: {
|
||||
createMysql() {
|
||||
this.$router.push({ name: 'DatabaseAppCreate', query: { type: 'mysql' }})
|
||||
},
|
||||
createPostgreSQL() {
|
||||
this.$router.push({ name: 'DatabaseAppCreate', query: { type: 'postgresql' }})
|
||||
},
|
||||
createMariaDB() {
|
||||
this.$router.push({ name: 'DatabaseAppCreate', query: { type: 'mariadb' }})
|
||||
},
|
||||
createOracle() {
|
||||
this.$router.push({ name: 'DatabaseAppCreate', query: { type: 'oracle' }})
|
||||
},
|
||||
isValidateLicense() {
|
||||
if (this.publicSettings.XPACK_ENABLED) {
|
||||
return this.publicSettings.XPACK_LICENSE_IS_VALID
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage v-bind="$data" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initial: {
|
||||
type: 'k8s'
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'type', 'domain']],
|
||||
[this.$t('applications.kubernetes'), ['attrs']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
type: {
|
||||
disabled: true
|
||||
},
|
||||
cluster: {
|
||||
helpText: this.$t('applications.clusterHelpTextMessage')
|
||||
},
|
||||
domain: {
|
||||
el: {
|
||||
multiple: false,
|
||||
clearable: true,
|
||||
ajax: {
|
||||
url: '/api/v1/assets/domains/'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
url: '/api/v1/applications/applications/',
|
||||
getUrl() {
|
||||
const params = this.$route.params
|
||||
let url = `/api/v1/applications/applications/`
|
||||
if (params.id) {
|
||||
url = `${url}${params.id}/`
|
||||
}
|
||||
return `${url}?type=k8s`
|
||||
},
|
||||
performSubmit(validValues) {
|
||||
const params = this.$route.params
|
||||
const baseUrl = `/api/v1/applications/applications/`
|
||||
const url = (params.id) ? `${baseUrl}${params.id}/` : baseUrl
|
||||
const method = this.getMethod()
|
||||
validValues.attrs = {
|
||||
cluster: validValues.cluster
|
||||
}
|
||||
validValues.category = 'cloud'
|
||||
return this.$axios[method](`${url}?type=${validValues.type}`, validValues)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getMethod() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user