mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-13 19:35:24 +00:00
Compare commits
278 Commits
v3.10.0
...
pr@v3@perf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf094cbb88 | ||
|
|
66532f4d4b | ||
|
|
42f27eb30f | ||
|
|
57920bf771 | ||
|
|
290772f44e | ||
|
|
f140f2f59e | ||
|
|
7b1883e012 | ||
|
|
352ac7e828 | ||
|
|
1cbd58664c | ||
|
|
e48da6be9b | ||
|
|
fa31b36550 | ||
|
|
6b93a6563d | ||
|
|
d561701049 | ||
|
|
edbf477c1e | ||
|
|
6a2578b339 | ||
|
|
2cb7569cb0 | ||
|
|
9e0c623b9a | ||
|
|
40bf040501 | ||
|
|
efee7c7bbf | ||
|
|
5daecb84ae | ||
|
|
3be325214d | ||
|
|
581509f42f | ||
|
|
654b36b064 | ||
|
|
dcec73ae67 | ||
|
|
00bafa8164 | ||
|
|
da09af79a7 | ||
|
|
b596815ea5 | ||
|
|
cb37273e80 | ||
|
|
c5bf7d0ad2 | ||
|
|
c31195a67a | ||
|
|
1eb59b379a | ||
|
|
b7cee17156 | ||
|
|
f1c8874010 | ||
|
|
5ccaa3b77d | ||
|
|
27d3637330 | ||
|
|
9c8ceb04f0 | ||
|
|
ccd7b319c8 | ||
|
|
55637c7fa1 | ||
|
|
4e95c88318 | ||
|
|
1ff49ca16d | ||
|
|
91c44d0500 | ||
|
|
0b3a9844f7 | ||
|
|
95b58f3c96 | ||
|
|
128b9c79ba | ||
|
|
4eda83f83d | ||
|
|
4cd0071054 | ||
|
|
67a2a9be6a | ||
|
|
f927a2a3cc | ||
|
|
ca40cb34da | ||
|
|
d725e5497d | ||
|
|
56f6c17275 | ||
|
|
e1bde89b29 | ||
|
|
e9da168c9f | ||
|
|
c19ef24ec9 | ||
|
|
fb7c4a8b2a | ||
|
|
428ba49f9c | ||
|
|
7602d6e270 | ||
|
|
7b62ce2d33 | ||
|
|
6ed40c45b0 | ||
|
|
31238e0398 | ||
|
|
676ac2bbf6 | ||
|
|
d5415b84c9 | ||
|
|
5e91917ba4 | ||
|
|
c4361b4c17 | ||
|
|
70b5ec3683 | ||
|
|
c93a061852 | ||
|
|
4351d20a1e | ||
|
|
ce23d53e3c | ||
|
|
32fc16126f | ||
|
|
b9a99148e3 | ||
|
|
2a4b99484b | ||
|
|
bd26894135 | ||
|
|
01e55d7f6e | ||
|
|
d0a7201683 | ||
|
|
e2dcc98ab3 | ||
|
|
8dd4f89395 | ||
|
|
33a997f3bb | ||
|
|
1adee78456 | ||
|
|
baf2b6cd9b | ||
|
|
28e756e163 | ||
|
|
251873a7e9 | ||
|
|
ff9bd42322 | ||
|
|
2c245020cd | ||
|
|
429d2fec40 | ||
|
|
c2f8fe45a1 | ||
|
|
caa5e7df75 | ||
|
|
8a439dec8d | ||
|
|
eabf5a117d | ||
|
|
c30672a014 | ||
|
|
5c5df32181 | ||
|
|
ce831df717 | ||
|
|
04688930ad | ||
|
|
ae5787ae52 | ||
|
|
1dfdfe3932 | ||
|
|
f409abbf79 | ||
|
|
a6232da3d0 | ||
|
|
2690538db6 | ||
|
|
6d0af7a149 | ||
|
|
babc048eb0 | ||
|
|
d49be903e8 | ||
|
|
332058b0ea | ||
|
|
e355abc1af | ||
|
|
99200d58bb | ||
|
|
61bb97efa9 | ||
|
|
c5e030e2fe | ||
|
|
8a60ad774f | ||
|
|
b08e6de527 | ||
|
|
a96851686f | ||
|
|
1b0adbfe71 | ||
|
|
c28419438b | ||
|
|
8807f24bcd | ||
|
|
b8a914eb02 | ||
|
|
8bbc66a281 | ||
|
|
19bab778ca | ||
|
|
134bcda895 | ||
|
|
8b725fa4f6 | ||
|
|
205f8bc280 | ||
|
|
4963446b74 | ||
|
|
98be3903db | ||
|
|
2cb7a859a8 | ||
|
|
d51571c530 | ||
|
|
bf0d19ac0b | ||
|
|
28062f5d60 | ||
|
|
720dee578a | ||
|
|
cc0d78a4e7 | ||
|
|
9f0b904043 | ||
|
|
90cbb25d47 | ||
|
|
450d9562c3 | ||
|
|
a69eec5ef6 | ||
|
|
5f6fc7e3b4 | ||
|
|
62b6ca026e | ||
|
|
006f258938 | ||
|
|
756b7db6b6 | ||
|
|
a8d7c01f94 | ||
|
|
0c7e7ecc99 | ||
|
|
342a70c441 | ||
|
|
815247f5b5 | ||
|
|
e6721a9905 | ||
|
|
d6305fddfd | ||
|
|
6db2d5ae31 | ||
|
|
9b43bba6f8 | ||
|
|
f9d89b30e2 | ||
|
|
be29794b4c | ||
|
|
97bf4f1b97 | ||
|
|
76e7684e26 | ||
|
|
2bfd764da7 | ||
|
|
daf3defe14 | ||
|
|
d8c165ca78 | ||
|
|
42115b2c30 | ||
|
|
447013abf5 | ||
|
|
44216326f9 | ||
|
|
fd64e71a3b | ||
|
|
7b990a264f | ||
|
|
7687917aae | ||
|
|
02b71619de | ||
|
|
da90b23f99 | ||
|
|
b8090abcdc | ||
|
|
45b07dd628 | ||
|
|
ad7a6a5579 | ||
|
|
67c8521d5c | ||
|
|
18fc63b8c0 | ||
|
|
64d2854e49 | ||
|
|
99f83a6bc4 | ||
|
|
ca9f90624e | ||
|
|
05edf98514 | ||
|
|
45b8c622bc | ||
|
|
faf1fb60b7 | ||
|
|
f28825ba74 | ||
|
|
3626fd024d | ||
|
|
79ec0610a8 | ||
|
|
eee5889d51 | ||
|
|
038ec49d5e | ||
|
|
669ffc73fd | ||
|
|
278562e855 | ||
|
|
fafa088a5e | ||
|
|
1ff65a2293 | ||
|
|
1c69c61432 | ||
|
|
046870e366 | ||
|
|
b44af12f3b | ||
|
|
d9a9c7e229 | ||
|
|
55dfa5889b | ||
|
|
b35b5bd774 | ||
|
|
1ee9e5df78 | ||
|
|
2fe6cb37e6 | ||
|
|
da570e21ee | ||
|
|
16adcd299a | ||
|
|
df8a464c36 | ||
|
|
52e121cfdb | ||
|
|
f014fc6426 | ||
|
|
88a6c2bb2b | ||
|
|
8fa31fe0c2 | ||
|
|
8f246c18e1 | ||
|
|
9d999a7119 | ||
|
|
dc8f237fec | ||
|
|
41d0615ab5 | ||
|
|
fadc3e7dd0 | ||
|
|
152f56b496 | ||
|
|
ed4f8dea90 | ||
|
|
dced020a20 | ||
|
|
bf3c87575c | ||
|
|
dadb54090c | ||
|
|
3f683b012c | ||
|
|
ecb1e91136 | ||
|
|
454947f08b | ||
|
|
3ff6c6fe2f | ||
|
|
527cc4d727 | ||
|
|
3b4201d2bf | ||
|
|
ba109da324 | ||
|
|
2a92c7657c | ||
|
|
beb8ace5bd | ||
|
|
5e1225524c | ||
|
|
931042eb2f | ||
|
|
383577bb18 | ||
|
|
f9e94386de | ||
|
|
5879eed926 | ||
|
|
e1e54bf7a3 | ||
|
|
9e0c43589d | ||
|
|
ca3b0cfce5 | ||
|
|
245b3f4ad2 | ||
|
|
ea575e0515 | ||
|
|
ff63d2ca39 | ||
|
|
045af27999 | ||
|
|
257365932c | ||
|
|
aca4e4077f | ||
|
|
ce80d36b8b | ||
|
|
7dd5256303 | ||
|
|
fcf1093b4c | ||
|
|
5f9e9afffb | ||
|
|
1bba9980c2 | ||
|
|
6cbcee7656 | ||
|
|
2084c50f95 | ||
|
|
20d98bf09e | ||
|
|
05c2f1f859 | ||
|
|
1e9107ec4a | ||
|
|
96a3f0a334 | ||
|
|
6938299940 | ||
|
|
0d1eb82fca | ||
|
|
ddf268e8ec | ||
|
|
ae6fb878da | ||
|
|
d7099c118b | ||
|
|
1b73591366 | ||
|
|
6f3f66df73 | ||
|
|
598020a89b | ||
|
|
88486e2b00 | ||
|
|
faf8521c91 | ||
|
|
ccd74fb76f | ||
|
|
d76c6fdbd8 | ||
|
|
af6a55d3f4 | ||
|
|
c052961efe | ||
|
|
57d339f513 | ||
|
|
097771175d | ||
|
|
0922557abc | ||
|
|
6842da1960 | ||
|
|
eb839b4113 | ||
|
|
7e3e8fbf2f | ||
|
|
3800151763 | ||
|
|
0121505f28 | ||
|
|
b9a6f5d3ac | ||
|
|
179b568b16 | ||
|
|
c563697efd | ||
|
|
fa9281aa92 | ||
|
|
cc8d94f666 | ||
|
|
1416405644 | ||
|
|
d29b3effbc | ||
|
|
bd9456ba2d | ||
|
|
0c9d5d9b6b | ||
|
|
b19ddd6799 | ||
|
|
ea48b6ebf3 | ||
|
|
fba2f77874 | ||
|
|
3c900ce387 | ||
|
|
7df11b907f | ||
|
|
cdd51a9c16 | ||
|
|
51a4c013d3 | ||
|
|
111aafb4bb | ||
|
|
81f0b13730 | ||
|
|
a36a9e7645 | ||
|
|
cfe6db6ec5 | ||
|
|
ccb221559a |
@@ -10,3 +10,4 @@ jobs:
|
||||
- uses: jumpserver/action-generic-handler@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PRIVATE_TOKEN }}
|
||||
I18N_TOKEN: ${{ secrets.I18N_TOKEN }}
|
||||
|
||||
10
package.json
10
package.json
@@ -20,20 +20,22 @@
|
||||
"vue-i18n-report": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json'",
|
||||
"vue-i18n-report-json": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -o /tmp/abc.json",
|
||||
"vue-i18n-report-add-miss": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -a",
|
||||
"diff-i18n": "python ./src/i18n/langs/i18n-util.py diff en ja",
|
||||
"apply-i18n": "python ./src/i18n/langs/i18n-util.py apply en ja"
|
||||
"diff-i18n": "python ./src/i18n/langs/i18n-util.py diff en ja zh_Hant",
|
||||
"apply-i18n": "python ./src/i18n/langs/i18n-util.py apply en ja zh_Hant"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
||||
"@traptitech/markdown-it-katex": "^3.6.0",
|
||||
"@ztree/ztree_v3": "3.5.44",
|
||||
"axios": "0.21.1",
|
||||
"axios": "0.28.0",
|
||||
"axios-retry": "^3.1.9",
|
||||
"cron-parser": "^4.0.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"css-color-function": "^1.3.3",
|
||||
"decimal.js": "^10.4.3",
|
||||
"deepmerge": "^4.2.2",
|
||||
"echarts": "^4.7.0",
|
||||
"dompurify": "^3.1.6",
|
||||
"echarts": "4.7.0",
|
||||
"element-ui": "2.13.2",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
if(pathname.indexOf('/ui') === -1) {
|
||||
window.location.href = window.location.origin + '/ui/#' + pathname
|
||||
}
|
||||
if (pathname.startsWith('/ui/#/chat')) {
|
||||
window.location.href = window.location.origin + pathname
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -53,12 +53,21 @@ export function createJob(form) {
|
||||
})
|
||||
}
|
||||
|
||||
export function StopJob(form) {
|
||||
return request({
|
||||
url: '/api/v1/ops/job-executions/stop/',
|
||||
method: 'post',
|
||||
data: form
|
||||
})
|
||||
}
|
||||
|
||||
export function JobUploadFile(form) {
|
||||
return request({
|
||||
url: '/api/v1/ops/jobs/upload/',
|
||||
method: 'post',
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
timeout: 10 * 60 * 1000,
|
||||
timeout: 60 * 60 * 1000,
|
||||
data: form
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ export function getProfile(token) {
|
||||
return request({
|
||||
url: '/api/v1/users/profile/',
|
||||
method: 'get'
|
||||
// params: { token }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
191
src/components/Apps/AccountCreateUpdateForm/const.js
Normal file
191
src/components/Apps/AccountCreateUpdateForm/const.js
Normal file
@@ -0,0 +1,191 @@
|
||||
import { UpdateToken, UploadSecret } from '@/components/Form/FormFields'
|
||||
import Select2 from '@/components/Form/FormFields/Select2.vue'
|
||||
import AssetSelect from '@/components/Apps/AssetSelect/index.vue'
|
||||
import { Required, RequiredChange } from '@/components/Form/DataForm/rules'
|
||||
import AutomationParamsForm from '@/views/assets/Platform/AutomationParamsSetting.vue'
|
||||
|
||||
export const accountFieldsMeta = (vm) => {
|
||||
const defaultPrivilegedAccounts = ['root', 'administrator']
|
||||
return {
|
||||
assets: {
|
||||
rules: [Required],
|
||||
component: AssetSelect,
|
||||
label: vm.$t('assets.Asset'),
|
||||
el: {
|
||||
multiple: false
|
||||
},
|
||||
hidden: () => {
|
||||
return vm.platform || vm.asset
|
||||
}
|
||||
},
|
||||
template: {
|
||||
component: Select2,
|
||||
rules: [Required],
|
||||
el: {
|
||||
multiple: false,
|
||||
ajax: {
|
||||
url: '/api/v1/accounts/account-templates/',
|
||||
transformOption: (item) => {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
}
|
||||
},
|
||||
hidden: () => {
|
||||
return vm.platform || vm.asset || !vm.addTemplate
|
||||
}
|
||||
},
|
||||
on_invalid: {
|
||||
rules: [Required],
|
||||
label: vm.$t('accounts.AccountPolicy'),
|
||||
helpText: vm.$t('accounts.BulkCreateStrategy'),
|
||||
hidden: () => {
|
||||
return vm.platform || vm.asset
|
||||
}
|
||||
},
|
||||
name: {
|
||||
label: vm.$t('common.Name'),
|
||||
rules: [RequiredChange],
|
||||
on: {
|
||||
input: ([value], updateForm) => {
|
||||
if (!vm.usernameChanged) {
|
||||
if (!vm.account?.name) {
|
||||
updateForm({ username: value })
|
||||
}
|
||||
const maybePrivileged = defaultPrivilegedAccounts.includes(value)
|
||||
if (maybePrivileged) {
|
||||
updateForm({ privileged: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hidden: () => {
|
||||
return vm.addTemplate
|
||||
}
|
||||
},
|
||||
username: {
|
||||
el: {
|
||||
disabled: !!vm.account?.name
|
||||
},
|
||||
on: {
|
||||
input: ([value], updateForm) => {
|
||||
vm.usernameChanged = true
|
||||
},
|
||||
change: ([value], updateForm) => {
|
||||
const maybePrivileged = defaultPrivilegedAccounts.includes(value)
|
||||
if (maybePrivileged) {
|
||||
updateForm({ privileged: true })
|
||||
}
|
||||
}
|
||||
},
|
||||
hidden: () => {
|
||||
return vm.addTemplate
|
||||
}
|
||||
},
|
||||
privileged: {
|
||||
label: vm.$t('assets.Privileged'),
|
||||
hidden: () => {
|
||||
return vm.addTemplate
|
||||
}
|
||||
},
|
||||
su_from: {
|
||||
component: Select2,
|
||||
hidden: (formValue) => {
|
||||
return !vm.asset?.id || !vm.iPlatform.su_enabled
|
||||
},
|
||||
el: {
|
||||
multiple: false,
|
||||
clearable: true,
|
||||
ajax: {
|
||||
url: `/api/v1/accounts/accounts/su-from-accounts/?account=${vm.account?.id || ''}&asset=${vm.asset?.id || ''}`,
|
||||
transformOption: (item) => {
|
||||
return { label: `${item.name}(${item.username})`, value: item.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
su_from_username: {
|
||||
label: vm.$t('assets.UserSwitchFrom'),
|
||||
hidden: (formValue) => {
|
||||
return vm.platform || vm.asset || vm.addTemplate
|
||||
}
|
||||
},
|
||||
password: {
|
||||
label: vm.$t('assets.Password'),
|
||||
component: UpdateToken,
|
||||
hidden: (formValue) => {
|
||||
return formValue.secret_type !== 'password' || vm.addTemplate
|
||||
}
|
||||
},
|
||||
ssh_key: {
|
||||
label: vm.$t('assets.PrivateKey'),
|
||||
component: UploadSecret,
|
||||
hidden: (formValue) => formValue.secret_type !== 'ssh_key' || vm.addTemplate
|
||||
},
|
||||
passphrase: {
|
||||
label: vm.$t('assets.Passphrase'),
|
||||
component: UpdateToken,
|
||||
hidden: (formValue) => formValue.secret_type !== 'ssh_key' || vm.addTemplate
|
||||
},
|
||||
token: {
|
||||
label: vm.$t('assets.Token'),
|
||||
component: UploadSecret,
|
||||
hidden: (formValue) => formValue.secret_type !== 'token' || vm.addTemplate
|
||||
},
|
||||
access_key: {
|
||||
id: 'access_key',
|
||||
label: vm.$t('assets.AccessKey'),
|
||||
component: UploadSecret,
|
||||
hidden: (formValue) => formValue.secret_type !== 'access_key' || vm.addTemplate
|
||||
},
|
||||
api_key: {
|
||||
id: 'api_key',
|
||||
label: vm.$t('assets.ApiKey'),
|
||||
component: UploadSecret,
|
||||
hidden: (formValue) => formValue.secret_type !== 'api_key' || vm.addTemplate
|
||||
},
|
||||
secret_type: {
|
||||
type: 'radio-group',
|
||||
options: [],
|
||||
hidden: () => {
|
||||
return vm.addTemplate
|
||||
}
|
||||
},
|
||||
push_now: {
|
||||
helpText: vm.$t('accounts.AccountPush.WindowsPushHelpText'),
|
||||
hidden: (formValue) => {
|
||||
const automation = vm.iPlatform.automation || {}
|
||||
return !automation.push_account_enabled ||
|
||||
!automation.ansible_enabled ||
|
||||
!vm.$hasPerm('accounts.push_account') ||
|
||||
(formValue.secret_type === 'ssh_key' && vm.iPlatform.type.value === 'windows') ||
|
||||
vm.addTemplate
|
||||
}
|
||||
},
|
||||
params: {
|
||||
label: vm.$t('assets.PushParams'),
|
||||
component: AutomationParamsForm,
|
||||
el: {},
|
||||
hidden: (formValue) => {
|
||||
const automation = vm.iPlatform.automation || {}
|
||||
vm.fieldsMeta.params.el.method = vm.iPlatform.automation.push_account_method
|
||||
vm.fieldsMeta.params.el.pushAccountParams = vm.iPlatform.automation.push_account_params
|
||||
return !formValue.push_now ||
|
||||
!automation.push_account_enabled ||
|
||||
!automation.ansible_enabled ||
|
||||
(formValue.secret_type === 'ssh_key' &&
|
||||
vm.iPlatform.type.value === 'windows') ||
|
||||
!vm.$hasPerm('accounts.push_account') ||
|
||||
vm.addTemplate
|
||||
}
|
||||
},
|
||||
is_active: {
|
||||
label: vm.$t('common.IsActive')
|
||||
},
|
||||
comment: {
|
||||
label: vm.$t('common.Comment'),
|
||||
hidden: () => {
|
||||
return vm.addTemplate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,8 @@
|
||||
|
||||
<script>
|
||||
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
|
||||
import { UpdateToken, UploadSecret } from '@/components/Form/FormFields'
|
||||
import Select2 from '@/components/Form/FormFields/Select2.vue'
|
||||
import AssetSelect from '@/components/Apps/AssetSelect/index.vue'
|
||||
import { encryptPassword } from '@/utils/crypto'
|
||||
import { Required, RequiredChange } from '@/components/Form/DataForm/rules'
|
||||
import AutomationParamsForm from '@/views/assets/Platform/AutomationParamsSetting.vue'
|
||||
import { accountFieldsMeta } from '@/components/Apps/AccountCreateUpdateForm/const'
|
||||
|
||||
export default {
|
||||
name: 'AccountCreateForm',
|
||||
@@ -48,7 +44,6 @@ export default {
|
||||
return {
|
||||
loading: true,
|
||||
usernameChanged: false,
|
||||
defaultPrivilegedAccounts: ['root', 'administrator'],
|
||||
iPlatform: {
|
||||
automation: {},
|
||||
su_enabled: false,
|
||||
@@ -72,179 +67,7 @@ export default {
|
||||
]],
|
||||
[this.$t('common.Other'), ['push_now', 'params', 'on_invalid', 'is_active', 'comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
assets: {
|
||||
rules: [Required],
|
||||
component: AssetSelect,
|
||||
label: this.$t('assets.Asset'),
|
||||
el: {
|
||||
multiple: false
|
||||
},
|
||||
hidden: () => {
|
||||
return this.platform || this.asset
|
||||
}
|
||||
},
|
||||
template: {
|
||||
component: Select2,
|
||||
rules: [Required],
|
||||
el: {
|
||||
multiple: false,
|
||||
ajax: {
|
||||
url: '/api/v1/accounts/account-templates/',
|
||||
transformOption: (item) => {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
}
|
||||
},
|
||||
hidden: () => {
|
||||
return this.platform || this.asset || !this.addTemplate
|
||||
}
|
||||
},
|
||||
on_invalid: {
|
||||
rules: [Required],
|
||||
label: this.$t('accounts.AccountPolicy'),
|
||||
helpText: this.$t('accounts.BulkCreateStrategy'),
|
||||
hidden: () => {
|
||||
return this.platform || this.asset
|
||||
}
|
||||
},
|
||||
name: {
|
||||
rules: [RequiredChange],
|
||||
on: {
|
||||
input: ([value], updateForm) => {
|
||||
if (!this.usernameChanged) {
|
||||
if (!this.account?.name) {
|
||||
updateForm({ username: value })
|
||||
}
|
||||
const maybePrivileged = this.defaultPrivilegedAccounts.includes(value)
|
||||
if (maybePrivileged) {
|
||||
updateForm({ privileged: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hidden: () => {
|
||||
return this.addTemplate
|
||||
}
|
||||
},
|
||||
username: {
|
||||
el: {
|
||||
disabled: !!this.account?.name
|
||||
},
|
||||
on: {
|
||||
input: ([value], updateForm) => {
|
||||
this.usernameChanged = true
|
||||
},
|
||||
change: ([value], updateForm) => {
|
||||
const maybePrivileged = this.defaultPrivilegedAccounts.includes(value)
|
||||
if (maybePrivileged) {
|
||||
updateForm({ privileged: true })
|
||||
}
|
||||
}
|
||||
},
|
||||
hidden: () => {
|
||||
return this.addTemplate
|
||||
}
|
||||
},
|
||||
privileged: {
|
||||
hidden: () => {
|
||||
return this.addTemplate
|
||||
}
|
||||
},
|
||||
su_from: {
|
||||
component: Select2,
|
||||
hidden: (formValue) => {
|
||||
return !this.asset?.id || !this.iPlatform.su_enabled
|
||||
},
|
||||
el: {
|
||||
multiple: false,
|
||||
clearable: true,
|
||||
ajax: {
|
||||
url: `/api/v1/accounts/accounts/su-from-accounts/?account=${this.account?.id || ''}&asset=${this.asset?.id || ''}`,
|
||||
transformOption: (item) => {
|
||||
return { label: `${item.name}(${item.username})`, value: item.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
su_from_username: {
|
||||
label: this.$t('assets.UserSwitchFrom'),
|
||||
hidden: (formValue) => {
|
||||
return this.platform || this.asset || this.addTemplate
|
||||
}
|
||||
},
|
||||
password: {
|
||||
label: this.$t('assets.Password'),
|
||||
component: UpdateToken,
|
||||
hidden: (formValue) => formValue.secret_type !== 'password' || this.addTemplate
|
||||
},
|
||||
ssh_key: {
|
||||
label: this.$t('assets.PrivateKey'),
|
||||
component: UploadSecret,
|
||||
hidden: (formValue) => formValue.secret_type !== 'ssh_key' || this.addTemplate
|
||||
},
|
||||
passphrase: {
|
||||
label: this.$t('assets.Passphrase'),
|
||||
component: UpdateToken,
|
||||
hidden: (formValue) => formValue.secret_type !== 'ssh_key' || this.addTemplate
|
||||
},
|
||||
token: {
|
||||
label: this.$t('assets.Token'),
|
||||
component: UploadSecret,
|
||||
hidden: (formValue) => formValue.secret_type !== 'token' || this.addTemplate
|
||||
},
|
||||
access_key: {
|
||||
id: 'access_key',
|
||||
label: this.$t('assets.AccessKey'),
|
||||
component: UploadSecret,
|
||||
hidden: (formValue) => formValue.secret_type !== 'access_key' || this.addTemplate
|
||||
},
|
||||
api_key: {
|
||||
id: 'api_key',
|
||||
label: this.$t('assets.ApiKey'),
|
||||
component: UploadSecret,
|
||||
hidden: (formValue) => formValue.secret_type !== 'api_key' || this.addTemplate
|
||||
},
|
||||
secret_type: {
|
||||
type: 'radio-group',
|
||||
options: [],
|
||||
hidden: () => {
|
||||
return this.addTemplate
|
||||
}
|
||||
},
|
||||
push_now: {
|
||||
helpText: this.$t('accounts.AccountPush.WindowsPushHelpText'),
|
||||
hidden: (formValue) => {
|
||||
const automation = this.iPlatform.automation || {}
|
||||
return !automation.push_account_enabled ||
|
||||
!automation.ansible_enabled ||
|
||||
!this.$hasPerm('accounts.push_account') ||
|
||||
(formValue.secret_type === 'ssh_key' && this.iPlatform.type.value === 'windows') ||
|
||||
this.addTemplate
|
||||
}
|
||||
},
|
||||
params: {
|
||||
label: this.$t('assets.PushParams'),
|
||||
component: AutomationParamsForm,
|
||||
el: {
|
||||
method: this.asset?.auto_config?.push_account_method
|
||||
},
|
||||
hidden: (formValue) => {
|
||||
const automation = this.iPlatform.automation || {}
|
||||
return !formValue.push_now ||
|
||||
!automation.push_account_enabled ||
|
||||
!automation.ansible_enabled ||
|
||||
(formValue.secret_type === 'ssh_key' && this.iPlatform.type.value === 'windows') ||
|
||||
!this.$hasPerm('accounts.push_account') ||
|
||||
this.addTemplate
|
||||
}
|
||||
},
|
||||
comment: {
|
||||
hidden: () => {
|
||||
return this.addTemplate
|
||||
}
|
||||
}
|
||||
},
|
||||
fieldsMeta: accountFieldsMeta(this),
|
||||
hasSaveContinue: false
|
||||
}
|
||||
},
|
||||
@@ -252,11 +75,18 @@ export default {
|
||||
try {
|
||||
await this.getPlatform()
|
||||
this.setSecretTypeOptions()
|
||||
this.getDefaultAssets()
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getDefaultAssets() {
|
||||
const assetId = this.$route.query.asset_id
|
||||
if (assetId && !this.form.name) {
|
||||
this.form.assets = [assetId]
|
||||
}
|
||||
},
|
||||
async getPlatform() {
|
||||
if (this.platform) {
|
||||
this.iPlatform = this.platform
|
||||
@@ -321,6 +151,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<GenericUpdateFormDialog
|
||||
v-if="visible"
|
||||
:form-setting="formSetting"
|
||||
:selected-rows="selectedRows"
|
||||
:visible="visible"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericUpdateFormDialog } from '@/layout/components'
|
||||
import { accountFieldsMeta } from '@/components/Apps/AccountCreateUpdateForm/const'
|
||||
import { encryptPassword } from '@/utils/crypto'
|
||||
|
||||
export default {
|
||||
name: 'AccountBulkUpdateDialog',
|
||||
components: {
|
||||
GenericUpdateFormDialog
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectedRows: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formSetting: {
|
||||
url: '/api/v1/accounts/accounts/',
|
||||
hasSaveContinue: false,
|
||||
fields: [],
|
||||
fieldsMeta: accountFieldsMeta(this),
|
||||
cleanOtherFormValue: (formValue) => {
|
||||
for (const value of formValue) {
|
||||
Object.keys(value).forEach((item, index, arr) => {
|
||||
if (['ssh_key', 'token', 'access_key', 'api_key', 'password'].includes(item)) {
|
||||
value['secret'] = encryptPassword(value[item])
|
||||
delete value[item]
|
||||
}
|
||||
})
|
||||
}
|
||||
return formValue
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.filterFieldsMeta()
|
||||
},
|
||||
methods: {
|
||||
filterFieldsMeta() {
|
||||
let fields = ['privileged']
|
||||
const fieldsMeta = {}
|
||||
const secretFields = ['password', 'ssh_key', 'passphrase', 'token', 'access_key', 'api_key']
|
||||
const secret_type = this.selectedRows[0].secret_type?.value || 'password'
|
||||
for (const field of secretFields) {
|
||||
if (secret_type === 'ssh_key' && field === 'passphrase') {
|
||||
fields.push('passphrase')
|
||||
this.formSetting.fieldsMeta['passphrase'].hidden = () => false
|
||||
continue
|
||||
}
|
||||
if (secret_type === field) {
|
||||
fields.push(field)
|
||||
this.formSetting.fieldsMeta[field].hidden = () => false
|
||||
continue
|
||||
}
|
||||
delete this.formSetting.fieldsMeta[field]
|
||||
}
|
||||
fields = fields.concat(['is_active', 'comment'])
|
||||
for (const field of fields) {
|
||||
fieldsMeta[field] = this.formSetting.fieldsMeta[field]
|
||||
}
|
||||
this.formSetting.fields = fields
|
||||
this.formSetting.fieldsMeta = fieldsMeta
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -37,6 +37,12 @@
|
||||
:result="createAccountResults"
|
||||
:visible.sync="showResultDialog"
|
||||
/>
|
||||
<AccountBulkUpdateDialog
|
||||
v-if="updateSelectedDialogSetting.visible"
|
||||
:visible.sync="updateSelectedDialogSetting.visible"
|
||||
v-bind="updateSelectedDialogSetting"
|
||||
@update="handleAccountBulkUpdate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -49,10 +55,12 @@ import AccountCreateUpdate from './AccountCreateUpdate.vue'
|
||||
import { connectivityMeta } from './const'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import ResultDialog from './BulkCreateResultDialog.vue'
|
||||
import AccountBulkUpdateDialog from '@/components/Apps/AccountListTable/AccountBulkUpdateDialog.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountListTable',
|
||||
components: {
|
||||
AccountBulkUpdateDialog,
|
||||
ResultDialog,
|
||||
ListTable,
|
||||
UpdateSecretInfo,
|
||||
@@ -117,6 +125,10 @@ export default {
|
||||
headerExtraActions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
extraQuery: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -138,9 +150,7 @@ export default {
|
||||
app: 'assets',
|
||||
resource: 'account'
|
||||
},
|
||||
extraQuery: {
|
||||
order: '-date_updated'
|
||||
},
|
||||
extraQuery: this.extraQuery,
|
||||
columnsExclude: ['spec_info'],
|
||||
columnsShow: {
|
||||
min: ['name', 'username', 'actions'],
|
||||
@@ -239,7 +249,7 @@ export default {
|
||||
},
|
||||
{
|
||||
name: 'Test',
|
||||
title: this.$t('common.Test'),
|
||||
title: this.$t('accounts.Test'),
|
||||
can: ({ row }) =>
|
||||
!this.$store.getters.currentOrgIsRoot &&
|
||||
this.$hasPerm('accounts.change_account') &&
|
||||
@@ -339,6 +349,29 @@ export default {
|
||||
...this.headerExtraActions
|
||||
],
|
||||
extraMoreActions: [
|
||||
{
|
||||
name: 'BulkVerify',
|
||||
title: this.$t('accounts.BulkVerify'),
|
||||
type: 'primary',
|
||||
fa: 'fa-link',
|
||||
can: ({ selectedRows }) => {
|
||||
return selectedRows.length > 0 &&
|
||||
['clickhouse', 'redis', 'website', 'chatgpt'].indexOf(selectedRows[0].asset.type.value) === -1 &&
|
||||
!this.$store.getters.currentOrgIsRoot
|
||||
},
|
||||
callback: function({ selectedRows }) {
|
||||
const ids = selectedRows.map(v => {
|
||||
return v.id
|
||||
})
|
||||
this.$axios.post(
|
||||
'/api/v1/accounts/accounts/tasks/',
|
||||
{ action: 'verify', accounts: ids }).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
}).catch(err => {
|
||||
this.$message.error(this.$tc('common.bulkVerifyErrorMsg' + ' ' + err))
|
||||
})
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'ClearSecrets',
|
||||
title: this.$t('common.ClearSecret'),
|
||||
@@ -348,7 +381,9 @@ export default {
|
||||
return selectedRows.length > 0 && vm.$hasPerm('accounts.change_account')
|
||||
},
|
||||
callback: function({ selectedRows }) {
|
||||
const ids = selectedRows.map(v => { return v.id })
|
||||
const ids = selectedRows.map(v => {
|
||||
return v.id
|
||||
})
|
||||
this.$axios.patch(
|
||||
'/api/v1/accounts/accounts/clear-secret/',
|
||||
{ account_ids: ids }).then(() => {
|
||||
@@ -357,6 +392,21 @@ export default {
|
||||
this.$message.error(this.$tc('common.bulkClearErrorMsg' + ' ' + err))
|
||||
})
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'actionUpdateSelected',
|
||||
title: this.$t('accounts.AccountBatchUpdate'),
|
||||
fa: 'batch-update',
|
||||
can: ({ selectedRows }) => {
|
||||
return selectedRows.length > 0 &&
|
||||
!this.$store.getters.currentOrgIsRoot &&
|
||||
vm.$hasPerm('accounts.change_account') &&
|
||||
selectedRows.every(i => i.secret_type.value === selectedRows[0].secret_type.value)
|
||||
},
|
||||
callback: ({ selectedRows }) => {
|
||||
vm.updateSelectedDialogSetting.selectedRows = selectedRows
|
||||
vm.updateSelectedDialogSetting.visible = true
|
||||
}
|
||||
}
|
||||
],
|
||||
canBulkDelete: vm.$hasPerm('accounts.delete_account'),
|
||||
@@ -365,6 +415,10 @@ export default {
|
||||
exclude: ['asset']
|
||||
},
|
||||
hasSearch: true
|
||||
},
|
||||
updateSelectedDialogSetting: {
|
||||
visible: false,
|
||||
selectedRows: []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -392,9 +446,18 @@ export default {
|
||||
can: this.$hasPerm('accounts.delete_account'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
this.$axios.delete(`/api/v1/accounts/accounts/${row.id}/`).then(() => {
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
this.$refs.ListTable.reloadTable()
|
||||
const msg = this.$t('accounts.AccountDeleteConfirmMsg')
|
||||
this.$confirm(msg, this.$tc('common.Info'), {
|
||||
type: 'warning',
|
||||
confirmButtonClass: 'el-button--danger',
|
||||
beforeClose: async(action, instance, done) => {
|
||||
if (action !== 'confirm') return done()
|
||||
this.$axios.delete(`/api/v1/accounts/accounts/${row.id}/`).then(() => {
|
||||
done()
|
||||
this.$refs.ListTable.reloadTable()
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -423,6 +486,10 @@ export default {
|
||||
setTimeout(() => {
|
||||
this.showResultDialog = true
|
||||
}, 100)
|
||||
},
|
||||
handleAccountBulkUpdate() {
|
||||
this.updateSelectedDialogSetting.visible = false
|
||||
this.$refs.ListTable.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,10 @@ export default {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'account'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: function() {
|
||||
@@ -136,7 +140,8 @@ export default {
|
||||
name: this.secretInfo.name,
|
||||
secret: encryptPassword(this.modifiedSecret)
|
||||
}
|
||||
this.$axios.patch(`/api/v1/accounts/accounts/${this.account.id}/`, params).then(() => {
|
||||
const url = this.type === 'account' ? `/api/v1/accounts/accounts` : `/api/v1/accounts/account-templates`
|
||||
this.$axios.patch(`${url}/${this.account.id}/`, params).then(() => {
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:close-on-click-modal="false"
|
||||
:loading-status="!isLoaded"
|
||||
:title="$tc('assets.Assets')"
|
||||
custom-class="asset-select-dialog"
|
||||
top="2vh"
|
||||
@@ -17,8 +19,10 @@
|
||||
:table-config="tableConfig"
|
||||
:tree-url="`${baseNodeUrl}children/tree/`"
|
||||
:url="baseUrl"
|
||||
:tree-setting="treeSetting"
|
||||
class="tree-table"
|
||||
v-bind="$attrs"
|
||||
@loaded="handleTableLoaded"
|
||||
/>
|
||||
</Dialog>
|
||||
</template>
|
||||
@@ -52,11 +56,16 @@ export default {
|
||||
disabled: {
|
||||
type: [Boolean, Function],
|
||||
default: false
|
||||
},
|
||||
treeSetting: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
isLoaded: false,
|
||||
dialogVisible: false,
|
||||
rowSelected: _.cloneDeep(this.value) || [],
|
||||
rowsAdd: [],
|
||||
@@ -104,6 +113,7 @@ export default {
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasRightActions: false,
|
||||
hasLabelSearch: true,
|
||||
searchConfig: {
|
||||
getUrlQuery: false
|
||||
}
|
||||
@@ -136,6 +146,9 @@ export default {
|
||||
if (selectValueIndex > -1) {
|
||||
this.rowSelected.splice(selectValueIndex, 1)
|
||||
}
|
||||
},
|
||||
handleTableLoaded() {
|
||||
this.isLoaded = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
ref="dialog"
|
||||
:base-node-url="baseNodeUrl"
|
||||
:base-url="baseUrl"
|
||||
:tree-setting="treeSetting"
|
||||
:tree-url-query="treeUrlQuery"
|
||||
:value="value"
|
||||
:visible.sync="dialogVisible"
|
||||
@@ -27,7 +28,6 @@
|
||||
<script>
|
||||
import Select2 from '@/components/Form/FormFields/Select2.vue'
|
||||
import AssetSelectDialog from './dialog.vue'
|
||||
import { b } from 'css-color-function/lib/adjusters'
|
||||
|
||||
export default {
|
||||
componentName: 'AssetSelect',
|
||||
@@ -48,6 +48,10 @@ export default {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
treeSetting: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -76,7 +80,6 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
b,
|
||||
handleFocus() {
|
||||
this.$refs.select2.selectRef.blur()
|
||||
this.dialogVisible = true
|
||||
|
||||
@@ -31,6 +31,10 @@ export default {
|
||||
type: String,
|
||||
default: '/api/v1/assets/assets/'
|
||||
},
|
||||
typeUrl: {
|
||||
type: String,
|
||||
default: '/api/v1/assets/nodes/category/tree/'
|
||||
},
|
||||
nodeUrl: {
|
||||
type: String,
|
||||
default: '/api/v1/assets/nodes/'
|
||||
@@ -60,6 +64,7 @@ export default {
|
||||
const showAssets = this.treeSetting?.showAssets || this.showAssets
|
||||
const treeUrlQuery = this.setTreeUrlQuery()
|
||||
const assetTreeUrl = `${this.treeUrl}?assets=${showAssets ? '1' : '0'}&${treeUrlQuery}`
|
||||
const vm = this
|
||||
|
||||
return {
|
||||
treeTabConfig: {
|
||||
@@ -81,7 +86,13 @@ export default {
|
||||
nodeUrl: this.nodeUrl,
|
||||
treeUrl: assetTreeUrl,
|
||||
callback: {
|
||||
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
|
||||
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode),
|
||||
beforeRefresh: () => {
|
||||
const query = { ...this.$route.query, node_id: '', asset_id: '' }
|
||||
setTimeout(() => {
|
||||
vm.$router.replace({ query: query })
|
||||
}, 100)
|
||||
}
|
||||
},
|
||||
...this.treeSetting
|
||||
}
|
||||
@@ -94,9 +105,9 @@ export default {
|
||||
showAssets: false,
|
||||
showSearch: false,
|
||||
customTreeHeaderName: this.$t('assets.BuiltinTree'),
|
||||
url: '/api/v1/assets/nodes/category/tree/',
|
||||
url: this.typeUrl,
|
||||
nodeUrl: this.treeSetting?.nodeUrl || this.nodeUrl,
|
||||
treeUrl: `/api/v1/assets/nodes/category/tree/?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
|
||||
treeUrl: `${this.typeUrl}?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
|
||||
callback: {
|
||||
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
|
||||
}
|
||||
|
||||
136
src/components/Apps/ChangeSecret/RecordViewSecret.vue
Normal file
136
src/components/Apps/ChangeSecret/RecordViewSecret.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<div>
|
||||
<Dialog
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
:title="title"
|
||||
:visible.sync="showSecret"
|
||||
:width="'50'"
|
||||
v-bind="$attrs"
|
||||
@confirm="accountConfirmHandle"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<el-form :model="secretInfo" class="password-form" label-position="right" label-width="100px">
|
||||
<el-form-item :label="$tc('accounts.AccountChangeSecret.OldSecret')">
|
||||
<ShowKeyCopyFormatter
|
||||
:cell-value="secretInfo.old_secret"
|
||||
:col="{ formatterArgs: {
|
||||
name: 'old_secret'
|
||||
}}"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$tc('accounts.AccountChangeSecret.NewSecret')">
|
||||
<ShowKeyCopyFormatter
|
||||
:cell-value="secretInfo.new_secret"
|
||||
:col="{ formatterArgs: {
|
||||
name: 'new_secret'
|
||||
}}"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import { ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'RecordViewSecret',
|
||||
components: {
|
||||
Dialog,
|
||||
ShowKeyCopyFormatter
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return this.$tc('common.ViewSecret')
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
secretInfo: {},
|
||||
showSecret: false,
|
||||
mfaDialogVisible: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
mounted() {
|
||||
this.showSecretDialog()
|
||||
},
|
||||
methods: {
|
||||
accountConfirmHandle() {
|
||||
this.showSecret = false
|
||||
this.mfaDialogVisible = false
|
||||
},
|
||||
showSecretDialog() {
|
||||
return this.$axios.get(this.url, { disableFlashErrorMsg: true }).then((res) => {
|
||||
this.secretInfo = res
|
||||
this.showSecret = true
|
||||
})
|
||||
},
|
||||
exit() {
|
||||
this.$emit('update:visible', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.item-textarea >>> .el-textarea__inner {
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
padding: 5px 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
>>> .el-form-item__label {
|
||||
padding-right: 20px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
>>> .el-form-item__content {
|
||||
line-height: 30px;
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
margin-bottom: 8px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.title {
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -42,7 +42,6 @@ const {
|
||||
addMessageToActiveChat,
|
||||
newChatAndAddMessageById,
|
||||
removeLoadingMessageInChat,
|
||||
removeLoadingAndAddMessageToChat,
|
||||
updateChaMessageContentById,
|
||||
addTemporaryLoadingToChat
|
||||
} = useChat()
|
||||
@@ -120,12 +119,11 @@ export default {
|
||||
}
|
||||
},
|
||||
onChatMessage(data) {
|
||||
if (!data.message.content && data.conversation_id) {
|
||||
if (data.conversation_id) {
|
||||
setLoading(true)
|
||||
removeLoadingAndAddMessageToChat(data)
|
||||
removeLoadingMessageInChat()
|
||||
this.currentConversationId = data.conversation_id
|
||||
} else {
|
||||
updateChaMessageContentById(data.message.id, data.message.content)
|
||||
updateChaMessageContentById(data.message.id, data)
|
||||
}
|
||||
if (data.message?.type === 'finish') {
|
||||
setLoading(false)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="close-sidebar">
|
||||
<i class="el-icon-close" @click="onClose" />
|
||||
<i v-if="hasClose" class="el-icon-close" @click="onClose" />
|
||||
</div>
|
||||
<el-tabs v-model="active" :tab-position="'right'" @tab-click="handleClick">
|
||||
<el-tab-pane v-for="(item) in submenu" :key="item.name" :name="item.name">
|
||||
@@ -22,6 +22,10 @@ export default {
|
||||
type: String,
|
||||
default: 'chat'
|
||||
},
|
||||
hasClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
submenu: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
@@ -48,9 +52,10 @@ export default {
|
||||
height: 100%;
|
||||
background-color: #f0f1f5;
|
||||
.close-sidebar {
|
||||
height: 48px;
|
||||
padding: 12px 0;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
cursor: pointer;
|
||||
i {
|
||||
font-size: 16px;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar">
|
||||
<Sidebar :active.sync="active" :submenu="submenu" />
|
||||
<Sidebar v-bind="$attrs" :active.sync="active" :submenu="submenu" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -62,11 +62,14 @@ export default {
|
||||
watch: {
|
||||
drawerPanelVisible(value) {
|
||||
if (value && !ws) {
|
||||
this.$refs.component?.init()
|
||||
this.initWebSocket()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initWebSocket() {
|
||||
this.$refs.component?.init()
|
||||
},
|
||||
onClose() {
|
||||
this.$parent.show = false
|
||||
},
|
||||
|
||||
@@ -59,13 +59,8 @@ export function useChat() {
|
||||
addChatMessageById(chat)
|
||||
}
|
||||
|
||||
const removeLoadingAndAddMessageToChat = (chat) => {
|
||||
store.commit('chat/removeLoadingMessageInChat')
|
||||
store.commit('chat/addMessageToActiveChat', chat)
|
||||
}
|
||||
|
||||
const updateChaMessageContentById = (id, content) => {
|
||||
store.commit('chat/updateChaMessageContentById', { id, content })
|
||||
const updateChaMessageContentById = (id, data) => {
|
||||
store.commit('chat/updateChaMessageContentById', { id, data })
|
||||
pageScroll('scrollRef')
|
||||
}
|
||||
|
||||
@@ -78,7 +73,6 @@ export function useChat() {
|
||||
addMessageToActiveChat,
|
||||
newChatAndAddMessageById,
|
||||
removeLoadingMessageInChat,
|
||||
removeLoadingAndAddMessageToChat,
|
||||
addChatMessageById,
|
||||
addTemporaryLoadingToChat,
|
||||
updateChaMessageContentById
|
||||
|
||||
@@ -174,7 +174,7 @@ export default {
|
||||
|
||||
.handle-button {
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
bottom: 20%;
|
||||
left: -48px;
|
||||
width: 48px;
|
||||
height: 45px;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
<script type="text/jsx">
|
||||
import TreeTable from '../../Table/TreeTable/index.vue'
|
||||
import { DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import { AccountInfoFormatter } from '@/components/Table/TableFormatters'
|
||||
import { connectivityMeta } from '@/components/Apps/AccountListTable/const'
|
||||
|
||||
export default {
|
||||
name: 'GrantedAssets',
|
||||
@@ -57,10 +59,11 @@ export default {
|
||||
tableConfig: {
|
||||
url: this.tableUrl,
|
||||
hasTree: true,
|
||||
columnsExtra: ['view_account'],
|
||||
columnsExclude: ['spec_info'],
|
||||
columnShow: {
|
||||
columnsShow: {
|
||||
min: ['name', 'address', 'accounts'],
|
||||
default: ['name', 'address', 'accounts', 'actions']
|
||||
default: ['name', 'address', 'platform', 'view_account', 'connectivity']
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
@@ -71,7 +74,13 @@ export default {
|
||||
},
|
||||
actions: {
|
||||
has: false
|
||||
}
|
||||
},
|
||||
view_account: {
|
||||
label: this.$t('assets.Account'),
|
||||
formatter: AccountInfoFormatter,
|
||||
width: '100px'
|
||||
},
|
||||
connectivity: connectivityMeta
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import { encryptPassword } from '@/utils/crypto'
|
||||
|
||||
export default {
|
||||
name: 'UserConfirmDialog',
|
||||
@@ -123,12 +124,15 @@ export default {
|
||||
mounted() {
|
||||
this.$eventBus.$on('showConfirmDialog', this.performConfirm)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventBus.$off('showConfirmDialog', this.performConfirm)
|
||||
},
|
||||
methods: {
|
||||
handleSubTypeChange(val) {
|
||||
this.inputPlaceholder = this.subTypeChoices.filter(item => item.name === val)[0]?.placeholder
|
||||
this.smsWidth = val === 'sms' ? 6 : 0
|
||||
},
|
||||
performConfirm({ response, callback, cancel }) {
|
||||
performConfirm: _.throttle(function({ response, callback, cancel }) {
|
||||
if (this.processing || this.visible) {
|
||||
return
|
||||
}
|
||||
@@ -164,7 +168,7 @@ export default {
|
||||
}).finally(() => {
|
||||
this.processing = false
|
||||
})
|
||||
},
|
||||
}, 300),
|
||||
logout() {
|
||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||
},
|
||||
@@ -196,7 +200,7 @@ export default {
|
||||
const data = {
|
||||
confirm_type: this.confirmTypeRequired,
|
||||
mfa_type: this.confirmTypeRequired === 'mfa' ? this.subTypeSelected : '',
|
||||
secret_key: this.secretValue
|
||||
secret_key: this.confirmTypeRequired === 'password' ? encryptPassword(this.secretValue) : this.secretValue
|
||||
}
|
||||
this.$axios.post(`/api/v1/authentication/confirm/`, data).then(res => {
|
||||
this.callback()
|
||||
|
||||
@@ -106,16 +106,28 @@ export default {
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
if (typeof value[0] === 'object') {
|
||||
value.forEach(item => {
|
||||
const fieldName = `${name}.${item.name}`
|
||||
if (excludes.includes(fieldName)) {
|
||||
return
|
||||
}
|
||||
this.items.push({
|
||||
key: item.label,
|
||||
value: item.value
|
||||
const firstValue = value[0]
|
||||
if (firstValue.hasOwnProperty('name')) {
|
||||
value.forEach(item => {
|
||||
const fieldName = `${name}.${item.name}`
|
||||
if (excludes.includes(fieldName)) {
|
||||
return
|
||||
}
|
||||
this.items.push({
|
||||
key: item.label,
|
||||
value: item.value
|
||||
})
|
||||
})
|
||||
})
|
||||
} else {
|
||||
value.forEach((item, index) => {
|
||||
const v = Object.entries(item).map(([key, value]) => `${key}:${value}`).join(', ')
|
||||
const data = { value: v }
|
||||
if (index === 0) {
|
||||
data['key'] = label
|
||||
}
|
||||
this.items.push(data)
|
||||
})
|
||||
}
|
||||
} else if (typeof value[0] === 'string') {
|
||||
value.forEach((item, index) => {
|
||||
let data = {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<IBox :fa="fa" :title="title">
|
||||
<el-form class="content" label-position="left" label-width="25%">
|
||||
<el-form-item v-for="item in items" :key="item.key" :label="item.key">
|
||||
<el-form-item v-for="item in iItems" :key="item.key" :label="item.key">
|
||||
<ItemValue :value="item.value" class="item-value" v-bind="item" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -35,6 +35,13 @@ export default {
|
||||
type: String,
|
||||
default: 'left'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
iItems: this.items.filter(item => {
|
||||
return !item.hasOwnProperty('has') || item.has === true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<slot />
|
||||
<div v-loading="loadingStatus">
|
||||
<slot />
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<slot name="footer">
|
||||
<el-button v-if="showCancel && showButtons" @click="onCancel">{{ cancelTitle }}</el-button>
|
||||
<el-button v-if="showConfirm && showButtons" :loading="loadingStatus" type="primary" @click="onConfirm">
|
||||
<el-button v-if="showConfirm && showButtons" :disabled="loadingStatus" type="primary" @click="onConfirm">
|
||||
{{ confirmTitle }}
|
||||
</el-button>
|
||||
</slot>
|
||||
@@ -71,13 +73,16 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iWidth() {
|
||||
return this.$store.getters.isMobile ? '1000px' : this.width
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
onCancel() {
|
||||
this.$emit('cancel')
|
||||
@@ -92,7 +97,7 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
.dialog >>> .el-dialog {
|
||||
border-radius: 0.3em;
|
||||
max-width: 1500px;
|
||||
max-width: min(100vw, 1500px);
|
||||
|
||||
.el-icon-circle-check {
|
||||
display: none;
|
||||
@@ -119,9 +124,14 @@ export default {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.dialog >>> .el-dialog {
|
||||
max-width: calc(100% - 30px);
|
||||
}
|
||||
}
|
||||
.dialog-footer >>> button.el-button {
|
||||
font-size: 13px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,6 @@ import Switcher from '@/components/Form/FormFields/Switcher.vue'
|
||||
import rules from '@/components/Form/DataForm/rules'
|
||||
import BasicTree from '@/components/Form/FormFields/BasicTree.vue'
|
||||
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
|
||||
import TransferSelect from '@/components/Form/FormFields/TransferSelect.vue'
|
||||
import { assignIfNot } from '@/utils/common'
|
||||
import TagInput from '@/components/Form/FormFields/TagInput.vue'
|
||||
|
||||
@@ -45,7 +44,7 @@ export class FormFieldGenerator {
|
||||
break
|
||||
case 'field':
|
||||
type = ''
|
||||
field.component = TransferSelect
|
||||
field.component = ObjectSelect2
|
||||
if (fieldRemoteMeta.required) {
|
||||
field.el.clearable = false
|
||||
}
|
||||
@@ -76,7 +75,7 @@ export class FormFieldGenerator {
|
||||
field.component = ObjectSelect2
|
||||
break
|
||||
case 'm2m_related_field':
|
||||
field.component = TransferSelect
|
||||
field.component = ObjectSelect2
|
||||
field.el.label = field.label
|
||||
break
|
||||
case 'nested object':
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
<div style="font-size: 13px;">{{ contabValueString }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<CrontabResult :ex="contabValueString" />
|
||||
<CrontabResult :ex="contabValueString" @crontabDiffChange="crontabDiffChangeHandle" />
|
||||
|
||||
<div class="pop_btn">
|
||||
<el-button
|
||||
@@ -167,7 +167,8 @@ export default {
|
||||
week: '*'
|
||||
// year: "",
|
||||
},
|
||||
newContabValueString: ''
|
||||
newContabValueString: '',
|
||||
crontabDiff: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -364,6 +365,12 @@ export default {
|
||||
},
|
||||
// 填充表达式
|
||||
submitFill() {
|
||||
const crontabDiffMin = this.crontabDiff / 1000 / 60
|
||||
if (crontabDiffMin > 0 && crontabDiffMin < 10) {
|
||||
const msg = this.$tc('common.crontabDiffError')
|
||||
this.$message.error(msg)
|
||||
return
|
||||
}
|
||||
this.$emit('fill', this.contabValueString)
|
||||
this.hidePopup()
|
||||
},
|
||||
@@ -381,6 +388,9 @@ export default {
|
||||
for (const j in this.contabValueObj) {
|
||||
this.changeRadio(j, this.contabValueObj[j])
|
||||
}
|
||||
},
|
||||
crontabDiffChangeHandle(diff) {
|
||||
this.crontabDiff = diff
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -454,7 +464,7 @@ export default {
|
||||
}
|
||||
|
||||
.crontab-panel {
|
||||
>>> .el-input-number {
|
||||
> > > .el-input-number {
|
||||
margin: 0 5px
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,18 +7,11 @@
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="2">
|
||||
{{ this.$t('common.CronTab.from') }}
|
||||
<el-input-number v-model="cycle01" :max="60" :min="0" size="mini" /> -
|
||||
<el-input-number v-model="cycle02" :max="60" :min="0" size="mini" /> {{ this.$t('common.CronTab.min') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
{{ this.$t('common.CronTab.from') }}
|
||||
<el-input-number v-model="average02" :max="60" :min="1" size="mini" /> {{ this.$t('common.CronTab.min') }}{{ this.$t('common.CronTab.executeOnce') }}
|
||||
<el-input-number v-model="average02" :max="60" :min="1" size="mini" />
|
||||
{{ this.$t('common.CronTab.min') }}{{ this.$t('common.CronTab.executeOnce') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
@@ -33,7 +26,7 @@
|
||||
size="small"
|
||||
style="width:100%"
|
||||
>
|
||||
<el-option v-for="item in 60" :key="item" :value="item-1">{{ item-1 }}</el-option>
|
||||
<el-option v-for="item in 60" :key="item" :value="item-1">{{ item - 1 }}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
@@ -158,7 +151,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-form-item--small.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.el-form-item--small.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<script>
|
||||
import parser from 'cron-parser'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'CrontabResult',
|
||||
props: {
|
||||
@@ -51,6 +52,10 @@ export default {
|
||||
const cur = interval.next().toString()
|
||||
this.resultList.push(toSafeLocalDateStr(cur))
|
||||
}
|
||||
const first = new Date(this.resultList[0])
|
||||
const second = new Date(this.resultList[1])
|
||||
const diff = Math.abs(second - first)
|
||||
this.$emit('crontabDiffChange', diff)
|
||||
} catch (error) {
|
||||
this.isShow = false
|
||||
// debug(error, 'error')
|
||||
|
||||
@@ -7,13 +7,11 @@
|
||||
v-bind="data.attrs"
|
||||
>
|
||||
<template v-if="data.helpTips" #label>
|
||||
{{ data.label }}
|
||||
<el-tooltip placement="top" effect="light" popper-class="help-tips">
|
||||
<div slot="content" v-html="data.helpTips" />
|
||||
<el-button style="padding: 0">
|
||||
<i class="fa fa-question-circle" />
|
||||
</el-button>
|
||||
<i class="fa fa-question-circle-o" />
|
||||
</el-tooltip>
|
||||
{{ data.label }}
|
||||
</template>
|
||||
<template v-if="readonly && hasReadonlyContent">
|
||||
<div
|
||||
@@ -70,7 +68,8 @@
|
||||
:key="opt.label"
|
||||
v-bind="opt"
|
||||
:label="'value' in opt ? opt.value : opt.label"
|
||||
>{{ opt.label }}</el-radio>
|
||||
>{{ opt.label }}
|
||||
</el-radio>
|
||||
</template>
|
||||
</custom-component>
|
||||
<div v-if="data.helpText" class="help-block" v-html="data.helpText" />
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="code-editor" style="font-size: 12px">
|
||||
<div class="toolbar">
|
||||
<div
|
||||
v-for="(item,index) in toolbar.left"
|
||||
v-for="(item,index) in iActions"
|
||||
:key="index"
|
||||
style="display: inline-block; margin: 0 2px"
|
||||
>
|
||||
@@ -93,6 +93,16 @@
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
<div v-if="toolbar.hasOwnProperty('fold')" class="fold">
|
||||
<el-tooltip :content="$tc('common.MoreActions')" placement="top">
|
||||
<i
|
||||
class="fa"
|
||||
:class="[isFold ? 'fa-angle-double-right': 'fa-angle-double-down']"
|
||||
@click="onChangeFold"
|
||||
/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="right-side" style="float: right">
|
||||
<div
|
||||
v-for="(item,index) in toolbar.right"
|
||||
@@ -154,9 +164,19 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
isFold: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iActions() {
|
||||
let actions = this.toolbar.left || {}
|
||||
const fold = this.toolbar.fold || {}
|
||||
if (!this.isFold) {
|
||||
actions = { ...actions, ...fold }
|
||||
}
|
||||
return actions
|
||||
},
|
||||
iValue: {
|
||||
get() {
|
||||
return this.value
|
||||
@@ -179,6 +199,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChangeFold() {
|
||||
this.isFold = !this.isFold
|
||||
},
|
||||
getLabel(value, items) {
|
||||
for (const item of items) {
|
||||
if (item.value === value) {
|
||||
@@ -205,6 +228,16 @@ export default {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.fold {
|
||||
display: inline-block;
|
||||
padding-left: 4px;
|
||||
i {
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
> > > .CodeMirror pre.CodeMirror-line,
|
||||
> > > .CodeMirror-linenumber.CodeMirror-gutter-elt {
|
||||
line-height: 18px !important;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'PhoneInput',
|
||||
@@ -35,21 +36,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rawValue: {},
|
||||
countries: [
|
||||
{ name: 'China(中国)', value: '+86' },
|
||||
{ name: 'HongKong(中国香港)', value: '+852' },
|
||||
{ name: 'Macao(中国澳门)', value: '+853' },
|
||||
{ name: 'Taiwan(中国台湾)', value: '+886' },
|
||||
{ name: 'America(America)', value: '+1' },
|
||||
{ name: 'Russia(Россия)', value: '+7' },
|
||||
{ name: 'France(français)', value: '+33' },
|
||||
{ name: 'Britain(Britain)', value: '+44' },
|
||||
{ name: 'Germany(Deutschland)', value: '+49' },
|
||||
{ name: 'Japan(日本)', value: '+81' },
|
||||
{ name: 'Korea(한국)', value: '+82' },
|
||||
{ name: 'India(भारत)', value: '+91' }
|
||||
]
|
||||
rawValue: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -58,7 +45,13 @@ export default {
|
||||
return ''
|
||||
}
|
||||
return `${this.rawValue.code}${this.rawValue.phone}`
|
||||
}
|
||||
},
|
||||
countries: {
|
||||
get() {
|
||||
return this.publicSettings.COUNTRY_CALLING_CODES
|
||||
}
|
||||
},
|
||||
...mapGetters(['publicSettings'])
|
||||
},
|
||||
mounted() {
|
||||
this.rawValue = this.value || { code: '+86', phone: '' }
|
||||
|
||||
@@ -225,9 +225,6 @@ export default {
|
||||
handler(newValue, oldValue) {
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
iOptions(val) {
|
||||
this.remote = val.length !== 0
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
@@ -359,7 +356,7 @@ export default {
|
||||
})
|
||||
},
|
||||
clearSelected() {
|
||||
this.iValue = []
|
||||
this.iValue = this.multiple ? [] : ''
|
||||
},
|
||||
checkDisabled(item) {
|
||||
return item.disabled === undefined ? this.disabledValues.indexOf(item.value) !== -1 : item.disabled
|
||||
|
||||
@@ -11,11 +11,15 @@
|
||||
/>
|
||||
<Dialog
|
||||
v-if="showTransfer"
|
||||
:loading-status="!isLoaded"
|
||||
:close-on-click-modal="false"
|
||||
:title="label"
|
||||
:visible.sync="showTransfer"
|
||||
class="the-dialog"
|
||||
width="730px"
|
||||
@cancel="handleTransCancel"
|
||||
@confirm="handleTransConfirm"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<krryPaging v-if="selectInitialized" ref="pageTransfer" class="transfer" v-bind="pagingTransfer" />
|
||||
</Dialog>
|
||||
@@ -75,13 +79,16 @@ export default {
|
||||
if (keyword) {
|
||||
params['search'] = keyword
|
||||
}
|
||||
this.isLoaded = false
|
||||
const data = await this.$axios.get(url, { params })
|
||||
this.isLoaded = true
|
||||
return data['results'].map(item => {
|
||||
const n = transformOption(item)
|
||||
return { id: n.value, label: n.label }
|
||||
})
|
||||
}
|
||||
return {
|
||||
isLoaded: false,
|
||||
showTransfer: false,
|
||||
selectInitialized: false,
|
||||
select2: {
|
||||
@@ -125,13 +132,17 @@ export default {
|
||||
return _.uniq(value)
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
this.emit(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
emit(val) {
|
||||
const value = _.uniq(val)
|
||||
this.$emit('input', value)
|
||||
},
|
||||
onInputChange(val) {
|
||||
this.$emit('input', val)
|
||||
this.emit(val)
|
||||
},
|
||||
handleFocus() {
|
||||
this.$refs.select2.selectRef.blur()
|
||||
@@ -149,18 +160,14 @@ export default {
|
||||
this.showTransfer = false
|
||||
},
|
||||
handleTransConfirm() {
|
||||
const selectedData = this.$refs.pageTransfer.checkedData
|
||||
const selectedData = this.$refs.pageTransfer.selectListCheck
|
||||
const options = selectedData.map(item => {
|
||||
return { value: item.id, label: item.label }
|
||||
})
|
||||
this.select2.options = options
|
||||
this.$emit('input', options.map(item => item.value))
|
||||
this.emit(options.map(item => item.value))
|
||||
this.showTransfer = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div v-if="tip !== ''" class="help-block">{{ tip }}</div>
|
||||
<input v-model="value" hidden type="text" v-on="$listeners">
|
||||
<div>
|
||||
<img :src="preview" v-bind="$attrs">
|
||||
<img :class="showBG ? 'show-bg' : ''" :src="preview" v-bind="$attrs">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -27,6 +27,10 @@ export default {
|
||||
accept: {
|
||||
type: String,
|
||||
default: '*'
|
||||
},
|
||||
showBG: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -74,6 +78,8 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.show-bg {
|
||||
background-color: var(--banner-bg);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
patterns.push([/\d/, i18n.t('common.password.NUMBER_REQUIRED')])
|
||||
}
|
||||
if (passwordRule['SECURITY_PASSWORD_SPECIAL_CHAR']) {
|
||||
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]")
|
||||
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?_+-]")
|
||||
patterns.push([pattern, i18n.t('common.password.SPECIAL_CHAR_REQUIRED')])
|
||||
}
|
||||
for (const [pattern, msg] of patterns) {
|
||||
|
||||
@@ -204,7 +204,12 @@ export default {
|
||||
},
|
||||
formatWeektime(col) {
|
||||
const timeStamp = 1542384000000 // '2018-11-17 00:00:00'
|
||||
const beginStamp = timeStamp + col * 1800000 // col * 30 * 60 * 1000
|
||||
const timezone = 8
|
||||
const offsetGMT = new Date().getTimezoneOffset() // 本地时间和格林威治的时间差,单位为分钟
|
||||
const nowDate = new Date(timeStamp).getTime()
|
||||
const targetStamp = new Date(nowDate + offsetGMT * 60 * 1000 + timezone * 60 * 60 * 1000).getTime()
|
||||
|
||||
const beginStamp = targetStamp + col * 1800000 // col * 30 * 60 * 1000
|
||||
const endStamp = beginStamp + 1800000
|
||||
|
||||
const begin = this.formatDate(new Date(beginStamp), 'hh:mm')
|
||||
|
||||
@@ -53,27 +53,34 @@ export default {
|
||||
},
|
||||
props: {
|
||||
boxTitle: {
|
||||
type: Array
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
boxOperation: {
|
||||
type: Array
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 地域数据
|
||||
dataObj: {
|
||||
type: Object
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
// 已选数据
|
||||
selectedData: {
|
||||
type: Array
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
onChangeSelected: {
|
||||
type: Function
|
||||
type: Function,
|
||||
default: () => () => {}
|
||||
},
|
||||
filterable: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
filterPlaceholder: {
|
||||
type: String
|
||||
type: String,
|
||||
default: () => ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<div class="vip-footer">
|
||||
<el-button
|
||||
type="text"
|
||||
:disabled="selectedDistrict.length > 0 ? false : true"
|
||||
:disabled="selectedDistrict.length<=0"
|
||||
size="small"
|
||||
round
|
||||
@click="checkedSelected"
|
||||
@@ -62,23 +62,29 @@ export default {
|
||||
components: {},
|
||||
props: {
|
||||
title: {
|
||||
type: String
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
operation: {
|
||||
type: String
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
operateId: {
|
||||
type: Number
|
||||
type: Number,
|
||||
default: () => 0
|
||||
},
|
||||
// 区域数据
|
||||
districtList: {
|
||||
type: Array
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
filterable: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
filterPlaceholder: {
|
||||
type: String
|
||||
type: String,
|
||||
default: () => ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -1,61 +1,69 @@
|
||||
<template>
|
||||
<div class="krry-main">
|
||||
<krry-box
|
||||
ref="noSelect"
|
||||
:async="async"
|
||||
:async-search-flag="asyncSearchFlag"
|
||||
:data-show-list="notSelectDataList"
|
||||
:filter-placeholder="filterPlaceholder[0] || $tc('common.Search')"
|
||||
:filterable="filterable"
|
||||
:highlight-color="highlightColor"
|
||||
:is-highlight="isHighlight"
|
||||
:is-last-page="isLastPage"
|
||||
:operate-id="0"
|
||||
:page-size="pageSize"
|
||||
:page-texts="pageTexts"
|
||||
:show-clear-btn="showClearBtn"
|
||||
:title="boxTitle[0] || $tc('common.Selection')"
|
||||
@check-district="noCheckSelect"
|
||||
@search-word="searchWord"
|
||||
@check-disable="checkDisable"
|
||||
@get-data="getData"
|
||||
@get-data-by-keyword="getDataByKeyword"
|
||||
@clear-input="clearQueryInp('left')"
|
||||
/>
|
||||
<div class="opera">
|
||||
<el-button
|
||||
:disabled="disablePre"
|
||||
class="el-transfer__button"
|
||||
icon="el-icon-arrow-left"
|
||||
size="mini"
|
||||
@click="deleteData"
|
||||
/>
|
||||
<el-button
|
||||
:disabled="disableNex"
|
||||
class="el-transfer__button"
|
||||
icon="el-icon-arrow-right"
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click="addData"
|
||||
/>
|
||||
</div>
|
||||
<krry-box
|
||||
ref="hasSelect"
|
||||
:data-show-list="checkedData"
|
||||
:filter-placeholder="filterPlaceholder[1] || $tc('common.Search')"
|
||||
:filterable="filterable"
|
||||
:highlight-color="highlightColor"
|
||||
:is-highlight="isHighlight"
|
||||
:operate-id="1"
|
||||
:page-size="pageSize"
|
||||
:page-texts="pageTexts"
|
||||
:show-clear-btn="showClearBtn"
|
||||
:title="boxTitle[1] || $tc('common.Selected')"
|
||||
@check-district="hasCheckSelect"
|
||||
@search-word="searchWord"
|
||||
@check-disable="checkDisable"
|
||||
@clear-input="clearQueryInp('right')"
|
||||
/>
|
||||
<el-row :gutter="10">
|
||||
<el-col :md="10" :sm="24">
|
||||
<krry-box
|
||||
ref="noSelect"
|
||||
:async="async"
|
||||
:async-search-flag="asyncSearchFlag"
|
||||
:data-show-list="notSelectDataList"
|
||||
:filter-placeholder="filterPlaceholder[0] || $tc('common.Search')"
|
||||
:filterable="filterable"
|
||||
:highlight-color="highlightColor"
|
||||
:is-highlight="isHighlight"
|
||||
:is-last-page="isLastPage"
|
||||
:operate-id="0"
|
||||
:page-size="pageSize"
|
||||
:page-texts="pageTexts"
|
||||
:show-clear-btn="showClearBtn"
|
||||
:title="boxTitle[0] || $tc('common.Selection')"
|
||||
@check-district="noCheckSelect"
|
||||
@search-word="searchWord"
|
||||
@check-disable="checkDisable"
|
||||
@get-data="getData"
|
||||
@get-data-by-keyword="getDataByKeyword"
|
||||
@clear-input="clearQueryInp('left')"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :md="4" :sm="24" class="buttons">
|
||||
<div class="opera">
|
||||
<el-button
|
||||
:disabled="disablePre"
|
||||
class="el-transfer__button"
|
||||
icon="el-icon-arrow-left"
|
||||
size="mini"
|
||||
@click="deleteData"
|
||||
/>
|
||||
<el-button
|
||||
:disabled="disableNex"
|
||||
class="el-transfer__button"
|
||||
icon="el-icon-arrow-right"
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click="addData"
|
||||
/>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :md="10" :sm="24">
|
||||
<krry-box
|
||||
ref="hasSelect"
|
||||
:data-show-list="checkedData"
|
||||
:filter-placeholder="filterPlaceholder[1] || $tc('common.Search')"
|
||||
:filterable="filterable"
|
||||
:highlight-color="highlightColor"
|
||||
:is-highlight="isHighlight"
|
||||
:operate-id="1"
|
||||
:page-size="pageSize"
|
||||
:page-texts="pageTexts"
|
||||
:show-clear-btn="showClearBtn"
|
||||
:title="boxTitle[1] || $tc('common.Selected')"
|
||||
@check-district="hasCheckSelect"
|
||||
@search-word="searchWord"
|
||||
@check-disable="checkDisable"
|
||||
@clear-input="clearQueryInp('right')"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -189,7 +197,7 @@ export default {
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.async ? this.getData(1) : this.initData()
|
||||
this.async ? this.getData(1, true) : this.initData(true)
|
||||
},
|
||||
methods: {
|
||||
// 分页数据,初始化数据,过滤已选数据
|
||||
@@ -370,7 +378,7 @@ export default {
|
||||
await this.getData(1)
|
||||
}
|
||||
},
|
||||
async getData(pageIndex) {
|
||||
async getData(pageIndex, changed = false) {
|
||||
this.$nextTick(() => {
|
||||
// 设置异步分页的 pageIndex
|
||||
this.$refs.noSelect.asyncPageIndex = pageIndex
|
||||
@@ -383,7 +391,8 @@ export default {
|
||||
if (Array.isArray(resData) && resData.length) {
|
||||
this.asyncDataList = resData
|
||||
this.notSelectDataList = resData
|
||||
this.initData(false)
|
||||
// 这里必须是 true,否则右侧不能搜索, 一搜索确认就不行了
|
||||
this.initData(changed)
|
||||
this.isLastPage = resData.length < this.pageSize
|
||||
} else {
|
||||
this.notSelectDataList = []
|
||||
@@ -401,18 +410,27 @@ export default {
|
||||
.inner-center {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.opera {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: 0 8px;
|
||||
text-align: center;
|
||||
margin: 180px 8px;
|
||||
width: 100%;
|
||||
@media screen and (max-width: 992px) {
|
||||
margin: 8px 8px;
|
||||
text-align:start
|
||||
}
|
||||
|
||||
.el-button.is-circle {
|
||||
border-radius: 50%;
|
||||
padding: 12px;
|
||||
display: block;
|
||||
margin: 25px auto;
|
||||
|
||||
}
|
||||
.el-transfer__button {
|
||||
padding: 5px;
|
||||
|
||||
@@ -52,7 +52,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
empty: () => {}
|
||||
empty: () => {
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<el-button v-if="shouldFold" circle class="search-btn" size="mini" @click="handleManualSearch">
|
||||
<svg-icon icon-class="search" />
|
||||
</el-button>
|
||||
<TagSearch v-else :options="iOption" v-bind="$attrs" @tagSearch="handleTagSearch" v-on="$listeners" />
|
||||
<TagSearch v-else :options="iOption" v-bind="$attrs" v-on="$listeners" @tag-search="handleTagSearch" />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -68,6 +68,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleTagSearch(tags) {
|
||||
if (_.isEqual(tags, this.tags)) {
|
||||
return
|
||||
}
|
||||
this.tags = tags
|
||||
if (tags.length === 0) {
|
||||
this.manualSearch = false
|
||||
|
||||
@@ -34,12 +34,14 @@ class StrategyNormal extends StrategyAbstract {
|
||||
onSelectionChange(val) {
|
||||
this.elDataTable.selected = val
|
||||
}
|
||||
|
||||
/**
|
||||
* toggleRowSelection和clearSelection的表现与el-table一致
|
||||
*/
|
||||
toggleRowSelection(...args) {
|
||||
return this.elTable.toggleRowSelection(...args)
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
return this.elTable.clearSelection()
|
||||
}
|
||||
@@ -50,12 +52,12 @@ class StrategyNormal extends StrategyAbstract {
|
||||
*/
|
||||
class StrategyPersistSelection extends StrategyAbstract {
|
||||
/**
|
||||
* el-table的selection-change事件不适用于开启跨页保存的情况。
|
||||
* 比如,当开启persistSelection时,发生以下两个场景:
|
||||
* el-table 的 selection-change 事件不适用于开启跨页保存的情况。
|
||||
* 比如,当开启 persistSelection时,发生以下两个场景:
|
||||
* 1. 用户点击翻页
|
||||
* 2. 用户点击行首的切换全选项按钮,清空当前页多选项数据
|
||||
* 其中场景1应该保持selected不变;而场景2只应该从selected移除当前页所有行,保留其他页面的多选状态。
|
||||
* 但el-table的selection-change事件在两个场景中无差别发生,所以这里不处理这个事件
|
||||
* 其中场景 1 应该保持 selected 不变;而场景 2 只应该从 selected 移除当前页所有行,保留其他页面的多选状态。
|
||||
* 但 el-table 的 selection-change 事件在两个场景中无差别发生,所以这里不处理这个事件
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -63,53 +65,106 @@ class StrategyPersistSelection extends StrategyAbstract {
|
||||
*/
|
||||
onSelect(selection, row) {
|
||||
const isChosen = selection.indexOf(row) > -1
|
||||
|
||||
this.toggleRowSelection(row, isChosen)
|
||||
}
|
||||
/**
|
||||
* 用户切换当前页的多选
|
||||
*/
|
||||
onSelectAll(selection, selectable = () => true) {
|
||||
const isSelected = !!selection.length
|
||||
this.elDataTable.data.forEach(r => {
|
||||
const { id, selected, data } = this.elDataTable
|
||||
|
||||
const selectedIds = new Set(selected.map(r => r[id]))
|
||||
|
||||
// 获取当前所有已选择的项
|
||||
const selectedRows = data.filter(r => selection.includes(r))
|
||||
|
||||
// 判断是否已全选
|
||||
const isSelected = data.every(r => selectable(r) && selectedRows.includes(r))
|
||||
|
||||
const rowsToSelect = []
|
||||
const rowsToDeselect = []
|
||||
|
||||
data.forEach(r => {
|
||||
if (selectable(r)) {
|
||||
this.toggleRowSelection(r, isSelected)
|
||||
const isRowSelected = selectedIds.has(r[id])
|
||||
|
||||
if (isSelected && !isRowSelected) {
|
||||
rowsToSelect.push(r)
|
||||
} else if (!isSelected && isRowSelected) {
|
||||
rowsToDeselect.push(r)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (isSelected) {
|
||||
rowsToSelect.forEach(row => {
|
||||
selected.push(row)
|
||||
selectedIds.add(row[id])
|
||||
})
|
||||
rowsToDeselect.forEach(row => {
|
||||
this.elDataTable.toggleRowSelection(row, true)
|
||||
})
|
||||
} else {
|
||||
rowsToDeselect.forEach(row => {
|
||||
const index = selected.findIndex(item => item[id] === row[id])
|
||||
if (index !== -1) {
|
||||
selected.splice(index, 1)
|
||||
}
|
||||
selectedIds.delete(row[id])
|
||||
})
|
||||
rowsToSelect.forEach(row => {
|
||||
this.elDataTable.toggleRowSelection(row, false)
|
||||
})
|
||||
}
|
||||
|
||||
// this.elTable.selected = Array.from(selectedIds).map(id => {
|
||||
// return data.find(r => r[id] === id)
|
||||
// })
|
||||
}
|
||||
/**
|
||||
* toggleRowSelection和clearSelection管理elDataTable的selected数组
|
||||
* 记得最后要将状态同步到el-table中
|
||||
* toggleRowSelection 和 clearSelection 管理 elDataTable 的 selected 数组
|
||||
* 记得最后要将状态同步到 el-table 中
|
||||
*/
|
||||
toggleRowSelection(row, isSelected) {
|
||||
const { id, selected } = this.elDataTable
|
||||
const foundIndex = selected.findIndex(r => r[id] === row[id])
|
||||
|
||||
if (typeof isSelected === 'undefined') {
|
||||
isSelected = foundIndex <= -1
|
||||
}
|
||||
|
||||
if (isSelected && foundIndex === -1) {
|
||||
selected.push(row)
|
||||
} else if (!isSelected && foundIndex > -1) {
|
||||
selected.splice(foundIndex, 1)
|
||||
}
|
||||
|
||||
this.elDataTable.$emit('toggle-row-selection', isSelected, row)
|
||||
this.updateElTableSelection()
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
this.elDataTable.selected = []
|
||||
this.updateElTableSelection()
|
||||
}
|
||||
|
||||
/**
|
||||
* 将selected状态同步到el-table中
|
||||
*/
|
||||
updateElTableSelection() {
|
||||
const { data, id, selected } = this.elDataTable
|
||||
|
||||
// 历史勾选的行已经不在当前页了,所以要将当前页的行数据和selected合并
|
||||
const mergeData = _.uniqWith([...data, ...selected], _.isEqual)
|
||||
|
||||
mergeData.forEach(r => {
|
||||
const isSelected = !!selected.find(r2 => r[id] === r2[id])
|
||||
|
||||
if (!this.elTable) {
|
||||
return
|
||||
}
|
||||
|
||||
this.elTable.toggleRowSelection(r, isSelected)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -153,6 +153,8 @@ export default {
|
||||
this.toggleRowSelection(row, true)
|
||||
}
|
||||
}
|
||||
|
||||
this.$emit('loaded')
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
localStorage.setItem('paginationSize', val)
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import * as queryUtil from '@/components/Table/DataTable/compenents/el-data-table/utils/query'
|
||||
import { download } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'ExportDialog',
|
||||
@@ -187,10 +188,7 @@ export default {
|
||||
})
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
download(url)
|
||||
},
|
||||
async defaultPerformExport(selectRows, exportOption, q, exportTypeOption) {
|
||||
const url = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import ImportTable from '@/components/Table/ListTable/TableAction/ImportTable.vue'
|
||||
import { getErrorResponseMsg } from '@/utils/common'
|
||||
import { download, getErrorResponseMsg } from '@/utils/common'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
|
||||
export default {
|
||||
@@ -199,7 +199,8 @@ export default {
|
||||
},
|
||||
async getDownloadTemplateUrl(tp) {
|
||||
const template = this.importOption === 'create' ? 'import' : 'update'
|
||||
let query = `format=${tp}&template=${template}`
|
||||
const action = this.importOption === 'create' ? 'create' : 'partial_update'
|
||||
let query = `format=${tp}&template=${template}&action=${action}`
|
||||
if (this.importOption === 'update' && this.selectedRows.length > 0) {
|
||||
const resources = []
|
||||
for (const item of this.selectedRows) {
|
||||
@@ -220,10 +221,7 @@ export default {
|
||||
this.$message.success(msg)
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
download(url)
|
||||
},
|
||||
async handleImportConfirm() {
|
||||
await this.$refs['importTable'].performUpload()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-row type="flex" align="center">
|
||||
<el-col :md="8" :sm="24">
|
||||
<div class="tableFilter">
|
||||
<el-radio-group v-model="importStatusFilter" size="small">
|
||||
@@ -11,7 +11,7 @@
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :md="8" :sm="24" style="text-align: center">
|
||||
<el-col :md="16" :sm="24" style="text-align: center; display: flex; align-items: center">
|
||||
<span class="summary-item summary-total"> {{ $t('common.Total') }}: {{ totalCount }}</span>
|
||||
<span class="summary-item summary-success"> {{ $t('common.Success') }}: {{ successCount }}</span>
|
||||
<span class="summary-item summary-failed"> {{ $t('common.Failed') }}: {{ failedCount }}</span>
|
||||
|
||||
@@ -104,7 +104,7 @@ export default {
|
||||
title: this.$t('common.BatchUpdate'),
|
||||
name: 'actionUpdateSelected',
|
||||
has: this.hasBulkUpdate,
|
||||
icon: 'fa fa-refresh',
|
||||
fa: 'batch-update',
|
||||
can: function({ selectedRows }) {
|
||||
let canBulkUpdate = vm.canBulkUpdate
|
||||
if (typeof canBulkUpdate === 'function') {
|
||||
|
||||
@@ -122,6 +122,9 @@ export default {
|
||||
return this.hasLeftActions ? 'right' : 'left'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$emit('done')
|
||||
},
|
||||
methods: {
|
||||
handleTagSearch(val) {
|
||||
this.searchTable(val)
|
||||
@@ -144,113 +147,121 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.table-header {
|
||||
/*display: flex;*/
|
||||
/*flex-direction: row;*/
|
||||
/*justify-content: space-between;*/
|
||||
}
|
||||
.table-header {
|
||||
/*display: flex;*/
|
||||
/*flex-direction: row;*/
|
||||
/*justify-content: space-between;*/
|
||||
}
|
||||
|
||||
.right-side-item {
|
||||
}
|
||||
.right-side-item {
|
||||
}
|
||||
|
||||
.right-side-actions >>> .el-button {
|
||||
border: none;
|
||||
padding: 5px;
|
||||
font-size: 14px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
color: #888;
|
||||
background-color: transparent;
|
||||
}
|
||||
.right-side-actions > > > .el-button {
|
||||
border: none;
|
||||
padding: 5px;
|
||||
font-size: 14px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
color: #888;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.right-side-actions >>> .fa {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
.right-side-actions > > > .fa {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.right-side-actions >>> .el-button:hover {
|
||||
background-color: rgb(0, 0, 0, 0.05);
|
||||
}
|
||||
.right-side-actions > > > .el-button:hover {
|
||||
background-color: rgb(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.action-search >>> .el-input__suffix i {
|
||||
font-weight: 500;
|
||||
color: #888;
|
||||
}
|
||||
.action-search > > > .el-input__suffix i {
|
||||
font-weight: 500;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.action-search >>> .el-cascader {
|
||||
line-height: 32px !important;
|
||||
}
|
||||
.action-search > > > .el-cascader {
|
||||
line-height: 32px !important;
|
||||
}
|
||||
|
||||
.right-side-actions {
|
||||
display: flex;
|
||||
padding-left: 10px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.right-side-actions {
|
||||
display: flex;
|
||||
padding-left: 10px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.table-action-right-side {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.table-action-right-side {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.export-item {
|
||||
display: block;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
.export-item {
|
||||
display: block;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.datepicker {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.datepicker {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
line-height: 32px;
|
||||
}
|
||||
.table-header {
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.left-side {
|
||||
float: left;
|
||||
display: block;
|
||||
}
|
||||
.left-side {
|
||||
float: left;
|
||||
display: block;
|
||||
|
||||
.right-side {
|
||||
float: right;
|
||||
}
|
||||
& > > > .action-item.el-dropdown {
|
||||
height: 33px;
|
||||
|
||||
.search {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
& > .el-button {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile .search {
|
||||
display: inherit;
|
||||
}
|
||||
.right-side {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mobile .search .datepicker {
|
||||
margin-left: 0;
|
||||
}
|
||||
.search {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.search.left {
|
||||
float: left;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.mobile .search {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.search.right {
|
||||
float: right;
|
||||
}
|
||||
.mobile .search .datepicker {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.mobile .search.right {
|
||||
float: none;
|
||||
}
|
||||
.search.left {
|
||||
float: left;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.mobile .search.right .action-search {
|
||||
width: 100%;
|
||||
}
|
||||
.search.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mobile .right-side {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.mobile .search.right {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.filter-field.right-side-item.action-search {
|
||||
height: 34px;
|
||||
}
|
||||
.mobile .search.right .action-search {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mobile .right-side {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.filter-field.right-side-item.action-search {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
:selected-rows="selectedRows"
|
||||
:table-url="tableUrl"
|
||||
v-bind="iHeaderActions"
|
||||
@done="handleActionInitialDone"
|
||||
/>
|
||||
<IBox class="table-content">
|
||||
<AutoDataTable
|
||||
v-if="actionInit"
|
||||
ref="dataTable"
|
||||
:config="iTableConfig"
|
||||
:filter-table="filter"
|
||||
@@ -73,7 +75,10 @@ export default {
|
||||
return {
|
||||
selectedRows: [],
|
||||
init: false,
|
||||
extraQuery: extraQuery
|
||||
isDeactivated: false,
|
||||
extraQuery: extraQuery,
|
||||
actionInit: this.headerActions.has === false,
|
||||
initQuery: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -166,19 +171,43 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleActionInitialDone() {
|
||||
setTimeout(() => {
|
||||
this.actionInit = true
|
||||
}, 100)
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.selectedRows = val
|
||||
},
|
||||
reloadTable() {
|
||||
this.dataTable.getList()
|
||||
},
|
||||
updateInitQuery(attrs) {
|
||||
if (!this.actionInit) {
|
||||
this.initQuery = attrs
|
||||
for (const key in attrs) {
|
||||
this.$set(this.extraQuery, key, attrs[key])
|
||||
}
|
||||
return true
|
||||
}
|
||||
const removeKeys = Object.keys(this.initQuery).filter(key => !attrs[key])
|
||||
for (const key of removeKeys) {
|
||||
this.$delete(this.extraQuery, key)
|
||||
}
|
||||
},
|
||||
search(attrs) {
|
||||
this.$log.debug('ListTable: search table', attrs)
|
||||
const init = this.updateInitQuery(attrs)
|
||||
if (init) {
|
||||
return
|
||||
}
|
||||
this.$emit('TagSearch', attrs)
|
||||
return this.dataTable?.search(attrs, true)
|
||||
this.$refs.dataTable?.$refs.dataTable?.search(attrs, true)
|
||||
},
|
||||
filter(attrs) {
|
||||
this.$emit('TagFilter', attrs)
|
||||
this.$refs.dataTable.$refs.dataTable.search(attrs, true)
|
||||
this.$log.debug('ListTable: found filter change', attrs)
|
||||
this.search(attrs)
|
||||
},
|
||||
hasActionPerm(action) {
|
||||
const permRequired = this.permissions[action]
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<el-popover
|
||||
:title="title"
|
||||
placement="left-start"
|
||||
trigger="click"
|
||||
@show="getAsyncItems"
|
||||
>
|
||||
<div class="detail-content">
|
||||
<div v-for="account of accountData" :key="account.id" class="detail-item">
|
||||
<span>{{ account.name }}({{ account.username }})</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-button slot="reference" size="mini" type="primary">{{ $t('common.View') }}</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base.vue'
|
||||
|
||||
export default {
|
||||
name: 'SwitchFormatter',
|
||||
extends: BaseFormatter,
|
||||
data() {
|
||||
return {
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs),
|
||||
value: this.cellValue,
|
||||
accountData: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.formatterArgs.title || this.col.label
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getAsyncItems() {
|
||||
const userId = this.$route.params.id
|
||||
const url = `/api/v1/perms/users/${userId}/assets/${this.row.id}`
|
||||
this.$axios.get(url).then(res => {
|
||||
this.accountData = res?.permed_accounts || []
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detail-content {
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
padding: 5px 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: #F5F7FA;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -3,17 +3,19 @@
|
||||
<template>
|
||||
<el-popover
|
||||
:disabled="!showItems"
|
||||
:open-delay="500"
|
||||
:title="title"
|
||||
placement="top-start"
|
||||
trigger="hover"
|
||||
width="400"
|
||||
@show="getAsyncItems"
|
||||
>
|
||||
<div class="detail-content">
|
||||
<div v-for="item of items" :key="getKey(item)" class="detail-item">
|
||||
<span class="detail-item-name">{{ item }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="reference">{{ items && items.length }}</span>
|
||||
<span slot="reference">{{ amount }}</span>
|
||||
</el-popover>
|
||||
</template>
|
||||
</DetailFormatter>
|
||||
@@ -37,55 +39,111 @@ export default {
|
||||
showItems: true,
|
||||
getItem(item) {
|
||||
return item.name
|
||||
}
|
||||
},
|
||||
async: false,
|
||||
ajax: {},
|
||||
title: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const formatterArgs = Object.assign(this.formatterArgsDefault, this.col.formatterArgs || {})
|
||||
return {
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs || {})
|
||||
formatterArgs: formatterArgs,
|
||||
data: formatterArgs.async ? [] : (this.cellValue || []),
|
||||
amount: '',
|
||||
asyncGetDone: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.formatterArgs.title || ''
|
||||
return this.formatterArgs.title || this.col.label.replace('amount', '').replace('数量', '')
|
||||
},
|
||||
cellValueToRemove() {
|
||||
return this.formatterArgs.cellValueToRemove || []
|
||||
},
|
||||
items() {
|
||||
if (this.formatterArgs.async && !this.asyncGetDone) {
|
||||
return [this.$t('common.tree.Loading') + '...']
|
||||
}
|
||||
const getItem = this.formatterArgs.getItem || (item => item.name)
|
||||
let data = this.cellValue?.map(item => getItem(item)) || []
|
||||
let data = []
|
||||
if (Array.isArray(this.data)) {
|
||||
data = this.data.map(item => getItem(item)) || []
|
||||
} else {
|
||||
// object {key: [value]}
|
||||
data = Object.entries(this.data).map(([key, value]) => {
|
||||
const item = { key: key, value: value }
|
||||
return getItem(item)
|
||||
}) || []
|
||||
}
|
||||
data = data.filter(Boolean)
|
||||
return data
|
||||
},
|
||||
showItems() {
|
||||
return this.formatterArgs.showItems !== false && this.cellValue?.length > 0
|
||||
return this.amount !== 0 && this.amount !== ''
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (this.formatterArgs.async) {
|
||||
this.amount = this.cellValue
|
||||
} else {
|
||||
let cellValue = []
|
||||
if (Array.isArray(this.cellValue)) {
|
||||
cellValue = this.cellValue
|
||||
} else {
|
||||
// object {key: [value]}
|
||||
cellValue = Object.keys(this.cellValue)
|
||||
}
|
||||
this.amount = (cellValue?.filter(value => !this.cellValueToRemove.includes(value)) || []).length
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getKey(item) {
|
||||
const id = Math.random().toString(36).substring(2)
|
||||
const id = Math.random().toString(36).substring(16)
|
||||
return id + item
|
||||
},
|
||||
getDefaultUrl() {
|
||||
const url = new URL(this.url, location.origin)
|
||||
url.pathname += this.row.id + '/'
|
||||
return url.pathname
|
||||
},
|
||||
async getAsyncItems() {
|
||||
if (!this.formatterArgs.async) {
|
||||
return
|
||||
}
|
||||
if (this.asyncGetDone) {
|
||||
return
|
||||
}
|
||||
const url = this.formatterArgs.ajax.url || this.getDefaultUrl()
|
||||
const params = this.formatterArgs.ajax.params || {}
|
||||
const transform = this.formatterArgs.ajax.transform || (resp => resp[this.col.prop.replace('_amount', '')])
|
||||
const response = await this.$axios.get(url, { params: params })
|
||||
this.data = transform(response)
|
||||
this.asyncGetDone = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.detail-content {
|
||||
padding: 20px 10px;
|
||||
padding: 5px 10px;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
padding: 5px 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: #F5F7FA;
|
||||
background-color: #F5F7FA;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-item:first-child {
|
||||
border-top: 1px solid #EBEEF5;
|
||||
//border-top: 1px solid #EBEEF5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<span>{{ value }}</span>
|
||||
<span class="date">{{ dateValue }}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -10,24 +10,31 @@ export default {
|
||||
name: 'DateFormatter',
|
||||
extends: BaseFormatter,
|
||||
data() {
|
||||
let value
|
||||
if (this.cellValue) {
|
||||
value = toSafeLocalDateStr(this.cellValue)
|
||||
} else {
|
||||
value = '-'
|
||||
}
|
||||
// let value
|
||||
// if (this.cellValue) {
|
||||
// value = toSafeLocalDateStr(this.cellValue)
|
||||
// } else {
|
||||
// value = '-'
|
||||
// }
|
||||
// const locale = this.$i18n.locale
|
||||
// const value = dt.toLocaleString(locale, { hourCycle: 'h23' })
|
||||
// debug(this.$i18n.locale)
|
||||
return {
|
||||
value: value
|
||||
}
|
||||
// return {
|
||||
// value: value
|
||||
// }
|
||||
// return {
|
||||
// value: `${year}-${month}-${date} ${hour}:${minutes}:${seconds}`
|
||||
// }
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
dateValue() {
|
||||
if (this.cellValue) {
|
||||
return toSafeLocalDateStr(this.cellValue)
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
:disabled="disabled"
|
||||
:type="col.type || 'info'"
|
||||
class="detail"
|
||||
:class="{ 'clicked': linkClicked }"
|
||||
@click="goDetail"
|
||||
>
|
||||
<slot>
|
||||
@@ -30,6 +31,7 @@ export default {
|
||||
routeQuery: null,
|
||||
can: true,
|
||||
openInNewPage: false,
|
||||
removeColorOnClick: false,
|
||||
getTitle({ col, row, cellValue }) {
|
||||
return cellValue
|
||||
},
|
||||
@@ -43,6 +45,7 @@ export default {
|
||||
data() {
|
||||
const formatterArgs = Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
return {
|
||||
linkClicked: false,
|
||||
formatterArgs: formatterArgs
|
||||
}
|
||||
},
|
||||
@@ -100,6 +103,7 @@ export default {
|
||||
methods: {
|
||||
goDetail() {
|
||||
if (this.formatterArgs.openInNewPage) {
|
||||
this.linkClicked = this.formatterArgs.removeColorOnClick
|
||||
const { href } = this.$router.resolve(this.detailRoute)
|
||||
window.open(href, '_blank')
|
||||
} else {
|
||||
@@ -125,6 +129,11 @@ export default {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.clicked,
|
||||
.el-link.el-link--info.clicked {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
|
||||
@@ -64,7 +64,7 @@ export default {
|
||||
}
|
||||
return text
|
||||
}
|
||||
return '-'
|
||||
return this.items?.distribution || '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="edit-btn" style="padding-left: 5px" @click="showDialog = true"> <i class="fa fa-edit" /></a>
|
||||
<a
|
||||
v-if="formatterArgs.showEditBtn"
|
||||
:class="[{ 'disabled-link': this.$store.getters.currentOrgIsRoot },'edit-btn']"
|
||||
style="padding-left: 5px"
|
||||
@click="showDialog = true"
|
||||
>
|
||||
<i class="fa fa-edit" />
|
||||
</a>
|
||||
<Dialog
|
||||
v-if="showDialog"
|
||||
:title="$tc('labels.BindLabel')"
|
||||
@@ -96,7 +103,8 @@ export default {
|
||||
getLabels(cellValue) {
|
||||
return cellValue
|
||||
},
|
||||
config: {}
|
||||
config: {},
|
||||
showEditBtn: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,4 +269,11 @@ export default {
|
||||
.tag-tip {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.disabled-link {
|
||||
pointer-events: none;
|
||||
color: grey;
|
||||
cursor: default;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
64
src/components/Table/TableFormatters/SwitchFormatter.vue
Normal file
64
src/components/Table/TableFormatters/SwitchFormatter.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div v-if="display">
|
||||
<el-switch v-model="value" @change="onChange" />
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base.vue'
|
||||
|
||||
export default {
|
||||
name: 'SwitchFormatter',
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
getPatchUrl(row) {
|
||||
return ''
|
||||
},
|
||||
getPatchData(row) {
|
||||
return {}
|
||||
},
|
||||
isDisplay(row) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs),
|
||||
value: this.cellValue
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
patchUrl() {
|
||||
return this.formatterArgs.getPatchUrl(this.row)
|
||||
},
|
||||
patchData() {
|
||||
return this.formatterArgs.getPatchData(this.row)
|
||||
},
|
||||
display(row) {
|
||||
return this.formatterArgs.isDisplay(this.row)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(val) {
|
||||
this.$axios.patch(this.patchUrl, this.patchData).then(res => {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
}).catch(err => {
|
||||
this.value = !val
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -16,6 +16,8 @@ import ObjectRelatedFormatter from './ObjectRelatedFormatter.vue'
|
||||
import TwoTabFormatter from './TwoTabFormatter.vue'
|
||||
import ProtocolsFormatter from './ProtocolsFormatter.vue'
|
||||
import TagChoicesFormatter from './TagChoicesFormatter.vue'
|
||||
import SwitchFormatter from './SwitchFormatter.vue'
|
||||
import AccountInfoFormatter from './AccountInfoFormatter.vue'
|
||||
|
||||
export default {
|
||||
DetailFormatter,
|
||||
@@ -35,7 +37,9 @@ export default {
|
||||
TwoTabFormatter,
|
||||
ProtocolsFormatter,
|
||||
TagChoicesFormatter,
|
||||
LabelsFormatter
|
||||
LabelsFormatter,
|
||||
SwitchFormatter,
|
||||
AccountInfoFormatter
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -56,5 +60,7 @@ export {
|
||||
TwoTabFormatter,
|
||||
ProtocolsFormatter,
|
||||
TagChoicesFormatter,
|
||||
LabelsFormatter
|
||||
LabelsFormatter,
|
||||
SwitchFormatter,
|
||||
AccountInfoFormatter
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
<el-tag
|
||||
v-for="(v, k) in filterTags"
|
||||
:key="k"
|
||||
:disable-transitions="true"
|
||||
:name="k"
|
||||
class="filter-tag"
|
||||
closable
|
||||
size="small"
|
||||
class="filter-tag"
|
||||
type="info"
|
||||
:disable-transitions="true"
|
||||
@close="handleTagClose(k)"
|
||||
@click="handleTagClick(v,k)"
|
||||
@close="handleTagClose(k)"
|
||||
>
|
||||
<strong v-if="v.label">{{ v.label + ':' }}</strong>
|
||||
<span v-if="v.valueLabel">{{ v.valueLabel }}</span>
|
||||
@@ -27,14 +27,14 @@
|
||||
<el-input
|
||||
ref="SearchInput"
|
||||
v-model="filterValue"
|
||||
:placeholder="placeholder"
|
||||
class="search-input"
|
||||
:class="options.length < 1 ? 'search-input2': ''"
|
||||
:placeholder="placeholder"
|
||||
:validate-event="false"
|
||||
class="search-input"
|
||||
suffix-icon="el-icon-search"
|
||||
@blur="focus = false"
|
||||
@focus="focus = true"
|
||||
@change="handleConfirm"
|
||||
@focus="focus = true"
|
||||
@keyup.enter.native="handleConfirm"
|
||||
@keyup.delete.native="handleDelete"
|
||||
/>
|
||||
@@ -48,7 +48,8 @@ export default {
|
||||
props: {
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
default: () => {
|
||||
}
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
@@ -56,7 +57,7 @@ export default {
|
||||
},
|
||||
getUrlQuery: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
default: () => false
|
||||
},
|
||||
default: {
|
||||
type: Object,
|
||||
@@ -122,6 +123,12 @@ export default {
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
filterTags: {
|
||||
handler() {
|
||||
this.$emit('tag-search', this.filterMaps)
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
filterValue(newValue, oldValue) {
|
||||
if (newValue === '' && oldValue !== '') {
|
||||
this.emptyCount = 1
|
||||
@@ -210,11 +217,6 @@ export default {
|
||||
...asFilterTags,
|
||||
...routeFilter
|
||||
}
|
||||
if (Object.keys(this.filterTags).length > 0) {
|
||||
setTimeout(() => {
|
||||
return this.$emit('tagSearch', this.filterMaps)
|
||||
}, 400)
|
||||
}
|
||||
},
|
||||
getValueLabel(key, value) {
|
||||
for (const field of this.options) {
|
||||
@@ -252,7 +254,7 @@ export default {
|
||||
if (this.getUrlQuery) {
|
||||
this.checkUrlFields(evt)
|
||||
}
|
||||
this.$emit('tagSearch', this.filterMaps)
|
||||
// this.$emit('tagSearch', this.filterMaps)
|
||||
return true
|
||||
},
|
||||
handleDelete() {
|
||||
@@ -284,7 +286,7 @@ export default {
|
||||
valueLabel: this.valueLabel
|
||||
}
|
||||
this.$set(this.filterTags, this.filterKey, tag)
|
||||
this.$emit('tagSearch', this.filterMaps)
|
||||
// this.$emit('tagSearch', this.filterMaps)
|
||||
|
||||
// 修改查询参数时改变url中保存的参数
|
||||
if (this.getUrlQuery) {
|
||||
@@ -342,70 +344,77 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.filter-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 198px;
|
||||
border: 1px solid #dcdee2;
|
||||
border-radius: 3px;
|
||||
background-color:#fff;
|
||||
.filter-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 198px;
|
||||
border: 1px solid #dcdee2;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
|
||||
}
|
||||
.search-input >>> .el-input__suffix {
|
||||
cursor: pointer;
|
||||
}
|
||||
.search-input2 >>> .el-input__inner {
|
||||
text-indent: 5px;
|
||||
}
|
||||
.search-input >>> .el-input__inner {
|
||||
/*max-width:inherit !important;*/
|
||||
}
|
||||
|
||||
max-width: 200px;
|
||||
border: none;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.el-input >>> .el-input__inner{
|
||||
border: none !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
.search-input > > > .el-input__suffix {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.filterTitle {
|
||||
padding-right: 2px;
|
||||
line-height: 100%;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
border-collapse: separate;
|
||||
box-sizing: border-box;
|
||||
color: rgb(96, 98, 102);
|
||||
display: inline;
|
||||
font-size: 13px;
|
||||
height: auto;
|
||||
}
|
||||
.filter-tag{
|
||||
margin: 2px 4px 2px 0;
|
||||
}
|
||||
.el-icon--right{
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
.search-input2 > > > .el-input__inner {
|
||||
text-indent: 5px;
|
||||
}
|
||||
|
||||
.filter-field >>> .el-cascader .el-input--suffix .el-input__inner {
|
||||
padding-right: 20px;
|
||||
}
|
||||
.search-input > > > .el-input__inner {
|
||||
/*max-width:inherit !important;*/
|
||||
|
||||
.filter-field >>> .el-cascader .el-input input {
|
||||
width: 0;
|
||||
border: none;
|
||||
}
|
||||
max-width: 200px;
|
||||
border: none;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.filter-field >>> .el-input__inner {
|
||||
height: 30px;
|
||||
}
|
||||
.el-input > > > .el-input__inner {
|
||||
border: none !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.el-cascader-menu__wrap {
|
||||
height: inherit;
|
||||
}
|
||||
.filterTitle {
|
||||
padding-right: 2px;
|
||||
line-height: 100%;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
border-collapse: separate;
|
||||
box-sizing: border-box;
|
||||
color: rgb(96, 98, 102);
|
||||
display: inline;
|
||||
font-size: 13px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.filter-tag {
|
||||
margin: 2px 4px 2px 0;
|
||||
}
|
||||
|
||||
.el-icon--right {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.filter-field > > > .el-cascader .el-input--suffix .el-input__inner {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.filter-field > > > .el-cascader .el-input input {
|
||||
width: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.filter-field > > > .el-input__inner {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.el-cascader-menu__wrap {
|
||||
height: inherit;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -91,7 +91,7 @@ export default {
|
||||
let treeUrl
|
||||
this.loading = true
|
||||
if (refresh && this.treeSetting.treeUrl.indexOf('/perms/') !== -1 &&
|
||||
this.treeSetting.treeUrl.indexOf('rebuild_tree') === -1
|
||||
this.treeSetting.treeUrl.indexOf('rebuild_tree') === -1
|
||||
) {
|
||||
treeUrl = (this.treeSetting.treeUrl.indexOf('?') === -1)
|
||||
? `${this.treeSetting.treeUrl}?rebuild_tree=1`
|
||||
@@ -162,11 +162,15 @@ export default {
|
||||
</span>`
|
||||
if (rootNode) {
|
||||
const $rootNodeRef = $('#' + rootNode.tId + '_a')
|
||||
$rootNodeRef.css({ 'width': 'calc(100% - 68px)', 'overflow': 'hidden', 'text-overflow': 'ellipsis' })
|
||||
$rootNodeRef.after(icons)
|
||||
}
|
||||
},
|
||||
async refresh() {
|
||||
this.treeSearchValue = ''
|
||||
if (this.treeSetting?.callback?.beforeRefresh) {
|
||||
this.treeSetting.callback.beforeRefresh()
|
||||
}
|
||||
if (this.treeSetting?.callback?.refresh) {
|
||||
await this.treeSetting.callback.refresh()
|
||||
}
|
||||
@@ -387,6 +391,8 @@ div.rMenu li {
|
||||
text-shadow: none;
|
||||
top: 100%;
|
||||
z-index: 1000;
|
||||
height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ztree ::v-deep .fa {
|
||||
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
showRenameBtn: false,
|
||||
drag: {
|
||||
isCopy: false,
|
||||
isMove: false
|
||||
isMove: true
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
|
||||
@@ -18,14 +18,15 @@
|
||||
/>
|
||||
</el-col>
|
||||
<el-col v-show="isShow" :span="span">
|
||||
<VueMarkdown class="result-html" :source="iValue" :show="true" :html="true" />
|
||||
<VueMarkdown class="result-html" :source="sanitizedValue" :html="false" :show="true" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<VueMarkdown v-else class="source" :source="iValue" :html="true" />
|
||||
<VueMarkdown v-else class="source" :html="false" :source="sanitizedValue" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DOMPurify from 'dompurify'
|
||||
import VueMarkdown from 'vue-markdown'
|
||||
import 'github-markdown-css/github-markdown-light.css'
|
||||
|
||||
@@ -56,6 +57,17 @@ export default {
|
||||
iValue: this.value
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sanitizedValue() {
|
||||
// 转义特殊字符
|
||||
let content = this.iValue.replace(/\\/g, '\\\\').replace(/\$/g, '\\$')
|
||||
|
||||
// 使用 DOMPurify 进行 XSS 过滤
|
||||
content = DOMPurify.sanitize(content)
|
||||
|
||||
return content
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.resizeObserver = new ResizeObserver(entries => {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
import 'xterm/css/xterm.css'
|
||||
import { Terminal } from 'xterm'
|
||||
import { FitAddon } from 'xterm-addon-fit'
|
||||
import { downloadText } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'Term',
|
||||
@@ -47,6 +48,7 @@ export default {
|
||||
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
|
||||
lineHeight: 1.2,
|
||||
fontSize: 13,
|
||||
scrollback: 9999999,
|
||||
rightClickSelectsWord: true,
|
||||
theme: {
|
||||
background: '#fff',
|
||||
@@ -75,6 +77,16 @@ export default {
|
||||
callback: () => {
|
||||
this.xterm.reset()
|
||||
}
|
||||
},
|
||||
{
|
||||
tip: this.$tc('common.Export'),
|
||||
icon: 'download',
|
||||
callback: () => {
|
||||
this.xterm.selectAll()
|
||||
const text = this.xterm.getSelection()
|
||||
const filename = `${this.$route.query?.type}_${this.$route.query?.taskId}.log`
|
||||
downloadText(text, filename)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -27,6 +27,20 @@ export default {
|
||||
hour: 'numeric', minute: 'numeric', hour12: true
|
||||
}
|
||||
},
|
||||
'zh_hant': {
|
||||
short: {
|
||||
year: 'numeric', month: 'short', day: 'numeric'
|
||||
},
|
||||
medium: {
|
||||
year: 'numeric', month: '2-digit', day: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit', second: '2-digit',
|
||||
hourCycle: 'h23', hour12: false
|
||||
},
|
||||
long: {
|
||||
year: 'numeric', month: 'short', day: 'numeric',
|
||||
hour: 'numeric', minute: 'numeric', hour12: true
|
||||
}
|
||||
},
|
||||
'ja': {
|
||||
short: {
|
||||
year: 'numeric', month: 'short', day: 'numeric'
|
||||
|
||||
@@ -10,7 +10,12 @@ Vue.use(VueI18n)
|
||||
const cookieLang = VueCookie.get('django_language')
|
||||
const browserLang = navigator.systemLanguage || navigator.language
|
||||
let lang = cookieLang || browserLang || 'zh'
|
||||
lang = lang.slice(0, 2)
|
||||
if (lang === 'zh-hant') {
|
||||
lang = 'zh_hant'
|
||||
} else {
|
||||
lang = lang.slice(0, 2)
|
||||
}
|
||||
|
||||
const i18n = new VueI18n({
|
||||
locale: lang,
|
||||
fallbackLocale: 'en',
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"": "",
|
||||
"accounts": {
|
||||
"AccountName": "Account name",
|
||||
"AccountBatchUpdate": "Batch update(same type)",
|
||||
"SuFrom": "Su from",
|
||||
"GenerateSuccessMsg": "Accounts generated successfully",
|
||||
"GenerateAccounts": "Regenerate accounts",
|
||||
@@ -31,6 +33,8 @@
|
||||
"AccountGatherList": "Gather task"
|
||||
},
|
||||
"AccountChangeSecret": {
|
||||
"OldSecret": "Old secret",
|
||||
"NewSecret": "New secret",
|
||||
"Result": "Result",
|
||||
"ParamsHelpText": "The change secret parameter settings are currently only effective for assets with a platform type of host.",
|
||||
"ExecutionTimes": "Execution times",
|
||||
@@ -77,6 +81,7 @@
|
||||
"ExecutionDetail": "Execution detail",
|
||||
"Name": "Name",
|
||||
"Retry": "Retry",
|
||||
"BatchRetry": "Batch retry",
|
||||
"Timer": "Timed execution",
|
||||
"Detail": "Detail",
|
||||
"TimeDelta": "Time delta",
|
||||
@@ -109,7 +114,7 @@
|
||||
"ExecutionList": "Execution list",
|
||||
"Reason": "Reason",
|
||||
"AccountBackup": "Account backup",
|
||||
"RecipientHelpText": "If both recipients A and B are set, the account key will be split into two parts: front and back",
|
||||
"RecipientHelpText": "If both recipients A and B are set, the account key will be split into two parts: front and back. If the user has not set an encryption password, please go to Personal Information ->Preferences ->Set Encryption Password",
|
||||
"RecipientServer": "Receiving server"
|
||||
},
|
||||
"DynamicUsername": "Dynamic username",
|
||||
@@ -124,7 +129,11 @@
|
||||
"AddAccountResult": "Add account result",
|
||||
"AutoPush": "Auto Push",
|
||||
"GeneralAccounts": "General Accounts",
|
||||
"VirtualAccounts": "Virtual Accounts"
|
||||
"VirtualAccounts": "Virtual Accounts",
|
||||
"AccountDeleteConfirmMsg": "Delete account, do you want to continue?",
|
||||
"Test": "Test",
|
||||
"QuickTest": "Quick Test",
|
||||
"BulkVerify": "Bulk Verify Connectivity"
|
||||
},
|
||||
"acl": {
|
||||
"CommandFilterACLHelpMsg": "You can control whether commands can be executed on assets. Based on the rules, certain commands can be allowed while others are prohibited.",
|
||||
@@ -254,6 +263,7 @@
|
||||
"SecretType": "Secret type",
|
||||
"PrivilegedTemplate": "Privileged",
|
||||
"InitialDeploy": "Initial deploy",
|
||||
"OnlyInitialDeploy": "Initial Setup",
|
||||
"Address": "Address",
|
||||
"PrivateKey": "Private key",
|
||||
"Secret": "Secret",
|
||||
@@ -475,7 +485,11 @@
|
||||
"WebUpdate": "Update asset - Web",
|
||||
"DatabaseUpdate": "Update asset - Database",
|
||||
"GPTCreate": "Create asset - GPT",
|
||||
"AppletHostDomainHelpText": "These domains are in System Organization"
|
||||
"AppletHostDomainHelpText": "These domains are in System Organization",
|
||||
"OracleDBNameHelpText": "Tips: Enter the Oracle database's SID or Service Name",
|
||||
"Remove": "Remove",
|
||||
"AddGatewayInDomain": "Add gateway",
|
||||
"AddAssetInDomain": "Add asset"
|
||||
},
|
||||
"audits": {
|
||||
"ChangeField": "Change field",
|
||||
@@ -553,6 +567,7 @@
|
||||
"Automations": "Automation",
|
||||
"Sync": "Sync",
|
||||
"Deploy": "Deploy",
|
||||
"Uninstall": "Uninstall",
|
||||
"Detail": "Detail",
|
||||
"Selector": "Selector",
|
||||
"NoContent": "No content",
|
||||
@@ -706,6 +721,7 @@
|
||||
"BatchActivate": "Batch activate",
|
||||
"SyncSelected": "Sync selected",
|
||||
"bulkDeploy": "Bulk deploy",
|
||||
"BulkVerify": "Bulk verify",
|
||||
"bulkDeleteErrorMsg": "Bulk delete failed: ",
|
||||
"bulkDeleteSuccessMsg": "Bulk delete success",
|
||||
"bulkRemoveErrorMsg": "Bulk remove failed: ",
|
||||
@@ -746,6 +762,7 @@
|
||||
"Prompt": "Prompt",
|
||||
"InputMessage": "Input message...",
|
||||
"CollapseSidebar": "Collapse the sidebar",
|
||||
"crontabDiffError": "Please ensure that the interval for scheduled execution is no less than ten minutes!",
|
||||
"introduction": {
|
||||
"ConceptTitle": "🤔 Python interpreter",
|
||||
"ConceptContent": "I want you to act like a Python interpreter. I will give you Python code, and you will execute it. Do not provide any explanations. Do not respond with anything except the output of the code. ",
|
||||
@@ -968,29 +985,30 @@
|
||||
"LoginCount": "Login count",
|
||||
"LoginOverview": "Sessions overview",
|
||||
"LoginTo": "Login to",
|
||||
"LoginUsers": "Active accounts",
|
||||
"ActiveUsers": "Active users",
|
||||
"Monthly": "Monthly",
|
||||
"CurrentConnections": "Current connections",
|
||||
"TodayFailedConnections": "Connections failed today",
|
||||
"CurrentConnectionUsers": "Current connection users",
|
||||
"TodayFailedConnections": "Number of failed sessions today",
|
||||
"OnlineSessions": "Online sessions",
|
||||
"OnlineUserDevices": "Online user devices",
|
||||
"RealTimeData": "Real-time data",
|
||||
"UserAssetActivity": "Account/Asset activity",
|
||||
"UserData": "Account data",
|
||||
"LoginUserToday": "Login account today",
|
||||
"UserAssetActivity": "User/asset activity status",
|
||||
"UserData": "User data",
|
||||
"LoginUserToday": "Login accounts today",
|
||||
"AssetData": "Asset data",
|
||||
"LoginAssetToday": "Active assets today",
|
||||
"WeekAdd": "New this week",
|
||||
"ProportionOfAssetTypes": "Proportion of asset types",
|
||||
"Proportion": "Proportion",
|
||||
"LoginUserRanking": "Login account ranking",
|
||||
"ActiveAssetRanking": "Login asset ranking",
|
||||
"LoginUserRanking": "Session user ranking",
|
||||
"ActiveAssetRanking": "Session asset ranking",
|
||||
"AssetName": "Asset name",
|
||||
"NumberOfVisits": "Number of visits",
|
||||
"ranking": "Ranking",
|
||||
"Today": "Today",
|
||||
"Last7Days": "Last 7 days",
|
||||
"Last30Days": "Last30 days",
|
||||
"Last7Days": "Last 7d",
|
||||
"Last30Days": "Last 30d",
|
||||
"OnlineUsers": "Online accounts",
|
||||
"ConnectUsers": "Connect accounts",
|
||||
"Num": "Num",
|
||||
@@ -1008,13 +1026,14 @@
|
||||
"BatchCommandNotExecuted": "Batch command not executed",
|
||||
"ExecuteFailedCommand": "Execute failed command",
|
||||
"SessionTrend": "Session trend",
|
||||
"SessionConnectTrend": "Session connection trends",
|
||||
"UserLoginTrend": "Account login trend",
|
||||
"TimesWeekUnit": "times/week",
|
||||
"TopAssetsOfWeek": "Top assets of week",
|
||||
"TopUsersOfWeek": "Top user of week",
|
||||
"User": "User",
|
||||
"UserRatio": "User Ratio",
|
||||
"UsersTotal": "Accounts total",
|
||||
"UsersTotal": "User total",
|
||||
"Weekly": "Weekly",
|
||||
"TotalJobFailed": "Total job failed",
|
||||
"TotalJobRunning": "Total job running",
|
||||
@@ -1193,7 +1212,9 @@
|
||||
"FileSizeExceedsLimit": "File size exceeds limit",
|
||||
"runSucceed": "Task executed successfully",
|
||||
"EnterUploadPath": "Enter the upload path",
|
||||
"FileNameTooLong": "File name too long"
|
||||
"FileNameTooLong": "File name too long",
|
||||
"StopJob": "Stop job",
|
||||
"StopLogOutput": "Task Canceled: The current task (currentTaskId) has been manually stopped. Since the progress of each task varies, the following is the final execution result of the task. A failed execution indicates that the task has been successfully stopped."
|
||||
},
|
||||
"perms": {
|
||||
"": "",
|
||||
@@ -1471,7 +1492,7 @@
|
||||
"TicketsDone": "Ticket Done",
|
||||
"TemplateUpdate": "Update template",
|
||||
"Session": "Session",
|
||||
"Templates": "模版管理",
|
||||
"Templates": "Templates",
|
||||
"AssetUserList": "Asset user",
|
||||
"UserLoginACLUpdate": "Update User Login ACL",
|
||||
"JobCreate": "Create job",
|
||||
@@ -1598,7 +1619,8 @@
|
||||
"remoteAddr": "Remote addr",
|
||||
"replay": "replay",
|
||||
"replaySession": "Replay session",
|
||||
"replayStorage": "Object storage",
|
||||
"replayStorage": "Replay storage",
|
||||
"objectStorage": "Object storage",
|
||||
"riskLevel": "Risk level",
|
||||
"session": "Session",
|
||||
"sshPort": "SSH port",
|
||||
@@ -1757,6 +1779,7 @@
|
||||
"LDAPServerInfo": "LDAP Server",
|
||||
"LDAPUser": "LDAP User",
|
||||
"ChatAI": "Chat ai",
|
||||
"Example": "Example: {example}",
|
||||
"InsecureCommandAlert": "Insecure command alert",
|
||||
"helpText": {
|
||||
"TempPassword": "For a while, there is a period of 300 seconds, failure immediately after use",
|
||||
@@ -1908,6 +1931,7 @@
|
||||
"CheckViewAcceptor": "View more acceptor",
|
||||
"Assignees": "Assignees",
|
||||
"Close": "Close",
|
||||
"CancelTicket": "Cancel Ticket",
|
||||
"OpenStatus": "Open",
|
||||
"CloseStatus": "Close",
|
||||
"Comment": "Comment",
|
||||
@@ -1978,6 +2002,7 @@
|
||||
"UpdateNodeAssetHardwareInfo": "Update node asset hardware information"
|
||||
},
|
||||
"users": {
|
||||
"OrgsAndRoles": "Organizations and roles",
|
||||
"LunaSettingUpdate": "Luna setting",
|
||||
"KokoSettingUpdate": "Koko setting",
|
||||
"UserSetting": "User setting",
|
||||
@@ -2000,7 +2025,7 @@
|
||||
"Account": "Account",
|
||||
"Existing": "Existing",
|
||||
"UserInformation": "User information",
|
||||
"Authentication": "Account",
|
||||
"Authentication": "Authentication",
|
||||
"Comment": "Comment",
|
||||
"ConfirmPassword": "Confirm password",
|
||||
"DateExpired": "Date expired",
|
||||
@@ -2010,6 +2035,7 @@
|
||||
"setWeCom": "Set wecom login",
|
||||
"setDingTalk": "Set dingtalk login",
|
||||
"setFeiShu": "Set feishu login",
|
||||
"setLark": "Set lark login",
|
||||
"setSlack": "Set Slack login",
|
||||
"DatePasswordUpdated": "Date password updated",
|
||||
"DescribeOfGuide": "Welcome to JumpServer. Click here for more information",
|
||||
@@ -2110,7 +2136,9 @@
|
||||
"passwordWillExpiredPrefixMsg": "The password will expire in ",
|
||||
"passwordWillExpiredSuffixMsg": " days.Please change your password as soon as possible.",
|
||||
"dateLastLogin": "Date last login",
|
||||
"AddAllMembersWarningMsg": "Are you sure you want to add all members"
|
||||
"AddAllMembersWarningMsg": "Are you sure you want to add all members?",
|
||||
"disallowSelfUpdateFields": "Not allowed to modify the current fields oneself",
|
||||
"GlobalDisableMfaMsg": "Global enforcement has been enabled"
|
||||
},
|
||||
"notifications": {
|
||||
"MessageType": "Message Type",
|
||||
@@ -2232,6 +2260,7 @@
|
||||
"ZStack": "ZStack",
|
||||
"GCP": "Google Cloud Platform",
|
||||
"UCloud": "UCloud Platform",
|
||||
"Volcengine": "Volcengine",
|
||||
"FC": "Fusion Compute",
|
||||
"AWS_China": "AWS(China)",
|
||||
"AWS_Int": "AWS(International)",
|
||||
@@ -2245,6 +2274,8 @@
|
||||
"HostnameStrategy": "Used to produce the asset hostname. For example, 1. Instance name (instanceDemo);2. Instance name and Partial IP (instanceDemo-250.1)",
|
||||
"IsAlwaysUpdate": "Keep assets up to date",
|
||||
"FullySynchronous": "Assets fully synchronized",
|
||||
"ReleaseAssets": "Release assets",
|
||||
"ReleaseAssetsHelpTips": "Whether to automatically delete assets synchronized through this task and released on the cloud at the end of the task",
|
||||
"AccountCreate": "Create account",
|
||||
"AccountList": "Account list",
|
||||
"AccountUpdate": "Update account",
|
||||
@@ -2385,7 +2416,7 @@
|
||||
"Template": {
|
||||
"Template": "Template"
|
||||
},
|
||||
"Beian": "Registration"
|
||||
"Footer": "Footer"
|
||||
},
|
||||
"applets": {
|
||||
"PublishStatus": "Publish status",
|
||||
|
||||
@@ -29,7 +29,8 @@ actions_display_mapper = {
|
||||
}
|
||||
langs_display_map = {
|
||||
'en': '英文',
|
||||
'ja': '日文'
|
||||
'ja': '日文',
|
||||
'zh_Hant': '繁体中文',
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +115,7 @@ if __name__ == '__main__':
|
||||
'action', type=str, choices=("diff", "apply"),
|
||||
)
|
||||
parser.add_argument(
|
||||
'langs', type=str, choices=("en", "ja"), nargs='*'
|
||||
'langs', type=str, choices=("en", "ja", "zh_Hant"), nargs='*'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
action = args.action
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import zhLocale from 'element-ui/lib/locale/lang/zh-CN'
|
||||
import zhTWLocale from 'element-ui/lib/locale/lang/zh-TW'
|
||||
import enLocale from 'element-ui/lib/locale/lang/en'
|
||||
import jaLocale from 'element-ui/lib/locale/lang/ja'
|
||||
import zh from './zh.json'
|
||||
import zhHant from './zh_Hant.json'
|
||||
import en from './en.json'
|
||||
import ja from './ja.json'
|
||||
|
||||
@@ -10,6 +12,10 @@ export default {
|
||||
...zhLocale,
|
||||
...zh
|
||||
},
|
||||
zh_hant: {
|
||||
...zhTWLocale,
|
||||
...zhHant
|
||||
},
|
||||
en: {
|
||||
...enLocale,
|
||||
...en
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"": "",
|
||||
"accounts": {
|
||||
"AccountName": "Account name",
|
||||
"AccountBatchUpdate": "ロット更新(同じタイプです)",
|
||||
"Accounts": "アカウント",
|
||||
"SuFrom": "から",
|
||||
"AccountTemplateUpdateSecretHelpText": "アカウントリストには、テンプレートで作成されたアカウントが表示されます。暗号文を更新すると、テンプレートで作成されたアカウントの暗号文が更新されます。",
|
||||
@@ -31,6 +33,8 @@
|
||||
"AccountGatherList": "収集タスク"
|
||||
},
|
||||
"AccountChangeSecret": {
|
||||
"OldSecret": "古い秘密",
|
||||
"NewSecret": "新しい秘密",
|
||||
"Result": "結果",
|
||||
"ParamsHelpText": "改密パラメータの設定は、プラットフォームの種類がホストである資産に対してのみ有効です。",
|
||||
"ExecutionTimes": "実行時間",
|
||||
@@ -77,6 +81,7 @@
|
||||
"ExecutionDetail": "実行の詳細",
|
||||
"Name": "名前",
|
||||
"Retry": "リトライ",
|
||||
"BatchRetry": "一括リトライ",
|
||||
"Timer": "時限実行",
|
||||
"Detail": "詳細",
|
||||
"TimeDelta": "営業時間",
|
||||
@@ -109,14 +114,14 @@
|
||||
"ExecutionList": "実行リスト",
|
||||
"Reason": "理由",
|
||||
"AccountBackup": "アカウントのバックアップ",
|
||||
"RecipientHelpText": "受信者A Bが設定されている場合、アカウントの鍵は前後2つに分割されます",
|
||||
"RecipientHelpText": "受信者A Bが設定されている場合、アカウントの鍵は前後2つに分割されます。ユーザーが暗号化パスワードを設定していない場合-個人情報->プリファレンス設定で暗号化パスワードを設定してください",
|
||||
"RecipientServer": "受信サーバー"
|
||||
},
|
||||
"DynamicUsername": "動的ユーザー名",
|
||||
"AutoCreate": "自動作成",
|
||||
"AccountExportTips": "エクスポート情報には機密情報を含むアカウント暗号文が含まれており、エクスポートされたフォーマットは暗号化されたzipファイルです(暗号化パスワードが設定されていない場合は、個人情報にファイル暗号化パスワードを設定してください)。",
|
||||
"TaskID": "タスク ID",
|
||||
"AccountTemplate": "账号模版",
|
||||
"AccountTemplate": "账号模板",
|
||||
"Sync": "同期",
|
||||
"SyncDelete": "同期削除",
|
||||
"BulkSyncDelete": "一括同期削除",
|
||||
@@ -124,7 +129,11 @@
|
||||
"AddAccountResult": "账号批量添加结果",
|
||||
"AutoPush": "自動プッシュ",
|
||||
"GeneralAccounts": "一般アカウント",
|
||||
"VirtualAccounts": "仮想アカウント"
|
||||
"VirtualAccounts": "仮想アカウント",
|
||||
"AccountDeleteConfirmMsg": "アカウントを削除します,続行しますか?",
|
||||
"Test": "テスト",
|
||||
"QuickTest": "クイックテスト",
|
||||
"BulkVerify": "一括接続性テスト"
|
||||
},
|
||||
"acl": {
|
||||
"CommandFilterACLHelpMsg": "コマンドフィルタリングを使用すると、コマンドがアセット上で実行されるかどうかを制御できます。ルールに基づいて、特定のコマンドは許可され、他のコマンドは禁止されることがあります。",
|
||||
@@ -457,6 +466,7 @@
|
||||
"Token": "トークン",
|
||||
"GatewayList": "ゲートウェイ一覧",
|
||||
"InitialDeploy": "初期展開",
|
||||
"OnlyInitialDeploy": "初期設定のみ",
|
||||
"PrivateKey": "鍵",
|
||||
"Category": "カテゴリー",
|
||||
"SSHPort": "SSH ポート",
|
||||
@@ -475,7 +485,11 @@
|
||||
"WebUpdate": "資産の更新 - Web",
|
||||
"DatabaseUpdate": "資産の更新 - データベース",
|
||||
"GPTCreate": "資産の作成 - GPT",
|
||||
"AppletHostDomainHelpText": "これらのドメインはシステム組織にあります"
|
||||
"AppletHostDomainHelpText": "これらのドメインはシステム組織にあります",
|
||||
"OracleDBNameHelpText": "Oracle データベースの SID またはサービス名を入力してください",
|
||||
"Remove": "取り除く",
|
||||
"AddGatewayInDomain": "ゲートウェイの追加",
|
||||
"AddAssetInDomain": "アセットの追加"
|
||||
},
|
||||
"audits": {
|
||||
"ChangeField": "フィールドを変更します",
|
||||
@@ -703,6 +717,7 @@
|
||||
"SyncSuccessMsg": "同期に成功しました",
|
||||
"SyncSelected": "選択した同期",
|
||||
"bulkDeploy": "一括デプロイ",
|
||||
"BulkVerify": "一括テスト",
|
||||
"bulkSyncErrorMsg": "一括同期に失敗しました:",
|
||||
"bulkDeleteErrorMsg": "一括削除に失敗しました:",
|
||||
"bulkDeleteSuccessMsg": "一括削除に成功しました",
|
||||
@@ -747,6 +762,7 @@
|
||||
"Prompt": "ヒント",
|
||||
"InputMessage": "メッセージの入力...",
|
||||
"CollapseSidebar": "サイドバーを閉じる",
|
||||
"crontabDiffError": "定期実行の間隔が10分以上であることをご確認ください!",
|
||||
"introduction": {
|
||||
"ConceptTitle": "🤔 Python インタプリタ",
|
||||
"IdeaContent": "私はあなたに Linux ターミナルの役割を果たしてもらいたいです。私はコマンドを入力し、ターミナルが表示すべき内容を返してもらいます。あなたにはユニークなコードブロック内でのみターミナルの出力に応答してほしいです。説明は書かないでください。私が何かを英語で伝える必要がある場合、中括弧で囲んでテキストを入れます {コメントテキスト}。",
|
||||
@@ -876,6 +892,7 @@
|
||||
"failedConditions": "条件に達していない結果!"
|
||||
},
|
||||
"Deploy": "配備",
|
||||
"Uninstall": "アンインストールする",
|
||||
"Publish": "リリース",
|
||||
"Icon": "アイコン",
|
||||
"Automations": "オートメーション",
|
||||
@@ -970,22 +987,23 @@
|
||||
"LoginCount": "ログイン回数",
|
||||
"LoginOverview": "セッション統計",
|
||||
"LoginTo": "ログインしました",
|
||||
"LoginUsers": "アクティブなアカウント",
|
||||
"ActiveUsers": "アクティブユーザー",
|
||||
"Monthly": "月ごと",
|
||||
"CurrentConnections": "現在の接続数",
|
||||
"TodayFailedConnections": "今日の接続に失敗しました",
|
||||
"CurrentConnectionUsers": "現在のセッションユーザーの数",
|
||||
"TodayFailedConnections": "今日の失敗したセッションの数",
|
||||
"OnlineSessions": "オンラインセッション",
|
||||
"RealTimeData": "リアルタイムデータ",
|
||||
"UserAssetActivity": "アカウント/資産のアクティブ化",
|
||||
"UserData": "アカウントデータ",
|
||||
"LoginUserToday": "今日のログインアカウント数",
|
||||
"UserAssetActivity": "ユーザー/アセットのアクティビティステータス",
|
||||
"UserData": "ユーザーデータ",
|
||||
"LoginUserToday": "今日のログインユーザー数",
|
||||
"AssetData": "資産データ",
|
||||
"LoginAssetToday": "今日のアクティブ資産数",
|
||||
"WeekAdd": "今週の追加",
|
||||
"ProportionOfAssetTypes": "資産タイプの割合",
|
||||
"Proportion": "占有率",
|
||||
"LoginUserRanking": "ログインアカウントランキング",
|
||||
"ActiveAssetRanking": "ログイン資産ランキング",
|
||||
"LoginUserRanking": "セッションユーザーランキング",
|
||||
"ActiveAssetRanking": "セッションアセットランキング",
|
||||
"AssetName": "資産名",
|
||||
"NumberOfVisits": "アクセス回数",
|
||||
"ranking": "ランキング",
|
||||
@@ -1009,13 +1027,14 @@
|
||||
"BatchCommandNotExecuted": "未実行コマンド",
|
||||
"ExecuteFailedCommand": "失敗コマンドの実行",
|
||||
"SessionTrend": "セッショントレンド",
|
||||
"SessionConnectTrend": "セッション接続の傾向",
|
||||
"UserLoginTrend": "アカウントログイントレンド",
|
||||
"TimesWeekUnit": "回/週",
|
||||
"TopAssetsOfWeek": "週間資産TOP10",
|
||||
"TopUsersOfWeek": "週ユーザーTOP10",
|
||||
"User": "ユーザー",
|
||||
"UserRatio": "ユーザー比率統計",
|
||||
"UsersTotal": "アカウント総数",
|
||||
"UsersTotal": "総ユーザー数",
|
||||
"Weekly": "週ごと"
|
||||
},
|
||||
"ops": {
|
||||
@@ -1191,7 +1210,9 @@
|
||||
"FileSizeExceedsLimit": "ファイルサイズが制限を超えています",
|
||||
"runSucceed": "タスクが成功しました",
|
||||
"EnterUploadPath": "アップロードパスを入力してください",
|
||||
"FileNameTooLong": "ファイル名が長すぎます"
|
||||
"FileNameTooLong": "ファイル名が長すぎます",
|
||||
"StopJob": "ジョブを停止",
|
||||
"StopLogOutput": "Task Canceled:現在のタスク(currentTaskId)は手動で停止されました。各タスクの進行状況が異なるため、以下はタスクの最終実行結果です。実行が失敗した場合は、タスクが正常に停止されました。"
|
||||
},
|
||||
"perms": {
|
||||
"": "",
|
||||
@@ -1592,7 +1613,8 @@
|
||||
"remoteAddr": "リモートアドレス",
|
||||
"replay": "リプレイ",
|
||||
"replaySession": "再生セッション",
|
||||
"replayStorage": "オブジェクトストレージ",
|
||||
"replayStorage": "ビデオ保存",
|
||||
"objectStorage": "オブジェクトストレージ",
|
||||
"riskLevel": "リスクレベル",
|
||||
"session": "会話",
|
||||
"sshPort": "SSHポート",
|
||||
@@ -1763,6 +1785,7 @@
|
||||
"LDAPServerInfo": "LDAPサーバー",
|
||||
"LDAPUser": "LDAPユーザー",
|
||||
"ChatAI": "チャットAI",
|
||||
"Example": "例: {example}",
|
||||
"helpText": {
|
||||
"TempPassword": "一時パスワードの有効期間は300秒で、使用後すぐに失効します",
|
||||
"ApiKeyList": "Api keyを使用してリクエストヘッダに署名します。リクエストのヘッダごとに異なります。使用ドキュメントを参照してください",
|
||||
@@ -1904,6 +1927,7 @@
|
||||
"Assignee": "処理者",
|
||||
"Assignees": "処理待ち",
|
||||
"Close": "閉じる",
|
||||
"CancelTicket": "作業指示をキャンセルする",
|
||||
"OpenStatus": "オン",
|
||||
"CloseStatus": "閉じる",
|
||||
"Comment": "備考",
|
||||
@@ -1969,6 +1993,7 @@
|
||||
"UpdateNodeAssetHardwareInfo": "ノード資産ハードウェア情報の更新"
|
||||
},
|
||||
"users": {
|
||||
"OrgsAndRoles": "そしきとやくわり",
|
||||
"LunaSettingUpdate": "Luna 設定更新",
|
||||
"KokoSettingUpdate": "Koko 設定更新",
|
||||
"UserSetting": "個人設定",
|
||||
@@ -2016,6 +2041,7 @@
|
||||
"setWeCom": "企業のwechat認証を設定する",
|
||||
"setDingTalk": "ホッチキス認証の設定",
|
||||
"setFeiShu": "飛書認証を設定する",
|
||||
"setLark": "Lark認証を設定する",
|
||||
"setSlack": "Slack認証を設定します",
|
||||
"HelpText": {
|
||||
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "アカウントをより安全にするために、マルチファクタ认证を有効にします。 <Br/> 有効にすると、次回のログイン時に多因子認証バインディングプロセスに入ります。また、 (個人情報-> クイック修正-> 多因子設定の変更) もできます。で直接紐付けます",
|
||||
@@ -2099,7 +2125,9 @@
|
||||
"passwordExpired": "パスワードが期限切れです",
|
||||
"passwordWillExpiredPrefixMsg": "パスワードはまもなく",
|
||||
"passwordWillExpiredSuffixMsg": "期限が切れた後、できるだけ早くパスワードを変更してください。",
|
||||
"AddAllMembersWarningMsg": "すべてのメンバーを追加してもよろしいですか"
|
||||
"AddAllMembersWarningMsg": "すべてのメンバーを追加してもよろしいですか?",
|
||||
"disallowSelfUpdateFields": "現在のフィールドを自分で変更することは許可されていません",
|
||||
"GlobalDisableMfaMsg": "グローバルでの強制が有効になっています"
|
||||
},
|
||||
"notifications": {
|
||||
"MessageType": "メッセージタイプ",
|
||||
@@ -2226,6 +2254,7 @@
|
||||
"CTYunPrivate": "天翼プライベート・クラウド",
|
||||
"GCP": "Googleクラウド",
|
||||
"UCloud": "UCloudユケデです",
|
||||
"Volcengine": "Volcengine",
|
||||
"FC": "Fusion Compute",
|
||||
"LAN": "ローカルエリアネットワーク",
|
||||
"AWS_China": "AWS(中国)",
|
||||
@@ -2241,6 +2270,8 @@
|
||||
"HostnameStrategy": "資産を生成するためにホスト名。例: 1. インスタンス名 (instanceDemo) 2.インスタンス名と一部IP (下位2桁) (instanceDemo-250.1)",
|
||||
"IsAlwaysUpdate": "資産は常に最新です",
|
||||
"FullySynchronous": "資産完全にシンクロします",
|
||||
"ReleaseAssets": "資産の同期解放",
|
||||
"ReleaseAssetsHelpTips": "タスクの終了時に、このタスクを介して同期され、クラウド上で解放された資産を自動的に削除するかどうか",
|
||||
"AccountCreate": "アカウントの作成",
|
||||
"AccountList": "アカウントリスト",
|
||||
"AccountUpdate": "アカウントの更新",
|
||||
@@ -2376,7 +2407,7 @@
|
||||
"IntervalOfCreateUpdatePage": "単位: 時",
|
||||
"UsernameOfCreateUpdatePage": "ターゲットホスト上のユーザーのユーザー名存在する場合は、ユーザーパスワードを変更します存在しない場合は、ユーザーを追加してパスワードを設定します"
|
||||
},
|
||||
"Beian": "ファイリング"
|
||||
"Footer": "フッター"
|
||||
},
|
||||
"applets": {
|
||||
"PublishStatus": "投稿ステータス",
|
||||
|
||||
@@ -5,7 +5,8 @@ i18n_report_path = '/tmp/abc.json'
|
||||
lang_paths = {
|
||||
'cn': 'cn.json',
|
||||
'en': 'en.json',
|
||||
'ja': 'ja.json'
|
||||
'ja': 'ja.json',
|
||||
'zh_hant': 'zh_Hant.json'
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
{
|
||||
"accounts": {
|
||||
"AccountName": "账号名称",
|
||||
"AccountBatchUpdate": "批量更新(同类型)",
|
||||
"BulkVerify": "批量测试可连接性",
|
||||
"Test": "测试",
|
||||
"QuickTest": "测试",
|
||||
"AutoPush": "自动推送",
|
||||
"GeneralAccounts": "普通账号",
|
||||
"VirtualAccounts": "虚拟账号",
|
||||
"Accounts": "账号",
|
||||
"SuFrom": "切换自",
|
||||
"SelectAccount": "选择账号",
|
||||
"AccountTemplateUpdateSecretHelpText": "账号列表展示通过模版创建的账号。更新密文时,会更新通过模版所创建账号的密文。",
|
||||
"AccountTemplateUpdateSecretHelpText": "账号列表展示通过模板创建的账号。更新密文时,会更新通过模板所创建账号的密文。",
|
||||
"GenerateSuccessMsg": "账号生成成功",
|
||||
"GenerateAccounts": "重新生成账号",
|
||||
"UpdateSecret": "更新密文",
|
||||
@@ -17,7 +22,7 @@
|
||||
"AddAccountResult": "账号批量添加结果",
|
||||
"AccountPolicy": "账号策略",
|
||||
"BulkCreateStrategy": "创建时对于不符合要求的账号,如:密钥类型不合规,唯一键约束,可选择以上策略。",
|
||||
"AccountTemplate": "账号模版",
|
||||
"AccountTemplate": "账号模板",
|
||||
"HistoryDate": "日期",
|
||||
"SameTypeAccountTip": "相同用户名、密钥类型的账号已存在",
|
||||
"TaskID": "任务 ID",
|
||||
@@ -30,6 +35,7 @@
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "收集用户列表,点击左侧资产进行查看",
|
||||
"AutoCreate": "自动创建",
|
||||
"AccountExportTips": "导出信息中包含账号密文涉及敏感信息,导出的格式为一个加密的zip文件(若没有设置加密密码,请前往个人信息中设置文件加密密码)。",
|
||||
"AccountDeleteConfirmMsg": "删除账号,是否继续?",
|
||||
"AccountPush": {
|
||||
"WindowsPushHelpText": "windows 资产暂不支持推送密钥",
|
||||
"AccountPushList": "账号推送",
|
||||
@@ -49,10 +55,12 @@
|
||||
"MailRecipient": "邮件收件人",
|
||||
"IsSuccess": "是否成功",
|
||||
"Reason": "原因",
|
||||
"RecipientHelpText": "若收件人 A B 都设置,账号的密钥将被拆分成前后两部分",
|
||||
"RecipientHelpText": "若收件人 A B 都设置,账号的密钥将被拆分成前后两部分。如用户未设置加密密码 - 请前往个人信息 -> 偏好设置中设置加密密码",
|
||||
"RecipientServer": "接收服务器"
|
||||
},
|
||||
"AccountChangeSecret": {
|
||||
"OldSecret": "原密钥",
|
||||
"NewSecret": "新密钥",
|
||||
"PasswordRule": "密码生成规则",
|
||||
"ParamsHelpText": "改密参数设置,目前仅对平台种类为主机的资产生效。",
|
||||
"ContainAttachment": "含附件",
|
||||
@@ -105,6 +113,7 @@
|
||||
"RegularlyPerform": "定期执行",
|
||||
"Result": "结果",
|
||||
"Retry": "重试",
|
||||
"BatchRetry": "批量重试",
|
||||
"Success": "成功",
|
||||
"TaskList": "任务记录",
|
||||
"TimeDelta": "运行时间",
|
||||
@@ -271,11 +280,12 @@
|
||||
"Category": "类别",
|
||||
"AccountDetail": "账号详情",
|
||||
"Accounts": "账号列表",
|
||||
"SelectTemplate": "选择模版",
|
||||
"SelectTemplate": "选择模板",
|
||||
"InAssetDetail": "在资产详情中更新账号信息",
|
||||
"SecretType": "密文类型",
|
||||
"PrivilegedTemplate": "特权的",
|
||||
"InitialDeploy": "初始化部署",
|
||||
"InitialDeploy": "初始化安装部署",
|
||||
"OnlyInitialDeploy": "仅初始化配置",
|
||||
"Address": "地址",
|
||||
"PrivateKey": "密钥",
|
||||
"Secret": "密码",
|
||||
@@ -464,7 +474,11 @@
|
||||
"NoSQLProtocol": "非关系数据库",
|
||||
"OtherProtocol": "其它协议",
|
||||
"PasswordOrToken": "密码 / 令牌",
|
||||
"AppletHostDomainHelpText": "这里的网域属于 System 组织"
|
||||
"AppletHostDomainHelpText": "这里的网域属于 System 组织",
|
||||
"OracleDBNameHelpText": "提示:填写 Oracle 数据库的SID或服务名称(Service Name)",
|
||||
"Remove": "移除",
|
||||
"AddGatewayInDomain": "添加网关",
|
||||
"AddAssetInDomain": "添加资产"
|
||||
},
|
||||
"audits": {
|
||||
"ChangeField": "变更字段",
|
||||
@@ -588,6 +602,7 @@
|
||||
"Automations": "自动化",
|
||||
"Sync": "同步",
|
||||
"Deploy": "部署",
|
||||
"Uninstall": "卸载",
|
||||
"Detail": "详情",
|
||||
"Selector": "选择器",
|
||||
"NoContent": "暂无内容",
|
||||
@@ -645,8 +660,8 @@
|
||||
"actionsTips": "各个权限作用协议不尽相同,点击权限后面的图标查看",
|
||||
"TableColSettingInfo": "请选择您想显示的列表详细信息。",
|
||||
"Add": "添加",
|
||||
"TemplateAdd": "模版添加",
|
||||
"TemplateHelpText": "选择模版添加时,会自动创建资产下不存在的账号并推送",
|
||||
"TemplateAdd": "模板添加",
|
||||
"TemplateHelpText": "选择模板添加时,会自动创建资产下不存在的账号并推送",
|
||||
"Task": "任务",
|
||||
"UpdateAssetDetail": "配置更多信息",
|
||||
"AddSuccessMsg": "添加成功",
|
||||
@@ -756,6 +771,7 @@
|
||||
"BatchActivate": "批量激活",
|
||||
"SyncSelected": "同步所选",
|
||||
"bulkDeploy": "批量部署",
|
||||
"BulkVerify": "批量测试",
|
||||
"bulkDeleteErrorMsg": "批量删除失败: ",
|
||||
"bulkDeleteSuccessMsg": "批量删除成功",
|
||||
"bulkRemoveErrorMsg": "批量移除失败: ",
|
||||
@@ -801,6 +817,7 @@
|
||||
"InputMessage": "输入消息...",
|
||||
"EnterMessage": "请输入问题, Enter 发送",
|
||||
"CollapseSidebar": "收起侧边栏",
|
||||
"crontabDiffError": "请确保定期执行的时间间隔不少于十分钟!",
|
||||
"introduction": {
|
||||
"ConceptTitle": "\uD83E\uDD14 Python 解释器 ",
|
||||
"ConceptContent": "我想让你像一个 Python 解释器一样行事。我将给你 Python 代码,你将执行它。不要提供任何解释。除了代码的输出,不要用任何东西来回应。",
|
||||
@@ -959,22 +976,23 @@
|
||||
"LoginCount": "登录次数",
|
||||
"LoginOverview": "会话统计",
|
||||
"LoginTo": "登录了",
|
||||
"LoginUsers": "活跃账号",
|
||||
"ActiveUsers": "活跃用户",
|
||||
"Monthly": "按月",
|
||||
"CurrentConnections": "当前连接数",
|
||||
"TodayFailedConnections": "今日连接失败数",
|
||||
"CurrentConnectionUsers": "当前会话用户数",
|
||||
"TodayFailedConnections": "今日会话失败数",
|
||||
"OnlineSessions": "在线会话数",
|
||||
"RealTimeData": "实时数据",
|
||||
"UserAssetActivity": "账号/资产活跃情况",
|
||||
"UserData": "账号数据",
|
||||
"LoginUserToday": "今日登录账号数",
|
||||
"UserAssetActivity": "用户/资产活跃情况",
|
||||
"UserData": "用户数据",
|
||||
"LoginUserToday": "今日登录用户数",
|
||||
"AssetData": "资产数据",
|
||||
"LoginAssetToday": "今日活跃资产数",
|
||||
"WeekAdd": "本周新增",
|
||||
"ProportionOfAssetTypes": "资产类型占比",
|
||||
"Proportion": "占比",
|
||||
"LoginUserRanking": "登录账号排名",
|
||||
"ActiveAssetRanking": "登录资产排名",
|
||||
"LoginUserRanking": "会话用户排名",
|
||||
"ActiveAssetRanking": "会话资产排名",
|
||||
"AssetName": "资产名称",
|
||||
"NumberOfVisits": "访问次数",
|
||||
"ranking": "排名",
|
||||
@@ -998,13 +1016,13 @@
|
||||
"BatchCommandNotExecuted": "未执行批量命令",
|
||||
"ExecuteFailedCommand": "执行失败命令",
|
||||
"SessionTrend": "会话趋势",
|
||||
"UserLoginTrend": "账号登录趋势",
|
||||
"SessionConnectTrend": "会话连接趋势",
|
||||
"TimesWeekUnit": "次/周",
|
||||
"TopAssetsOfWeek": "周资产 TOP10",
|
||||
"TopUsersOfWeek": "周用户 TOP10",
|
||||
"User": "用户",
|
||||
"UserRatio": "用户占比统计",
|
||||
"UsersTotal": "账号总数",
|
||||
"UsersTotal": "用户总数",
|
||||
"Weekly": "按周"
|
||||
},
|
||||
"ops": {
|
||||
@@ -1180,7 +1198,9 @@
|
||||
"FileSizeExceedsLimit": "文件大小超出限制",
|
||||
"runSucceed": "任务执行成功",
|
||||
"EnterUploadPath": "输入上传路径",
|
||||
"FileNameTooLong": "文件名太长"
|
||||
"FileNameTooLong": "文件名太长",
|
||||
"StopJob": "停止作业",
|
||||
"StopLogOutput": "Task Canceled:当前任务(currentTaskId)已手动停止,由于每个任务的执行进度不一样,下面是任务最终的执行结果,执行失败表示已成功停止任务执行。"
|
||||
},
|
||||
"perms": {
|
||||
"": "",
|
||||
@@ -1311,9 +1331,9 @@
|
||||
"Accounts": "账号管理",
|
||||
"AssetAccount": "账号列表",
|
||||
"AssetAccountDetail": "账号详情",
|
||||
"AccountTemplate": "账号模版",
|
||||
"CreateAccountTemplate": "创建账号模版",
|
||||
"UpdateAccountTemplate": "更新账号模版",
|
||||
"AccountTemplate": "账号模板",
|
||||
"CreateAccountTemplate": "创建账号模板",
|
||||
"UpdateAccountTemplate": "更新账号模板",
|
||||
"AssetHistoryAccount": "资产历史账号",
|
||||
"ApplicationAccount": "应用账号",
|
||||
"Ticket": "工单",
|
||||
@@ -1346,7 +1366,7 @@
|
||||
"BatchCommand": "批量命令",
|
||||
"BatchScript": "批量脚本",
|
||||
"Execution": "执行历史",
|
||||
"Template": "模版管理",
|
||||
"Template": "模板管理",
|
||||
"TicketsTodo": "待办工单",
|
||||
"TicketsDone": "已办工单",
|
||||
"TicketsNew": "提交工单",
|
||||
@@ -1462,10 +1482,10 @@
|
||||
"TicketDetail": "工单详情",
|
||||
"TicketCreate": "创建工单",
|
||||
"Tickets": "工单列表",
|
||||
"Templates": "模版管理",
|
||||
"TemplateDetail": "模版详情",
|
||||
"TemplateCreate": "创建模版",
|
||||
"TemplateUpdate": "更新模版",
|
||||
"Templates": "模板管理",
|
||||
"TemplateDetail": "模板详情",
|
||||
"TemplateCreate": "创建模板",
|
||||
"TemplateUpdate": "更新模板",
|
||||
"UserCreate": "创建用户",
|
||||
"UserDetail": "用户详情",
|
||||
"UserFirstLogin": "首次登录",
|
||||
@@ -1575,7 +1595,8 @@
|
||||
"remoteAddr": "远端地址",
|
||||
"replay": "回放",
|
||||
"replaySession": "回放会话",
|
||||
"replayStorage": "对象存储",
|
||||
"replayStorage": "录像存储",
|
||||
"objectStorage": "对象存储",
|
||||
"riskLevel": "风险等级",
|
||||
"session": "会话",
|
||||
"sshPort": "SSH端口",
|
||||
@@ -1752,6 +1773,7 @@
|
||||
"LDAPServerInfo": "LDAP 服务器",
|
||||
"LDAPUser": "LDAP 用户",
|
||||
"ChatAI": "智能问答",
|
||||
"Example": "例: {example}",
|
||||
"helpText": {
|
||||
"TempPassword": "临时密码有效期为 300 秒,使用后立刻失效",
|
||||
"ApiKeyList": "使用 Api key 签名请求头进行认证,每个请求的头部是不一样的, 相对于 Token 方式,更加安全,请查阅文档使用;<br>为降低泄露风险,Secret 仅在生成时可以查看, 每个用户最多支持创建 10 个",
|
||||
@@ -1891,6 +1913,7 @@
|
||||
"Assignee": "处理人",
|
||||
"Assignees": "待处理人",
|
||||
"Close": "关闭",
|
||||
"CancelTicket": "取消工单",
|
||||
"OpenStatus": "审批中",
|
||||
"CloseStatus": "已完成",
|
||||
"Comment": "备注",
|
||||
@@ -1961,11 +1984,12 @@
|
||||
"PasskeyAddDisableInfo": "你的认证来源是 {source}, 不支持添加 Passkey"
|
||||
},
|
||||
"users": {
|
||||
"OrgsAndRoles": "组织和角色",
|
||||
"LunaSettingUpdate": "Luna 配置设置",
|
||||
"KokoSettingUpdate": "Koko 配置设置",
|
||||
"UserSetting": "偏好设置",
|
||||
"AllMembers": "全部成员",
|
||||
"AddAllMembersWarningMsg": "你确定要添加全部成员",
|
||||
"AddAllMembersWarningMsg": "你确定要添加全部成员?",
|
||||
"UnbindHelpText": "本地用户为此认证来源用户,无法解绑",
|
||||
"SetStatus": "设置状态",
|
||||
"Set": "已设置",
|
||||
@@ -2009,7 +2033,8 @@
|
||||
"setWeCom": "设置企业微信认证",
|
||||
"setDingTalk": "设置钉钉认证",
|
||||
"setFeiShu": "设置飞书认证",
|
||||
"setSlack": "设置Slack认证",
|
||||
"setLark": "设置 Lark 认证",
|
||||
"setSlack": "设置 Slack 认证",
|
||||
"HelpText": {
|
||||
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "启用多因子认证,使账号更加安全。<br/> 启用之后您将会在下次登录时进入多因子认证绑定流程;您也可以在(个人信息->快速修改->更改多因子设置)中直接绑定!",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:设置复杂密码,并启用多因子认证)<br/> 邮箱、手机号、微信等个人信息,仅作为用户认证和平台内部消息通知使用。",
|
||||
@@ -2091,7 +2116,9 @@
|
||||
"SetPublicKey": "设置SSH公钥",
|
||||
"passwordExpired": "密码过期了",
|
||||
"passwordWillExpiredPrefixMsg": "密码即将在 ",
|
||||
"passwordWillExpiredSuffixMsg": "天 后过期,请尽快修改您的密码。"
|
||||
"passwordWillExpiredSuffixMsg": "天 后过期,请尽快修改您的密码。",
|
||||
"disallowSelfUpdateFields": "不允许自己修改当前字段",
|
||||
"GlobalDisableMfaMsg": "全局已强制开启"
|
||||
},
|
||||
"notifications": {
|
||||
"MessageType": "消息类型",
|
||||
@@ -2147,6 +2174,7 @@
|
||||
"ZStack": "ZStack",
|
||||
"GCP": "谷歌云",
|
||||
"UCloud": "UCloud优刻得",
|
||||
"Volcengine": "火山引擎",
|
||||
"FC": "Fusion Compute",
|
||||
"AWS_China": "AWS(中国)",
|
||||
"AWS_Int": "AWS(国际)",
|
||||
@@ -2161,6 +2189,8 @@
|
||||
"HostnameStrategy": "用于生成资产主机名。例如:1. 实例名称 (instanceDemo);2. 实例名称和部分IP(后两位) (instanceDemo-250.1)",
|
||||
"IsAlwaysUpdate": "资产保持最新",
|
||||
"FullySynchronous": "资产完全同步",
|
||||
"ReleaseAssets": "同步释放资产",
|
||||
"ReleaseAssetsHelpTips": "是否在任务结束时,自动删除通过此任务同步下来且已经在云上释放的资产",
|
||||
"AccountCreate": "创建账户",
|
||||
"AccountList": "云账号",
|
||||
"AccountUpdate": "更新账户",
|
||||
@@ -2208,7 +2238,7 @@
|
||||
"DeleteReleasedAssets": "删除已释放资产"
|
||||
},
|
||||
"Template": {
|
||||
"Template": "模版管理"
|
||||
"Template": "模板管理"
|
||||
},
|
||||
"Corporation": "公司",
|
||||
"Edition": "版本",
|
||||
@@ -2286,6 +2316,6 @@
|
||||
"IntervalOfCreateUpdatePage": "单位:时",
|
||||
"UsernameOfCreateUpdatePage": "目标主机上用户的用户名;如果已️存在,修改用户密码;如果不存在,添加用户并设置密码;"
|
||||
},
|
||||
"Beian": "备案"
|
||||
"Footer": "页脚"
|
||||
}
|
||||
}
|
||||
|
||||
2318
src/i18n/langs/zh_Hant.json
Normal file
2318
src/i18n/langs/zh_Hant.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
<svg t="1687315588323" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3119" width="11" height="11"><path d="M302.545455 116.363636h409.6a27.927273 27.927273 0 0 0 0-55.854545h-409.6a27.927273 27.927273 0 1 0 0 55.854545z m-93.09091 102.4h595.781819a27.927273 27.927273 0 0 0 0-55.854545h-595.781819a27.927273 27.927273 0 1 0 0 55.854545z m670.254546 55.854546h-735.418182c-51.414109 0-93.090909 41.686109-93.090909 93.090909v512c0 51.414109 41.6768 93.090909 93.090909 93.090909h735.418182c51.414109 0 93.090909-41.6768 93.090909-93.090909v-512c0-51.4048-41.6768-93.090909-93.090909-93.090909z m37.236364 594.3296a46.545455 46.545455 0 0 1-46.545455 46.545454h-716.8a46.545455 46.545455 0 0 1-46.545455-46.545454V386.327273a46.545455 46.545455 0 0 1 46.545455-46.545455h716.8a46.545455 46.545455 0 0 1 46.545455 46.545455v482.620509zM602.177164 522.621673l-68.9152-64.474764a4.747636 4.747636 0 0 0-1.405673-1.349818 9.234618 9.234618 0 0 0-1.396364-1.163636 11.776 11.776 0 0 0-3.006836-1.7408c-0.996073-0.577164-1.796655-0.772655-2.802036-1.163637-0.605091-0.195491-1.200873-0.390982-2.010764-0.577163-0.390982 0-0.586473-0.195491-0.996073-0.195491-2.196945-0.577164-4.012218-0.577164-5.008291-0.577164h-0.400291c-0.996073 0-3.006836 0-5.408581 0.577164-0.400291 0-0.595782 0.195491-0.996073 0.195491-0.400291 0.195491-1.005382 0.195491-1.405673 0.390981a15.555491 15.555491 0 0 0-3.006836 1.163637c-0.195491 0-0.195491 0.195491-0.400291 0.195491 0 0.195491-0.195491 0.195491-0.195491 0.195491-1.200873 0.577164-2.606545 1.349818-3.611927 2.122472l-0.195491 0.195491a7.000436 7.000436 0 0 0-2.401746 1.7408l-67.314036 64.660946c-4.803491 4.449745-7.614836 10.426182-7.614836 16.7936-0.195491 6.376727 2.606545 12.744145 7.214545 17.370763s10.826473 7.335564 17.435927 7.335564 13.218909-2.513455 18.031709-7.149382l24.641164-23.738182v266.267928c0 13.312 11.226764 24.129164 25.041455 24.129163s25.041455-10.817164 25.041454-24.129163V532.452073l26.046836 24.520145c4.608 4.626618 10.817164 6.953891 17.640728 6.953891 6.795636 0 13.014109-2.513455 17.836218-7.149382l0.186182-0.195491c9.616291-9.644218 9.4208-24.510836-0.400291-33.773381l-0.223418-0.186182z" fill="#2c2c2c" p-id="3120"></path></svg>
|
||||
<svg t="1704873961556" viewBox="0 0 1124 1224" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8913" width="14" height="14"><path stroke-width="150" d="M302.545455 116.363636h409.6a27.927273 27.927273 0 0 0 0-55.854545h-409.6a27.927273 27.927273 0 1 0 0 55.854545z m-93.09091 102.4h595.781819a27.927273 27.927273 0 0 0 0-55.854545h-595.781819a27.927273 27.927273 0 1 0 0 55.854545z m670.254546 55.854546h-735.418182c-51.414109 0-93.090909 41.686109-93.090909 93.090909v512c0 51.414109 41.6768 93.090909 93.090909 93.090909h735.418182c51.414109 0 93.090909-41.6768 93.090909-93.090909v-512c0-51.4048-41.6768-93.090909-93.090909-93.090909z m37.236364 594.3296a46.545455 46.545455 0 0 1-46.545455 46.545454h-716.8a46.545455 46.545455 0 0 1-46.545455-46.545454V386.327273a46.545455 46.545455 0 0 1 46.545455-46.545455h716.8a46.545455 46.545455 0 0 1 46.545455 46.545455v482.620509zM602.177164 522.621673l-68.9152-64.474764a4.747636 4.747636 0 0 0-1.405673-1.349818 9.234618 9.234618 0 0 0-1.396364-1.163636 11.776 11.776 0 0 0-3.006836-1.7408c-0.996073-0.577164-1.796655-0.772655-2.802036-1.163637-0.605091-0.195491-1.200873-0.390982-2.010764-0.577163-0.390982 0-0.586473-0.195491-0.996073-0.195491-2.196945-0.577164-4.012218-0.577164-5.008291-0.577164h-0.400291c-0.996073 0-3.006836 0-5.408581 0.577164-0.400291 0-0.595782 0.195491-0.996073 0.195491-0.400291 0.195491-1.005382 0.195491-1.405673 0.390981a15.555491 15.555491 0 0 0-3.006836 1.163637c-0.195491 0-0.195491 0.195491-0.400291 0.195491 0 0.195491-0.195491 0.195491-0.195491 0.195491-1.200873 0.577164-2.606545 1.349818-3.611927 2.122472l-0.195491 0.195491a7.000436 7.000436 0 0 0-2.401746 1.7408l-67.314036 64.660946c-4.803491 4.449745-7.614836 10.426182-7.614836 16.7936-0.195491 6.376727 2.606545 12.744145 7.214545 17.370763s10.826473 7.335564 17.435927 7.335564 13.218909-2.513455 18.031709-7.149382l24.641164-23.738182v266.267928c0 13.312 11.226764 24.129164 25.041455 24.129163s25.041455-10.817164 25.041454-24.129163V532.452073l26.046836 24.520145c4.608 4.626618 10.817164 6.953891 17.640728 6.953891 6.795636 0 13.014109-2.513455 17.836218-7.149382l0.186182-0.195491c9.616291-9.644218 9.4208-24.510836-0.400291-33.773381l-0.223418-0.186182z" fill="#2c2c2c" p-id="8914"></path></svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
1
src/icons/svg/help.svg
Normal file
1
src/icons/svg/help.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1705571985818" class="icon" viewBox="0 0 1028 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="19018" width="13" height="13"><path d="M983.678752 314.585575c-25.849513-60.781287-62.777388-115.474464-109.785575-162.482651s-101.701365-83.936062-162.482651-109.785575C648.433528 15.569591 581.663938 1.996101 512.998051 1.996101c-68.665887 0-135.435478 13.573489-198.412476 40.321248-60.781287 25.849513-115.474464 62.777388-162.482651 109.785575s-83.936062 101.701365-109.785575 162.482651C15.569591 377.562573 1.996101 444.332164 1.996101 512.998051c0 68.665887 13.573489 135.435478 40.321248 198.412475 25.849513 60.781287 62.777388 115.474464 109.785575 162.482651s101.701365 83.936062 162.482651 109.785575c62.976998 26.747758 129.746589 40.321248 198.412476 40.321248 68.665887 0 135.435478-13.573489 198.412475-40.321248 60.781287-25.849513 115.474464-62.777388 162.482651-109.785575s83.936062-101.701365 109.785575-162.482651c26.747758-62.976998 40.321248-129.746589 40.321248-198.412475 0-68.665887-13.573489-135.435478-40.321248-198.412476zM673.684211 894.752437c-51.00039 21.6577-105.094737 32.636257-160.68616 32.636257s-109.68577-10.978558-160.68616-32.636257c-49.403509-21.058869-93.816764-51.100195-131.9423-89.125926-38.125536-38.125536-68.067057-82.438986-89.125926-131.9423-21.6577-51.00039-32.636257-105.094737-32.636258-160.68616s10.978558-109.68577 32.636258-160.68616c21.058869-49.403509 51.100195-93.816764 89.125926-131.9423 38.125536-38.125536 82.438986-68.067057 131.9423-89.125926 51.00039-21.6577 105.094737-32.636257 160.68616-32.636258s109.68577 10.978558 160.68616 32.636258c49.403509 21.058869 93.816764 51.100195 131.9423 89.125926 38.125536 38.125536 68.067057 82.438986 89.125926 131.9423 21.6577 51.00039 32.636257 105.094737 32.636257 160.68616s-10.978558 109.68577-32.636257 160.68616c-21.058869 49.403509-51.100195 93.816764-89.125926 131.9423-38.125536 38.025731-82.538791 68.067057-131.9423 89.125926z" p-id="19019" fill="#080606"></path><path d="M476.868616 808.820273h74.554386c8.283821 0 14.97076-6.68694 14.97076-14.97076v-68.366472c0-8.283821-6.68694-14.97076-14.97076-14.97076h-74.554386c-8.283821 0-14.97076 6.68694-14.97076 14.97076v68.366472c0 8.283821 6.68694 14.97076 14.97076 14.97076zM687.557115 373.270955c-3.293567-51.299805-19.262378-90.922417-47.307602-117.869785-28.045224-26.947368-68.266667-41.119688-119.566472-42.217544h-0.998051c-58.585575 2.195712-104.096686 19.461988-135.235867 51.100195-31.039376 31.638207-47.906433 77.847953-50.102144 137.331774-0.299415 8.483431 6.487329 15.469786 14.97076 15.469785h65.272514c8.084211 0 14.77115-6.487329 14.970761-14.57154 1.896296-72.558285 30.440546-107.490058 90.024171-109.68577h1.297466c48.904483 2.095906 73.65614 26.947368 77.548538 77.947758 0 0.499025 0.099805 1.097856 0 1.596882-0.59883 26.548148-19.961014 59.0846-57.687329 96.810916-51.100195 48.904483-75.951657 93.118129-75.951657 135.036257v55.990643c0 8.283821 6.68694 14.97076 14.97076 14.970761h68.366472c8.283821 0 14.97076-6.68694 14.97076-14.970761v-65.272514c0-11.377778 7.8846-32.736062 45.211696-70.162963 52.497466-50.301754 79.145419-101.002729 79.145419-150.605848l0.099805-0.898246z" p-id="19020" fill="#080606"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
1
src/icons/svg/load-file.svg
Normal file
1
src/icons/svg/load-file.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1705572472151" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="27270" width="13" height="13"><path d="M896.64 63.872v132.224c0 137.216-88.64 260.48-197.76 327.296 109.12 66.752 197.76 189.056 197.76 326.336V960h28.8a32 32 0 0 1 0 64H99.136a32 32 0 1 1 0-64h28.416v-110.272c0-137.28 92.8-259.584 201.856-326.4C220.16 456.576 127.488 333.376 127.488 196.16V63.872h-31.616A31.872 31.872 0 0 1 63.936 32 32 32 0 0 1 96 0h832.128a32 32 0 0 1 0 63.872h-31.36z m-703.04 0v132.224c0 135.424 117.824 253.312 243.84 286.336a39.68 39.68 0 0 1 15.936 8.384 43.712 43.712 0 0 1-1.344 66.176 42.24 42.24 0 0 1-16.064 7.68c-125.248 33.6-242.368 150.08-242.368 285.056V960h638.272v-110.272c0-135.488-119.808-252.352-245.76-285.44a43.712 43.712 0 0 1-22.784-15.872 42.56 42.56 0 0 1-7.424-18.24c-2.368-23.04 10.24-42.56 30.08-47.68 126.08-33.088 245.888-150.976 245.888-286.4V63.872H193.6zM511.872 458.24a107.264 107.264 0 0 0-14.848-16l-2.88-2.496-2.304-2.048-2.56-1.728a104.448 104.448 0 0 0-35.2-16.768c-65.28-17.088-113.664-67.392-135.744-135.936h387.264C683.52 351.744 634.88 402.048 569.408 419.2c-23.552 6.144-43.52 20.288-57.6 38.976z m-17.472 146.24l2.944-2.368c2.112-1.92 3.776-3.456 5.376-5.12l2.112-2.24 2.944-3.456 2.944-3.712s1.024-1.472 1.024-1.6l6.016 9.152 2.56 1.408 0.832 0.768a82.752 82.752 0 0 0 29.248 19.392l3.136 1.088c1.408 0.512 2.816 0.96 4.288 1.344 72.96 20.48 117.568 80.96 117.568 177.024v18.56H348.608v-18.56c0-95.36 43.776-155.52 115.648-176.64 8.704-2.176 16.96-5.888 24.576-11.008l1.664-0.896 3.84-3.136z" fill="#000000" p-id="27271"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
1
src/icons/svg/save-line.svg
Normal file
1
src/icons/svg/save-line.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1705571586044" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17569" width="13" height="13"><path d="M925.248 356.928l-258.176-258.176a64 64 0 0 0-45.248-18.752H144a64 64 0 0 0-64 64v736a64 64 0 0 0 64 64h736a64 64 0 0 0 64-64V402.176a64 64 0 0 0-18.752-45.248zM288 144h192V256H288V144z m448 736H288V736h448v144z m144 0H800V704a32 32 0 0 0-32-32H256a32 32 0 0 0-32 32v176H144v-736H224V288a32 32 0 0 0 32 32h256a32 32 0 0 0 32-32V144h77.824l258.176 258.176V880z" p-id="17570"></path></svg>
|
||||
|
After Width: | Height: | Size: 541 B |
@@ -89,7 +89,7 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
const defaultFormSetting = this.getDefaultFormSetting()
|
||||
this.iFormSetting = Object.assign({}, this.formSetting, defaultFormSetting)
|
||||
this.iFormSetting = Object.assign({}, defaultFormSetting, this.formSetting)
|
||||
},
|
||||
methods: {
|
||||
handleCheckedFieldsChange(values) {
|
||||
@@ -107,6 +107,7 @@ export default {
|
||||
return {
|
||||
needGetObjectDetail: false,
|
||||
submitMethod: () => 'patch',
|
||||
cleanOtherFormValue: (formValue) => formValue,
|
||||
cleanFormValue: (value) => {
|
||||
const filterValue = {}
|
||||
Object.keys(value)
|
||||
@@ -114,12 +115,15 @@ export default {
|
||||
.forEach((key) => {
|
||||
filterValue[key] = value[key]
|
||||
})
|
||||
const formValue = []
|
||||
let formValue = []
|
||||
let object = {}
|
||||
for (const row of vm.selectedRows) {
|
||||
object = Object.assign({}, filterValue, { id: row.id })
|
||||
formValue.push(object)
|
||||
}
|
||||
if (typeof this.iFormSetting.cleanOtherFormValue === 'function') {
|
||||
formValue = this.iFormSetting.cleanOtherFormValue(formValue)
|
||||
}
|
||||
return formValue
|
||||
},
|
||||
onSubmit: function(validValues) {
|
||||
|
||||
@@ -61,7 +61,6 @@ export default {
|
||||
break
|
||||
case 'logout':
|
||||
this.logout()
|
||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||
break
|
||||
case 'apiKey':
|
||||
this.$router.push('/profile/api-keys')
|
||||
@@ -73,7 +72,12 @@ export default {
|
||||
this.$router.push('/profile/user/setting')
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
async logout() {
|
||||
const currentOrg = this.$store.getters.currentOrg
|
||||
if (currentOrg.autoEnter) {
|
||||
await this.$store.dispatch('users/setCurrentOrg', this.$store.getters.preOrg)
|
||||
}
|
||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ export default {
|
||||
code: 'cn',
|
||||
cookieCode: 'zh-hans' // cookie code是为了让后端知道当前语言
|
||||
},
|
||||
{
|
||||
title: '中文(繁體)',
|
||||
code: 'zh_hant',
|
||||
cookieCode: 'zh-hant' // cookie code是为了让后端知道当前语言
|
||||
},
|
||||
{
|
||||
title: 'English',
|
||||
code: 'en',
|
||||
@@ -64,6 +69,8 @@ export default {
|
||||
this.$moment.locale('en')
|
||||
} else if (this.currentLang.code.indexOf('ja') > -1) {
|
||||
this.$moment.locale('ja')
|
||||
} else if (this.currentLang.code.indexOf('zh_hant') > -1) {
|
||||
this.$moment.locale('zh-tw')
|
||||
} else {
|
||||
this.$moment.locale('zh-cn')
|
||||
}
|
||||
@@ -75,15 +82,19 @@ export default {
|
||||
window.location.reload()
|
||||
},
|
||||
getLangCode() {
|
||||
let langCode = localStorage.lang
|
||||
let langCode = this.$cookie.get(this.LANG_COOKIE_NAME)
|
||||
if (!langCode) {
|
||||
langCode = this.$cookie.get(this.LANG_COOKIE_NAME)
|
||||
langCode = localStorage.lang
|
||||
}
|
||||
if (!langCode) {
|
||||
langCode = navigator.language || navigator.userLanguage
|
||||
}
|
||||
langCode = langCode.substr(0, 2)
|
||||
langCode = langCode.replace('zh', 'cn')
|
||||
if (langCode === 'zh-hant') {
|
||||
langCode = 'zh_hant'
|
||||
} else {
|
||||
langCode = langCode.slice(0, 2)
|
||||
langCode = langCode.replace('zh', 'cn')
|
||||
}
|
||||
return langCode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ export default {
|
||||
name: 'WebTerminal',
|
||||
computed: {
|
||||
webTerminalUrl() {
|
||||
let url = `${BASE_URL}/luna/?_=${Date.now()}`
|
||||
const oid = this.$store.getters.currentOrg ? this.$store.getters.currentOrg.id : ''
|
||||
let url = `${BASE_URL}/luna/?_=${Date.now()}${oid ? `&oid=${oid}` : ''}`
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
url = url.replace('9528', '4200')
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="sidebar-logo-container" :class="{'collapse':collapse}">
|
||||
<transition name="sidebarLogoFade">
|
||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||
<a v-if="collapse" key="collapse" class="sidebar-logo-link" @click="handleClick">
|
||||
<img :src="logoSrc" class="sidebar-logo" alt="logo">
|
||||
</router-link>
|
||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||
</a>
|
||||
<a v-else key="expand" class="sidebar-logo-link" @click="handleClick">
|
||||
<img :src="logoTextSrc" class="sidebar-logo-text" alt="logo">
|
||||
</router-link>
|
||||
</a>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
@@ -27,6 +27,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'viewRoutes',
|
||||
'publicSettings'
|
||||
]),
|
||||
// eslint-disable-next-line vue/return-in-computed-property
|
||||
@@ -38,6 +39,18 @@ export default {
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
const currentPath = this.$route.path
|
||||
const matchingRoute = this.viewRoutes.find(route => currentPath.startsWith(route.path))
|
||||
|
||||
if (matchingRoute) {
|
||||
this.$router.push(matchingRoute.redirect)
|
||||
} else {
|
||||
this.$router.push('/')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -66,6 +66,9 @@ export default {
|
||||
font-weight: 500;
|
||||
line-height: 32px;
|
||||
color: #1F2329;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.page-heading-right {
|
||||
|
||||
@@ -71,6 +71,11 @@ export default {
|
||||
height: calc(100vh - 50px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
.el-alert {
|
||||
margin-top: -10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.go-back {
|
||||
|
||||
20
src/main.js
20
src/main.js
@@ -22,6 +22,7 @@ import ECharts from 'vue-echarts'
|
||||
import service from '@/utils/request'
|
||||
import { message } from '@/utils/message'
|
||||
import xss from '@/utils/xss'
|
||||
import request from '@/utils/request'
|
||||
import ElTableTooltipPatch from '@/utils/elTableTooltipPatch.js'
|
||||
|
||||
/**
|
||||
@@ -70,6 +71,7 @@ Vue.prototype.$xss = xss
|
||||
|
||||
// 注册全局事件总线
|
||||
Vue.prototype.$eventBus = eventBus
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
i18n,
|
||||
@@ -77,3 +79,21 @@ new Vue({
|
||||
store,
|
||||
render: h => h(App)
|
||||
})
|
||||
|
||||
;(function() {
|
||||
request({
|
||||
url: '/api/v1/authentication/user-session/',
|
||||
method: 'get'
|
||||
})
|
||||
})()
|
||||
|
||||
let IdBeforeunload = false
|
||||
|
||||
window.addEventListener('beforeunload', (event) => {
|
||||
if (IdBeforeunload) return
|
||||
IdBeforeunload = true
|
||||
request({
|
||||
url: '/api/v1/authentication/user-session/',
|
||||
method: 'delete'
|
||||
})
|
||||
})
|
||||
|
||||
@@ -30,6 +30,16 @@ export default [
|
||||
component: () => import('@/views/settings/Task/CeleryTaskLog'),
|
||||
name: 'TaskLog',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('setting.ChatAI'),
|
||||
permissions: []
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/chat/chat-ai/',
|
||||
component: () => import('@/views/chat/ChatAi'),
|
||||
name: 'ChatAi',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('route.CeleryTaskLog'),
|
||||
permissions: []
|
||||
|
||||
@@ -38,10 +38,14 @@ const mutations = {
|
||||
}
|
||||
},
|
||||
|
||||
updateChaMessageContentById(state, { id, content }) {
|
||||
updateChaMessageContentById(state, { id, data }) {
|
||||
const chats = state.activeChat.chats || []
|
||||
const filterChat = chats.filter((chat) => chat.message.id === id)?.[0] || {}
|
||||
filterChat.message.content = content
|
||||
if (Object.keys(filterChat).length > 0) {
|
||||
filterChat.message.content = data.message.content
|
||||
} else {
|
||||
chats?.push(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { getProfile as apiGetProfile, logout } from '@/api/users'
|
||||
import { getCurrentOrgLocal, getPreOrgLocal, getTokenFromCookie, saveCurrentOrgLocal, setPreOrgLocal } from '@/utils/auth'
|
||||
import {
|
||||
getCurrentOrgLocal, getPreOrgLocal, getTokenFromCookie, saveCurrentOrgLocal, setPreOrgLocal
|
||||
} from '@/utils/auth'
|
||||
import orgUtil from '@/utils/org'
|
||||
import { resetRouter } from '@/router'
|
||||
import Vue from 'vue'
|
||||
@@ -70,7 +72,7 @@ const mutations = {
|
||||
},
|
||||
SET_CURRENT_ORG(state, org) {
|
||||
// 系统组织和全局组织不设置成 Pre org
|
||||
if (!state.currentOrg?.autoEnter) {
|
||||
if (!state.currentOrg?.autoEnter && !state.currentOrg?.is_root) {
|
||||
state.preOrg = state.currentOrg
|
||||
setPreOrgLocal(state.username, state.currentOrg)
|
||||
}
|
||||
@@ -142,7 +144,7 @@ const actions = {
|
||||
const systemOrg = {
|
||||
id: orgUtil.SYSTEM_ORG_ID,
|
||||
name: 'SystemSetting',
|
||||
autoEnter: true
|
||||
autoEnter: new Date().getTime()
|
||||
}
|
||||
commit('SET_CURRENT_ORG', systemOrg)
|
||||
},
|
||||
@@ -157,7 +159,8 @@ const actions = {
|
||||
const globalOrg = {
|
||||
id: orgUtil.GLOBAL_ORG_ID,
|
||||
name: 'Global',
|
||||
autoEnter: true
|
||||
is_root: true,
|
||||
autoEnter: new Date().getTime()
|
||||
}
|
||||
commit('SET_CURRENT_ORG', globalOrg)
|
||||
},
|
||||
|
||||
BIN
src/styles/icons/dameng.png
Normal file
BIN
src/styles/icons/dameng.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -304,6 +304,10 @@ input[type=file] {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.el-dialog {
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
.el-input--mini .el-input__inner::placeholder {
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
|
||||
@@ -92,6 +92,10 @@
|
||||
background: url('./icons/db2.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
&.dameng_ico_docu {
|
||||
background: url('./icons/dameng.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
&.private_ico_docu {
|
||||
background: url('./icons/private.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
@@ -363,13 +363,19 @@ export function downloadText(content, filename) {
|
||||
}
|
||||
|
||||
export function download(downloadUrl, filename) {
|
||||
const iframe = document.createElement('iframe')
|
||||
iframe.style.display = 'none'
|
||||
document.body.appendChild(iframe)
|
||||
const a = document.createElement('a')
|
||||
a.href = downloadUrl
|
||||
if (filename) {
|
||||
a.download = filename
|
||||
}
|
||||
iframe.contentWindow.document.body.appendChild(a)
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(downloadUrl)
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(iframe)
|
||||
}, 1000 * 60 * 30) // If you can't download it in half an hour, don't download it.
|
||||
}
|
||||
|
||||
export function diffObject(object, base) {
|
||||
|
||||
@@ -8,6 +8,11 @@ export const GLOBAL_ORG_ID = '00000000-0000-0000-0000-000000000000'
|
||||
|
||||
function getPropOrg() {
|
||||
const orgs = store.getters.usingOrgs
|
||||
const preOrg = store.getters.preOrg || {}
|
||||
const preFound = orgs.find((item) => item.id === preOrg.id)
|
||||
if (preFound) {
|
||||
return preFound
|
||||
}
|
||||
const defaultOrg = orgs.find((item) => item.is_default)
|
||||
if (defaultOrg) {
|
||||
return defaultOrg
|
||||
@@ -62,7 +67,7 @@ async function changeOrg(org, reload = true, vm = null) {
|
||||
}
|
||||
}
|
||||
location.hash = '#' + path
|
||||
setTimeout(() => location.reload(), 400)
|
||||
setTimeout(() => location.reload(), 500)
|
||||
}
|
||||
|
||||
function hasCurrentOrgPermission() {
|
||||
|
||||
@@ -9,7 +9,6 @@ import { message } from '@/utils/message'
|
||||
import store from '@/store'
|
||||
import axiosRetry from 'axios-retry'
|
||||
import router from '@/router'
|
||||
import { DEFAULT_ORG_ID, SYSTEM_ORG_ID } from '@/utils/org'
|
||||
|
||||
// create an axios instance
|
||||
const service = axios.create({
|
||||
@@ -25,10 +24,7 @@ function beforeRequestAddToken(config) {
|
||||
}
|
||||
const queryOrgId = router.currentRoute.query?.oid
|
||||
const storeOrgId = store.getters.currentOrg?.id
|
||||
let orgId = queryOrgId || storeOrgId
|
||||
if (!store.getters.publicSettings?.XPACK_ENABLED && orgId !== SYSTEM_ORG_ID) {
|
||||
orgId = DEFAULT_ORG_ID
|
||||
}
|
||||
const orgId = queryOrgId || storeOrgId
|
||||
if (orgId) {
|
||||
config.headers['X-JMS-ORG'] = orgId
|
||||
}
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
import store from '@/store'
|
||||
import router, { resetRouter } from '@/router'
|
||||
import Vue from 'vue'
|
||||
import VueCookie from 'vue-cookie'
|
||||
import { message } from '@/utils/message'
|
||||
import orgUtil from '@/utils/org'
|
||||
import orgs from '@/api/orgs'
|
||||
import { getPropView, isViewHasOrgs } from '@/utils/jms'
|
||||
import request from '@/utils/request'
|
||||
|
||||
const whiteList = ['/login', process.env.VUE_APP_LOGIN_PATH] // no redirect whitelist
|
||||
const autoEnterOrgs = ['00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000000']
|
||||
|
||||
function reject(msg) {
|
||||
return new Promise((resolve, reject) => reject(msg))
|
||||
@@ -19,30 +18,15 @@ async function checkLogin({ to, from, next }) {
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
next()
|
||||
}
|
||||
// Determine whether the user has logged in
|
||||
const sessionExpire = VueCookie.get('jms_session_expire')
|
||||
if (!sessionExpire) {
|
||||
request.get(process.env['VUE_APP_LOGOUT_PATH']).finally(() => {
|
||||
window.location = process.env.VUE_APP_LOGIN_PATH
|
||||
})
|
||||
return reject('No session mark found in cookie')
|
||||
} else if (sessionExpire === 'close') {
|
||||
let startTime = new Date().getTime()
|
||||
setInterval(() => {
|
||||
const endTime = new Date().getTime()
|
||||
const delta = (endTime - startTime)
|
||||
startTime = endTime
|
||||
Vue.$log.debug('Set session expire: ', delta)
|
||||
VueCookie.set('jms_session_expire', 'close', { expires: '2m' })
|
||||
}, 10 * 1000)
|
||||
} else if (sessionExpire === 'age') {
|
||||
Vue.$log.debug('Session expire on age')
|
||||
}
|
||||
|
||||
try {
|
||||
return await store.dispatch('users/getProfile')
|
||||
} catch (e) {
|
||||
Vue.$log.error(e)
|
||||
const status = e.response.status
|
||||
if (store.getters.currentOrg.autoEnter) {
|
||||
await store.dispatch('users/setCurrentOrg', store.getters.preOrg)
|
||||
}
|
||||
if (status === 401 || status === 403) {
|
||||
setTimeout(() => {
|
||||
window.location = process.env.VUE_APP_LOGIN_PATH
|
||||
@@ -61,7 +45,11 @@ async function getPublicSetting({ to, from, next }, isOpen) {
|
||||
}
|
||||
|
||||
async function refreshCurrentOrg() {
|
||||
orgs.getCurrentOrg().then(org => {
|
||||
return orgs.getCurrentOrg().then(org => {
|
||||
// Root 就不刷新本地的了, 会影响 autoEnter
|
||||
if (autoEnterOrgs.indexOf(org.id) !== -1) {
|
||||
return
|
||||
}
|
||||
store.dispatch('users/setCurrentOrg', org)
|
||||
})
|
||||
}
|
||||
@@ -80,9 +68,16 @@ async function changeCurrentOrgIfNeed({ to, from, next }) {
|
||||
Vue.$log.error('Current org is null or not a object: ', currentOrg)
|
||||
await orgUtil.change2PropOrg({ to, from, next })
|
||||
}
|
||||
if (currentOrg.name === 'SystemSetting') {
|
||||
const preOrg = store.getters.preOrg
|
||||
await orgUtil.changeOrg(preOrg)
|
||||
const globalOrgPath = [
|
||||
'/console/perms/login-acls/', '/console/users/roles/',
|
||||
'/console/perms/connect-method-acls/', '/settings/'
|
||||
]
|
||||
if (autoEnterOrgs.indexOf(currentOrg.id) !== -1 && currentOrg.autoEnter) {
|
||||
const delta = new Date().getTime() - currentOrg.autoEnter
|
||||
const notNeedChange = globalOrgPath.find(path => to.path.indexOf(path) === 0)
|
||||
if (!notNeedChange && delta > 3000) {
|
||||
await orgUtil.change2PropOrg({ to, from, next })
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!orgUtil.hasCurrentOrgPermission()) {
|
||||
@@ -156,7 +151,9 @@ export async function changeCurrentViewIfNeed({ to, from, next }) {
|
||||
|
||||
export async function startup({ to, from, next }) {
|
||||
// if (store.getters.inited) { return true }
|
||||
if (store.getters.inited) { return true }
|
||||
if (store.getters.inited) {
|
||||
return true
|
||||
}
|
||||
await store.dispatch('app/init')
|
||||
|
||||
// set page title
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
const xss = require('xss')
|
||||
const excludeTags = ['iframe', 'script']
|
||||
|
||||
const options = {
|
||||
css: false,
|
||||
stripIgnoreTagBody: ['script'],
|
||||
onTag(tag, html, options) {
|
||||
if (tag === 'iframe') {
|
||||
return html.replace(/javascript:?/, '')
|
||||
if (excludeTags.indexOf(tag) !== -1) {
|
||||
return html.replace(/</g, '<').replace(/>/g, '>')
|
||||
}
|
||||
},
|
||||
// 避免把页面样式过滤掉
|
||||
onIgnoreTagAttr(tag, name, value, isWhiteAttr) {
|
||||
onTagAttr(tag, name, value, isWhiteAttr) {
|
||||
// 过滤掉标签上的事件
|
||||
if (/^[^on]/.test(name)) {
|
||||
return name + '="' + xss.escapeAttrValue(value) + '"'
|
||||
} else {
|
||||
if (tag === 'a') {
|
||||
return name + '="' + xss.escapeAttrValue(value) + '"'
|
||||
}
|
||||
if (/^on/.test(name)) {
|
||||
return name + '=' + '.'
|
||||
}
|
||||
if (['src', 'href'].indexOf(name) !== -1) {
|
||||
return name + '=' + value.replace('javascript:', 'java:').replace('data:', 'dt:')
|
||||
}
|
||||
return name + '="' + xss.escapeAttrValue(value) + '"'
|
||||
}
|
||||
}
|
||||
const filter = new xss.FilterXSS(options)
|
||||
|
||||
@@ -91,7 +91,7 @@ export default {
|
||||
title: this.$t('assets.TestAccountConnective'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('assets.Test'),
|
||||
label: this.$t('accounts.Test'),
|
||||
disabled: (
|
||||
!vm.$hasPerm('accounts.verify_account') ||
|
||||
!vm.object.asset.auto_config?.ansible_enabled ||
|
||||
@@ -104,7 +104,7 @@ export default {
|
||||
this.$axios.post(
|
||||
`/api/v1/accounts/accounts/tasks/`,
|
||||
{
|
||||
action: 'test',
|
||||
action: 'verify',
|
||||
accounts: [this.object.id]
|
||||
}
|
||||
).then(res => {
|
||||
|
||||
@@ -99,6 +99,12 @@ export default {
|
||||
hasBulkUpdate: false
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const plan_id = this.$route.query.plan_id
|
||||
if (plan_id !== undefined) {
|
||||
this.tableConfig.url = `${this.tableConfig.url}?plan_id=${plan_id}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -45,7 +45,7 @@ export default {
|
||||
fieldsMeta: {
|
||||
...getChangeSecretFields(),
|
||||
assets: {
|
||||
label: this.$t('xpack.Asset'),
|
||||
label: this.$t('assets.Asset'),
|
||||
type: 'assetSelect',
|
||||
component: AssetSelect,
|
||||
rules: [
|
||||
@@ -61,7 +61,7 @@ export default {
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
label: this.$t('xpack.Node'),
|
||||
label: this.$t('assets.Node'),
|
||||
el: {
|
||||
value: [],
|
||||
ajax: {
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
<template>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<div>
|
||||
<RecordViewSecret
|
||||
v-if="showViewSecretDialog"
|
||||
:url="secretUrl"
|
||||
:visible.sync="showViewSecretDialog"
|
||||
/>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import { ActionsFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import RecordViewSecret from '@/components/Apps/ChangeSecret/RecordViewSecret.vue'
|
||||
|
||||
export default {
|
||||
name: 'AccountChangeSecretExecutionTaskList',
|
||||
name: 'AccountChangeSecretRecord',
|
||||
components: {
|
||||
RecordViewSecret,
|
||||
GenericListTable
|
||||
},
|
||||
props: {
|
||||
@@ -20,7 +29,10 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
secretUrl: '',
|
||||
showViewSecretDialog: false,
|
||||
tableConfig: {
|
||||
url: `/api/v1/accounts/change-secret-records/?execution_id=${this.object.id}`,
|
||||
columns: [
|
||||
@@ -44,12 +56,11 @@ export default {
|
||||
}
|
||||
},
|
||||
account: {
|
||||
label: this.$t('users.Username'),
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
can: this.$hasPerm('accounts.view_account'),
|
||||
getTitle({ row }) {
|
||||
return row.account.name
|
||||
return row.account.username
|
||||
},
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
@@ -79,6 +90,19 @@ export default {
|
||||
hasClone: false,
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
// debugger
|
||||
vm.secretUrl = `/api/v1/accounts/change-secret-records/${row.id}/secret/`
|
||||
vm.showViewSecretDialog = false
|
||||
setTimeout(() => {
|
||||
vm.showViewSecretDialog = true
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Retry',
|
||||
title: this.$t('accounts.AccountChangeSecret.Retry'),
|
||||
@@ -87,7 +111,7 @@ export default {
|
||||
callback: ({ row }) => {
|
||||
this.$axios.post(
|
||||
'/api/v1/accounts/change-secret-records/execute/',
|
||||
{ record_id: row.id }
|
||||
{ record_ids: [row.id] }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
@@ -107,7 +131,57 @@ export default {
|
||||
hasImport: false,
|
||||
hasCreate: false,
|
||||
hasBulkDelete: false,
|
||||
hasBulkUpdate: false
|
||||
hasBulkUpdate: false,
|
||||
searchConfig: {
|
||||
exclude: ['id', 'status'],
|
||||
options: [
|
||||
{
|
||||
label: this.$t('accounts.AccountChangeSecret.Asset'),
|
||||
value: 'asset_name'
|
||||
},
|
||||
{
|
||||
label: this.$t('accounts.Accounts'),
|
||||
value: 'account_username'
|
||||
},
|
||||
{
|
||||
value: 'status',
|
||||
label: this.$t('common.Status'),
|
||||
type: 'choice',
|
||||
children: [
|
||||
{
|
||||
default: true,
|
||||
value: 'success',
|
||||
label: this.$t('common.Success')
|
||||
},
|
||||
{
|
||||
value: 'failed',
|
||||
label: this.$t('common.Failed')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
extraMoreActions: [
|
||||
{
|
||||
name: 'BatchRetry',
|
||||
title: this.$t('accounts.AccountChangeSecret.BatchRetry'),
|
||||
type: 'primary',
|
||||
fa: 'fa-retweet',
|
||||
can: ({ selectedRows }) => {
|
||||
return selectedRows.length > 0 && vm.$hasPerm('accounts.add_changesecretexecution')
|
||||
},
|
||||
callback: function({ selectedRows }) {
|
||||
const ids = selectedRows.map(v => {
|
||||
return v.id
|
||||
})
|
||||
this.$axios.post(
|
||||
'/api/v1/accounts/change-secret-records/execute/',
|
||||
{ record_ids: ids }).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,13 @@
|
||||
<script>
|
||||
import { GenericDetailPage } from '@/layout/components'
|
||||
import AccountChangeSecretExecutionInfo from './AccountChangeSecretExecutionInfo'
|
||||
import AccountChangeSecretExecutionTaskList from './AccountChangeSecretExecutionTaskList'
|
||||
import AccountChangeSecretRecord from './AccountChangeSecretRecord'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
AccountChangeSecretExecutionInfo,
|
||||
AccountChangeSecretExecutionTaskList
|
||||
AccountChangeSecretRecord
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
},
|
||||
{
|
||||
title: this.$t('accounts.AccountChangeSecret.TaskList'),
|
||||
name: 'AccountChangeSecretExecutionTaskList',
|
||||
name: 'AccountChangeSecretRecord',
|
||||
hidden: () => !this.$hasPerm('accounts.view_changesecretrecord')
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<GenericListTable :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -110,7 +110,7 @@ export default {
|
||||
hasSearch: true,
|
||||
hasRefresh: true,
|
||||
hasRightActions: true,
|
||||
hasLeftActions: true,
|
||||
hasLeftActions: false,
|
||||
hasMoreActions: false,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
@@ -119,6 +119,12 @@ export default {
|
||||
hasBulkUpdate: false
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const automation_id = this.$route.query.automation_id
|
||||
if (automation_id !== undefined) {
|
||||
this.tableConfig.url = `${this.tableConfig.url}?automation_id=${automation_id}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user