mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-14 03:46:26 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48beb502bd | ||
|
|
383a799c6a | ||
|
|
c378b2bf0d |
@@ -23,5 +23,4 @@ VUE_APP_LOGOUT_PATH = '/core/auth/logout/'
|
||||
|
||||
# Dev server for core proxy
|
||||
VUE_APP_CORE_HOST = 'http://localhost:8080'
|
||||
VUE_APP_CORE_WS = 'ws://localhost:8070'
|
||||
VUE_APP_ENV = 'development'
|
||||
|
||||
@@ -12,8 +12,8 @@ RUN npm config set sass_binary_site=${SASS_BINARY_SITE}
|
||||
RUN npm config set registry ${NPM_REGISTRY}
|
||||
RUN yarn config set registry ${NPM_REGISTRY}
|
||||
COPY package.json yarn.lock /data/
|
||||
RUN yarn install
|
||||
RUN npm rebuild node-sass
|
||||
COPY utils /data/utils/
|
||||
RUN ls && cd utils && bash -xieu build.sh dep
|
||||
|
||||
ADD . /data
|
||||
RUN cd utils && bash -xieu build.sh build
|
||||
|
||||
@@ -57,26 +57,6 @@ export function TestReplayStorage(id) {
|
||||
})
|
||||
}
|
||||
|
||||
function SetToDefaultStorage(url) {
|
||||
return request({
|
||||
url: url,
|
||||
method: 'patch',
|
||||
data: { 'is_default': true }
|
||||
})
|
||||
}
|
||||
|
||||
export function SetToDefaultCommandStorage(id) {
|
||||
return SetToDefaultStorage(
|
||||
`/api/v1/terminal/command-storages/${id}/`,
|
||||
)
|
||||
}
|
||||
|
||||
export function SetToDefaultReplayStorage(id) {
|
||||
return SetToDefaultStorage(
|
||||
`/api/v1/terminal/replay-storages/${id}/`,
|
||||
)
|
||||
}
|
||||
|
||||
export function getReplayStorage(id) {
|
||||
return request({
|
||||
url: `/api/v1/terminal/replay-storages/${id}/`,
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<MFAVerifyDialog
|
||||
@MFAVerifyDone="getAuthInfo"
|
||||
@MFAVerifyCancel="exit"
|
||||
/>
|
||||
<Dialog
|
||||
:title="dialogTitle"
|
||||
:show-confirm="false"
|
||||
:show-cancel="false"
|
||||
:destroy-on-close="true"
|
||||
:width="'50'"
|
||||
:visible.sync="showAuthInfo"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<div>
|
||||
<el-form label-position="right" label-width="80px" :model="authInfo">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="account.hostname" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="account['username']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="authInfo.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.SSHKey')">
|
||||
<el-input v-model="authInfo['private_key']" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import MFAVerifyDialog from '@/components/MFAVerifyDialog'
|
||||
export default {
|
||||
name: 'ShowSecretInfo',
|
||||
components: {
|
||||
Dialog,
|
||||
MFAVerifyDialog
|
||||
},
|
||||
props: {
|
||||
account: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogTitle: this.$t('common.ViewSecret'),
|
||||
authInfo: {},
|
||||
showAuthInfo: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getAuthInfo()
|
||||
},
|
||||
methods: {
|
||||
getAuthInfo() {
|
||||
const url = `/api/v1/assets/account-secrets/${this.account.id}/`
|
||||
this.$axios.get(url, { disableFlashErrorMsg: true }).then(resp => {
|
||||
this.authInfo = resp
|
||||
this.showAuthInfo = true
|
||||
})
|
||||
},
|
||||
exit() {
|
||||
this.$emit('update:visible', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,92 +0,0 @@
|
||||
<template>
|
||||
<Dialog
|
||||
width="50"
|
||||
:title="this.$t('assets.UpdateAssetUserToken')"
|
||||
:destroy-on-close="true"
|
||||
v-bind="$attrs"
|
||||
@confirm="handleConfirm()"
|
||||
@cancel="handleCancel()"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<el-form label-position="right" label-width="80px">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="account.hostname" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="account['username']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="authInfo.password" type="password" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.SSHKey')">
|
||||
<input type="file" @change="onPrivateKeyLoaded">
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
export default {
|
||||
name: 'UpdateSecretInfo',
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
account: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
authInfo: {
|
||||
password: '',
|
||||
private_key: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleConfirm() {
|
||||
const data = {}
|
||||
if (this.authInfo.password !== '') {
|
||||
data.password = this.authInfo.password
|
||||
}
|
||||
if (this.authInfo.private_key !== '') {
|
||||
data.private_key = this.authInfo.private_key
|
||||
}
|
||||
this.$axios.patch(
|
||||
`/api/v1/assets/accounts/${this.account.id}/`,
|
||||
data
|
||||
).then(res => {
|
||||
this.authInfo = { password: '', private_key: '' }
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
this.$emit('updateAuthDone', res)
|
||||
this.$emit('update:visible', false)
|
||||
}).catch(err => {
|
||||
const errMsg = Object.values(err.response.data).join(', ')
|
||||
this.$message.error(this.$tc('common.updateErrorMsg') + ' ' + errMsg)
|
||||
this.$emit('update:visible', true)
|
||||
})
|
||||
},
|
||||
handleCancel() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
onPrivateKeyLoaded(e) {
|
||||
const vm = this
|
||||
// TODO 校验文件类型
|
||||
const reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
vm.authInfo.private_key = this.result
|
||||
}
|
||||
reader.readAsText(
|
||||
e.target.files[0]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,31 +0,0 @@
|
||||
import { ChoicesFormatter } from '@/components/TableFormatters'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export const connectivityMeta = {
|
||||
label: i18n.t('assets.Reachable'),
|
||||
formatter: ChoicesFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
ok: 'fa-check text-primary',
|
||||
failed: 'fa-times text-danger',
|
||||
unknown: 'fa-circle text-warning'
|
||||
},
|
||||
hasTips: true,
|
||||
getTips: ({ row, cellValue }) => {
|
||||
const mapper = {
|
||||
'ok': i18n.t('assets.Reachable'),
|
||||
'failed': i18n.t('assets.Unreachable'),
|
||||
'unknown': i18n.t('assets.Unknown')
|
||||
}
|
||||
let tips = mapper[cellValue]
|
||||
if (row['date_verified']) {
|
||||
const datetime = toSafeLocalDateStr(row['date_verified'])
|
||||
tips += '<br> ' + datetime
|
||||
}
|
||||
return tips
|
||||
}
|
||||
},
|
||||
width: '90px',
|
||||
align: 'center'
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<ShowSecretInfo v-if="showViewSecretDialog" :visible.sync="showViewSecretDialog" :account="account" />
|
||||
<UpdateSecretInfo :visible.sync="showUpdateSecretDialog" :account="account" @updateAuthDone="onUpdateAuthDone" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import { ActionsFormatter, DetailFormatter, DisplayFormatter } from '@/components/TableFormatters'
|
||||
import ShowSecretInfo from './ShowSecretInfo'
|
||||
import UpdateSecretInfo from './UpdateSecretInfo'
|
||||
import { connectivityMeta } from './const'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'AccountListTable',
|
||||
components: {
|
||||
ListTable,
|
||||
UpdateSecretInfo,
|
||||
ShowSecretInfo
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
exportUrl: {
|
||||
type: String,
|
||||
default() {
|
||||
return this.url.replace('/assets/accounts/', '/assets/account-secrets/')
|
||||
}
|
||||
},
|
||||
hasLeftActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
otherActions: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
hasClone: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showViewSecretDialog: false,
|
||||
showUpdateSecretDialog: false,
|
||||
account: {},
|
||||
tableConfig: {
|
||||
url: this.url,
|
||||
columns: [
|
||||
'hostname', 'ip', 'username', 'version', 'connectivity',
|
||||
'systemuser', 'date_created', 'date_updated', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['username', 'actions'],
|
||||
default: ['hostname', 'ip', 'username', 'version', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
hostname: {
|
||||
prop: 'hostname',
|
||||
label: this.$t('assets.Hostname'),
|
||||
showOverflowTooltip: true,
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.asset }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ip: {
|
||||
width: '120px'
|
||||
},
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
systemuser: {
|
||||
formatter: DisplayFormatter
|
||||
},
|
||||
version: {
|
||||
width: '70px'
|
||||
},
|
||||
connectivity: connectivityMeta,
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
hasClone: this.hasClone,
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: function({ row }) {
|
||||
this.account = row
|
||||
this.showViewSecretDialog = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
title: this.$t('common.Delete'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
this.$axios.delete(`/api/v1/assets/accounts/${row.id}/`).then(() => {
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
this.$refs.ListTable.reloadTable()
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Test',
|
||||
title: this.$t('common.Test'),
|
||||
callback: ({ row }) => {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/accounts/${row.id}/verify/`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
title: this.$t('common.Update'),
|
||||
can: !this.$store.getters.currentOrgIsRoot,
|
||||
callback: function({ row }) {
|
||||
this.account = row
|
||||
this.showUpdateSecretDialog = true
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: this.hasLeftActions,
|
||||
hasMoreActions: false,
|
||||
hasImport: false,
|
||||
hasExport: true,
|
||||
exportOptions: {
|
||||
url: this.exportUrl,
|
||||
mfaVerifyRequired: true
|
||||
},
|
||||
searchConfig: {
|
||||
exclude: ['systemuser', 'asset']
|
||||
},
|
||||
hasSearch: true
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url(iNew) {
|
||||
this.$set(this.tableConfig, 'url', iNew)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.otherActions) {
|
||||
const actionColumn = this.tableConfig.columns[this.tableConfig.columns.length - 1]
|
||||
for (const item of this.otherActions) {
|
||||
actionColumn.formatterArgs.extraActions.push(item)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onUpdateAuthDone(account) {
|
||||
Object.assign(this.account, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<MFAVerifyDialog
|
||||
@MFAVerifyDone="getAuthInfo"
|
||||
@MFAVerifyCancel="exit"
|
||||
/>
|
||||
<Dialog
|
||||
:title="dialogTitle"
|
||||
:show-confirm="false"
|
||||
:show-cancel="false"
|
||||
:destroy-on-close="true"
|
||||
:width="'50'"
|
||||
:visible.sync="showAuthInfo"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<div>
|
||||
<el-form label-position="right" label-width="80px" :model="authInfo">
|
||||
<el-form-item :label="this.$t('applications.appName')">
|
||||
<el-input v-model="account['app_name']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="account['username']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="authInfo.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import MFAVerifyDialog from '@/components/MFAVerifyDialog'
|
||||
export default {
|
||||
name: 'ShowSecretInfo',
|
||||
components: {
|
||||
Dialog,
|
||||
MFAVerifyDialog
|
||||
},
|
||||
props: {
|
||||
account: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogTitle: this.$t('common.ViewSecret'),
|
||||
authInfo: {},
|
||||
showAuthInfo: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getAuthInfo()
|
||||
},
|
||||
methods: {
|
||||
getAuthInfo() {
|
||||
console.log(this.account)
|
||||
const url = `/api/v1/applications/account-secrets/${this.account.uid}/`
|
||||
this.$axios.get(url, { disableFlashErrorMsg: true }).then(resp => {
|
||||
this.authInfo = resp
|
||||
this.showAuthInfo = true
|
||||
})
|
||||
},
|
||||
exit() {
|
||||
this.$emit('update:visible', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,31 +0,0 @@
|
||||
import { ChoicesFormatter } from '@/components/TableFormatters'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export const connectivityMeta = {
|
||||
label: i18n.t('assets.Reachable'),
|
||||
formatter: ChoicesFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
ok: 'fa-check text-primary',
|
||||
failed: 'fa-times text-danger',
|
||||
unknown: 'fa-circle text-warning'
|
||||
},
|
||||
hasTips: true,
|
||||
getTips: ({ row, cellValue }) => {
|
||||
const mapper = {
|
||||
'ok': i18n.t('assets.Reachable'),
|
||||
'failed': i18n.t('assets.Unreachable'),
|
||||
'unknown': i18n.t('assets.Unknown')
|
||||
}
|
||||
let tips = mapper[cellValue]
|
||||
if (row['date_verified']) {
|
||||
const datetime = toSafeLocalDateStr(row['date_verified'])
|
||||
tips += '<br> ' + datetime
|
||||
}
|
||||
return tips
|
||||
}
|
||||
},
|
||||
width: '90px',
|
||||
align: 'center'
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<ShowSecretInfo v-if="showViewSecretDialog" :visible.sync="showViewSecretDialog" :account="account" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import { ActionsFormatter, DetailFormatter } from '@/components/TableFormatters'
|
||||
import ShowSecretInfo from './ShowSecretInfo'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
ListTable,
|
||||
ShowSecretInfo
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
exportUrl: {
|
||||
type: String,
|
||||
default() {
|
||||
return this.url.replace('/applications/accounts/', '/applications/account-secrets/')
|
||||
}
|
||||
},
|
||||
hasLeftActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
otherActions: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
hasClone: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showViewSecretDialog: false,
|
||||
showUpdateSecretDialog: false,
|
||||
account: {},
|
||||
tableConfig: {
|
||||
url: this.url,
|
||||
columns: [
|
||||
'app_name', 'username', 'category_display',
|
||||
'type_display', 'systemuser', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
app_name: {
|
||||
showOverflowTooltip: true,
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
getRoute({ row }) {
|
||||
switch (row['category']) {
|
||||
case 'remote_app':
|
||||
return {
|
||||
name: 'RemoteAppDetail',
|
||||
params: { id: row.app }
|
||||
}
|
||||
case 'db':
|
||||
return {
|
||||
name: 'DatabaseAppDetail',
|
||||
params: { id: row.app }
|
||||
}
|
||||
default:
|
||||
return {
|
||||
name: 'KubernetesAppDetail',
|
||||
params: { id: row.app }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
systemuser: {
|
||||
showOverflowTooltip: true,
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
getTitle({ row }) {
|
||||
return row.systemuser_display
|
||||
},
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
name: 'SystemUserDetail',
|
||||
params: { id: row.systemuser }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
hasClone: this.hasClone,
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: function({ row }) {
|
||||
this.account = row
|
||||
this.showViewSecretDialog = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
title: this.$t('common.Update'),
|
||||
can: !this.$store.getters.currentOrgIsRoot,
|
||||
callback: function({ row }) {
|
||||
this.$message.success(this.$tc('applications.updateAccountMsg'))
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: this.hasLeftActions,
|
||||
hasMoreActions: false,
|
||||
hasImport: false,
|
||||
hasExport: true,
|
||||
exportOptions: {
|
||||
url: this.exportUrl,
|
||||
mfaVerifyRequired: true
|
||||
},
|
||||
searchConfig: {
|
||||
exclude: ['systemuser', 'asset']
|
||||
},
|
||||
hasSearch: true
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url(iNew) {
|
||||
this.$set(this.tableConfig, 'url', iNew)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.otherActions) {
|
||||
const actionColumn = this.tableConfig.columns[this.tableConfig.columns.length - 1]
|
||||
for (const item of this.otherActions) {
|
||||
actionColumn.formatterArgs.extraActions.push(item)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onUpdateAuthDone(account) {
|
||||
Object.assign(this.account, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -76,7 +76,7 @@ export default {
|
||||
select2Config: select2Config,
|
||||
dialogSelect2Config: select2Config,
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/assets/?fields_size=mini',
|
||||
url: '/api/v1/assets/assets/',
|
||||
hasTree: true,
|
||||
canSelect: this.canSelect,
|
||||
columns: [
|
||||
@@ -94,18 +94,6 @@ export default {
|
||||
prop: 'ip',
|
||||
label: this.$t('assets.ipDomain'),
|
||||
sortable: 'custom'
|
||||
},
|
||||
{
|
||||
prop: 'platform',
|
||||
label: this.$t('assets.Platform'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
prop: 'protocols',
|
||||
formatter: function(row) {
|
||||
return <span> {row.protocols.toString()} </span>
|
||||
},
|
||||
label: this.$t('assets.Protocols')
|
||||
}
|
||||
],
|
||||
listeners: {
|
||||
|
||||
529
src/components/AssetUserTable/index.vue
Normal file
529
src/components/AssetUserTable/index.vue
Normal file
@@ -0,0 +1,529 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<ListTable ref="ListTable" :table-config="iTableConfig" :header-actions="headerActions" />
|
||||
<Dialog v-if="showMFADialog" width="50" :title="this.$t('common.MFAConfirm')" :visible.sync="showMFADialog" :show-confirm="false" :show-cancel="false" :destroy-on-close="true">
|
||||
<div v-if="MFAConfirmed">
|
||||
<el-form label-position="right" label-width="80px" :model="MFAInfo">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="MFAInfo.hostname" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="MFAInfo.username" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="MFAInfo.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-row v-else :gutter="20">
|
||||
<el-col :span="4">
|
||||
<div style="line-height: 34px;text-align: center">MFA</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-input v-model="MFAInput" />
|
||||
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button size="mini" type="primary" style="line-height:20px " @click="MFAConfirm">{{ this.$t('common.Confirm') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
<Dialog width="50" :title="this.$t('assets.UpdateAssetUserToken')" :visible.sync="showDialog" @confirm="handleConfirm()" @cancel="handleCancel()">
|
||||
<el-form label-position="right" label-width="80px" :model="dialogInfo">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="dialogInfo.hostname" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="dialogInfo.username" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="dialogInfo.password" type="password" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.sshkey')">
|
||||
<input type="file" @change="Onchange">
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
<Dialog :title="$t('common.Export')" :visible.sync="showExportDialog" :destroy-on-close="true" @confirm="handleExportConfirm()" @cancel="handleExportCancel()">
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportOption">
|
||||
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import * as queryUtil from '@/components/DataTable/compenents/el-data-table/utils/query'
|
||||
import { ActionsFormatter, DateFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
ListTable,
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
searchExclude: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
extraQuery: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
canExportAll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
canExportSelected: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
canExportFiltered: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hasLeftActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
otherActions: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
handleExport: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
handleImport: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
hasImport: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hasExport: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hasClone: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
MFAConfirmed: false,
|
||||
MFAInput: '',
|
||||
MFAInfo: {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: ''
|
||||
},
|
||||
showDialog: false,
|
||||
showMFADialog: false,
|
||||
dialogInfo: {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: '',
|
||||
private_key: ''
|
||||
},
|
||||
selectedRows: '',
|
||||
dialogStatus: '',
|
||||
showExportDialog: false,
|
||||
exportOption: 'all',
|
||||
exportTypeOption: 'csv',
|
||||
defaultTableConfig: {
|
||||
url: this.url,
|
||||
columns: ['hostname', 'ip', 'username', 'version', 'date_created', 'actions'],
|
||||
columnsMeta: {
|
||||
'hostname': {
|
||||
label: this.$t('assets.Hostname'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
'ip': {
|
||||
label: this.$t('assets.ip'),
|
||||
width: '120px'
|
||||
},
|
||||
'username': {
|
||||
label: this.$t('assets.Username'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
'version': {
|
||||
label: this.$t('assets.Version'),
|
||||
width: '70px'
|
||||
},
|
||||
'date_created': {
|
||||
label: this.$t('assets.date_joined'),
|
||||
formatter: DateFormatter
|
||||
},
|
||||
'actions': {
|
||||
label: this.$t('common.Action'),
|
||||
align: 'center',
|
||||
width: 150,
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
hasClone: this.hasClone,
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: function(val) {
|
||||
this.dialogStatus = 'viewAutoInfo'
|
||||
this.MFAInfo.asset = val.row.id
|
||||
if (!this.needMFAVerify) {
|
||||
this.showMFADialog = true
|
||||
this.MFAConfirmed = true
|
||||
this.$axios.get(`/api/v1/assets/asset-user-auth-infos/${this.MFAInfo.asset}/`).then(res => {
|
||||
this.MFAConfirmed = true
|
||||
this.MFAInfo.hostname = res.hostname
|
||||
this.MFAInfo.password = res.password
|
||||
this.MFAInfo.username = res.username
|
||||
})
|
||||
} else {
|
||||
this.showMFADialog = true
|
||||
}
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
title: this.$t('common.Delete'),
|
||||
type: 'primary',
|
||||
callback: (val) => {
|
||||
this.$axios.delete(`/api/v1/assets/asset-users/${val.row.id}/`).then(() => {
|
||||
this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
this.$refs.ListTable.reloadTable()
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Test',
|
||||
title: this.$t('common.Test'),
|
||||
callback: (val) => {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/asset-users/tasks/?id=${val.row.id}`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
title: this.$t('common.Update'),
|
||||
can: !this.$store.getters.currentOrgIsRoot,
|
||||
callback: function(val) {
|
||||
this.showDialog = true
|
||||
this.dialogInfo.asset = val.row.asset
|
||||
this.dialogInfo.hostname = val.row.hostname
|
||||
this.dialogInfo.username = val.row.username
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
extraQuery: this.extraQuery || { latest: 1 }
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: this.hasLeftActions,
|
||||
hasMoreActions: false,
|
||||
hasImport: this.hasImport,
|
||||
hasExport: this.hasExport,
|
||||
hasSearch: true,
|
||||
searchConfig: {
|
||||
exclude: this.searchExclude,
|
||||
options: [
|
||||
{
|
||||
label: this.$t('assets.OnlyLatestVersion'),
|
||||
value: 'latest',
|
||||
children: [
|
||||
{
|
||||
label: this.$t('common.Yes'),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: this.$t('common.No'),
|
||||
value: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'MFA_TTl',
|
||||
'MFAVerifyAt',
|
||||
'publicSettings'
|
||||
]),
|
||||
needMFAVerify() {
|
||||
if (!this.publicSettings.SECURITY_VIEW_AUTH_NEED_MFA) {
|
||||
return false
|
||||
}
|
||||
const ttl = this.publicSettings.SECURITY_MFA_VERIFY_TTL
|
||||
const now = new Date()
|
||||
return !(this.MFAVerifyAt && (now - this.MFAVerifyAt) < ttl * 1000)
|
||||
},
|
||||
iTableConfig() {
|
||||
const columnsMeta = Object.assign({}, this.defaultTableConfig.columnsMeta, this.tableConfig.columnsMeta || {})
|
||||
const config = Object.assign(this.defaultTableConfig, this.tableConfig)
|
||||
config.columnsMeta = columnsMeta
|
||||
return config
|
||||
},
|
||||
exportOptions() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('common.imExport.ExportAll'),
|
||||
value: 'all',
|
||||
can: this.canExportAll
|
||||
},
|
||||
{
|
||||
label: this.$t('common.imExport.ExportOnlySelectedItems'),
|
||||
value: 'selected',
|
||||
can: this.selectedRows.length > 0 && this.canExportSelected
|
||||
},
|
||||
{
|
||||
label: this.$t('common.imExport.ExportOnlyFiltered'),
|
||||
value: 'filtered',
|
||||
can: this.tableHasQuery() && this.canExportFiltered
|
||||
}
|
||||
]
|
||||
},
|
||||
exportTypeOptions() {
|
||||
return [
|
||||
{
|
||||
label: 'CSV',
|
||||
value: 'csv',
|
||||
can: true
|
||||
},
|
||||
{
|
||||
label: 'Excel',
|
||||
value: 'xlsx',
|
||||
can: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url(iNew) {
|
||||
this.$set(this.iTableConfig, 'url', iNew)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.otherActions) {
|
||||
const actionColumn = this.iTableConfig.columns[this.iTableConfig.columns.length - 1]
|
||||
for (const item of this.otherActions) {
|
||||
actionColumn.formatterArgs.extraActions.push(item)
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.headerActions.handleExport = this.handleExport || this.defaultHandleExport
|
||||
if (this.handleImport) {
|
||||
this.headerActions.handleImport = this.handleImport
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
MFAConfirm() {
|
||||
if (this.MFAInput.length !== 6) {
|
||||
return this.$message.error(this.$t('common.MFAErrorMsg'))
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/otp/verify/`, {
|
||||
code: this.MFAInput
|
||||
}
|
||||
).then(
|
||||
res => {
|
||||
this.$store.dispatch('users/setMFAVerify')
|
||||
if (this.dialogStatus === 'export') {
|
||||
this.showMFADialog = false
|
||||
this.showExportDialog = true
|
||||
} else {
|
||||
this.$axios.get(`/api/v1/assets/asset-user-auth-infos/${this.MFAInfo.asset}/`).then(res => {
|
||||
this.MFAConfirmed = true
|
||||
this.MFAInfo.hostname = res.hostname
|
||||
this.MFAInfo.password = res.password
|
||||
this.MFAInfo.username = res.username
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
handleMFAConfirm() {
|
||||
this.MFAInfo = {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: ''
|
||||
}
|
||||
this.MFAInput = ''
|
||||
this.showMFADialog = false
|
||||
this.MFAConfirmed = false
|
||||
},
|
||||
handleCancel() {
|
||||
this.dialogInfo = {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: '',
|
||||
private_key: ''
|
||||
}
|
||||
this.showDialog = false
|
||||
this.$refs.ListTable.reloadTable()
|
||||
},
|
||||
Onchange(e) {
|
||||
const vm = this
|
||||
// TODO 校验文件类型
|
||||
const reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
vm.dialogInfo.private_key = this.result
|
||||
}
|
||||
reader.readAsText(
|
||||
e.target.files[0]
|
||||
)
|
||||
},
|
||||
handleConfirm() {
|
||||
const data = {
|
||||
asset: this.dialogInfo.asset,
|
||||
username: this.dialogInfo.username
|
||||
}
|
||||
if (this.dialogInfo.password !== '') {
|
||||
data.password = this.dialogInfo.password
|
||||
}
|
||||
if (this.dialogInfo.private_key !== '') {
|
||||
data.private_key = this.dialogInfo.private_key
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/asset-users/`,
|
||||
data
|
||||
).then(res => {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
}).catch(err => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
|
||||
})
|
||||
this.dialogInfo = {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: '',
|
||||
private_key: ''
|
||||
}
|
||||
this.showDialog = false
|
||||
this.$refs.ListTable.reloadTable()
|
||||
},
|
||||
tableQuery() {
|
||||
const listTableRef = this.$refs.ListTable
|
||||
if (!listTableRef) {
|
||||
return {}
|
||||
}
|
||||
const query = listTableRef.dataTable.getQuery()
|
||||
delete query['limit']
|
||||
delete query['offset']
|
||||
delete query['date_from']
|
||||
delete query['date_to']
|
||||
return query
|
||||
},
|
||||
tableHasQuery() {
|
||||
return Object.keys(this.tableQuery()).length > 0
|
||||
},
|
||||
defaultHandleExport({ selectedRows }) {
|
||||
this.selectedRows = selectedRows
|
||||
this.dialogStatus = 'export'
|
||||
if (!this.needMFAVerify) {
|
||||
this.showMFADialog = false
|
||||
this.showExportDialog = true
|
||||
} else {
|
||||
this.showMFADialog = true
|
||||
}
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
},
|
||||
async performExport(selectRows, exportOption, q) {
|
||||
const url = `/api/v1/assets/asset-user-auth-infos/`
|
||||
const query = Object.assign({}, q)
|
||||
if (exportOption === 'selected') {
|
||||
const resources = []
|
||||
const data = selectRows
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
resources.push(data[index].id)
|
||||
}
|
||||
const spm = await createSourceIdCache(resources)
|
||||
query['spm'] = spm.spm
|
||||
} else if (exportOption === 'filtered') {
|
||||
// console.log(listTableRef)
|
||||
// console.log(listTableRef.dataTable)
|
||||
// delete query['limit']
|
||||
// delete query['offset']
|
||||
}
|
||||
query['format'] = this.exportTypeOption
|
||||
const queryStr =
|
||||
(url.indexOf('?') > -1 ? '&' : '?') +
|
||||
queryUtil.stringify(query, '=', '&')
|
||||
return this.downloadCsv(url + queryStr)
|
||||
},
|
||||
async performExportConfirm() {
|
||||
const listTableRef = this.$refs.ListTable
|
||||
const query = listTableRef.dataTable.getQuery()
|
||||
delete query['limit']
|
||||
delete query['offset']
|
||||
return this.performExport(this.selectedRows, this.exportOption, query)
|
||||
},
|
||||
async handleExportConfirm() {
|
||||
await this.performExportConfirm()
|
||||
this.showExportDialog = false
|
||||
},
|
||||
handleExportCancel() {
|
||||
this.showExportDialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.export-item {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.export-form >>> .el-form-item__label {
|
||||
line-height: 2
|
||||
}
|
||||
</style>
|
||||
@@ -9,7 +9,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataForm from '@/components/DataForm'
|
||||
import { DataForm } from '@/components'
|
||||
|
||||
export default {
|
||||
name: 'NestedField',
|
||||
|
||||
@@ -170,12 +170,12 @@ export default {
|
||||
col.filters = column.choices.map(item => {
|
||||
if (typeof (item.value) === 'boolean') {
|
||||
if (item.value) {
|
||||
return { text: item['display_name'], value: 'True' }
|
||||
return { text: item.display_name, value: 'True' }
|
||||
} else {
|
||||
return { text: item['display_name'], value: 'False' }
|
||||
return { text: item.display_name, value: 'False' }
|
||||
}
|
||||
}
|
||||
return { text: item['display_name'], value: item.value }
|
||||
return { text: item.display_name, value: item.value }
|
||||
})
|
||||
col.sortable = false
|
||||
col['column-key'] = col.prop
|
||||
@@ -223,7 +223,7 @@ export default {
|
||||
defaultColumnsNames = totalColumnsNames.filter(n => defaultColumnsNames.indexOf(n) > -1)
|
||||
|
||||
// 最小列
|
||||
const minColumnsNames = _.get(this.iConfig, 'columnsShow.min', ['actions', 'id'])
|
||||
const minColumnsNames = _.get(this.iConfig, 'columnsShow.min', ['action', 'id'])
|
||||
.filter(n => defaultColumnsNames.indexOf(n) > -1)
|
||||
|
||||
// 应该显示的列
|
||||
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
autoParam: ['id=key', 'name=n', 'level=lv'],
|
||||
type: 'get',
|
||||
headers: {
|
||||
'X-JMS-ORG': this.$store.getters.currentOrg.id
|
||||
'X-JMS-ORG': JSON.parse(this.$cookie.get('jms_current_org')) ? JSON.parse(this.$cookie.get('jms_current_org')).id : ''
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
@@ -89,7 +89,7 @@ export default {
|
||||
return
|
||||
}
|
||||
if (currentNode) {
|
||||
currentNode.name = currentNode.meta.data.value
|
||||
currentNode.name = currentNode.meta.node.value
|
||||
}
|
||||
this.zTree.editName(currentNode)
|
||||
},
|
||||
@@ -108,12 +108,12 @@ export default {
|
||||
const query = Object.assign({}, this.$route.query)
|
||||
if (treeNode.meta.type === 'node') {
|
||||
this.currentNode = treeNode
|
||||
this.currentNodeId = treeNode.meta.data.id
|
||||
this.currentNodeId = treeNode.meta.node.id
|
||||
query['node'] = this.currentNodeId
|
||||
url = `${this.setting.url}${combinator}node_id=${treeNode.meta.data.id}&show_current_asset=${show_current_asset}`
|
||||
url = `${this.setting.url}${combinator}node_id=${treeNode.meta.node.id}&show_current_asset=${show_current_asset}`
|
||||
} else if (treeNode.meta.type === 'asset') {
|
||||
query['asset'] = treeNode.meta.data.id
|
||||
url = `${this.setting.url}${combinator}asset_id=${treeNode.meta.data.id}&show_current_asset=${show_current_asset}`
|
||||
query['asset'] = treeNode.meta.asset.id
|
||||
url = `${this.setting.url}${combinator}asset_id=${treeNode.meta.asset.id}&show_current_asset=${show_current_asset}`
|
||||
}
|
||||
this.$router.push({ query })
|
||||
this.$emit('urlChange', url)
|
||||
@@ -125,7 +125,7 @@ export default {
|
||||
return
|
||||
}
|
||||
this.$axios.delete(
|
||||
`${this.treeSetting.nodeUrl}${currentNode.meta.data.id}/`
|
||||
`${this.treeSetting.nodeUrl}${currentNode.meta.node.id}/`
|
||||
).then(() => {
|
||||
this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
this.zTree.removeNode(currentNode)
|
||||
@@ -143,7 +143,7 @@ export default {
|
||||
url,
|
||||
{ 'value': treeNode.name }
|
||||
).then(res => {
|
||||
let assetsAmount = treeNode.meta.data.assetsAmount
|
||||
let assetsAmount = treeNode.meta.node.assetsAmount
|
||||
if (!assetsAmount) {
|
||||
assetsAmount = 0
|
||||
}
|
||||
@@ -208,9 +208,9 @@ export default {
|
||||
onDrop: function(event, treeId, treeNodes, targetNode, moveType) {
|
||||
const treeNodesIds = []
|
||||
$.each(treeNodes, function(index, value) {
|
||||
treeNodesIds.push(value.meta.data.id)
|
||||
treeNodesIds.push(value.meta.node.id)
|
||||
})
|
||||
const theUrl = `${this.treeSetting.nodeUrl}${targetNode.meta.data.id}/children/add/`
|
||||
const theUrl = `${this.treeSetting.nodeUrl}${targetNode.meta.node.id}/children/add/`
|
||||
this.$axios.put(
|
||||
theUrl, {
|
||||
nodes: treeNodesIds
|
||||
@@ -229,21 +229,21 @@ export default {
|
||||
}
|
||||
this.zTree.expandNode(parentNode, true, false, true, false)
|
||||
// http://localhost/api/v1/assets/nodes/85aa4ee2-0bd9-41db-9079-aa3646448d0c/children/
|
||||
const url = `${this.treeSetting.nodeUrl}${parentNode.meta.data.id}/children/`
|
||||
const url = `${this.treeSetting.nodeUrl}${parentNode.meta.node.id}/children/`
|
||||
this.$axios.post(url, {}).then(data => {
|
||||
const newNode = {
|
||||
id: data['key'],
|
||||
name: data['value'],
|
||||
pId: parentNode.id,
|
||||
meta: {
|
||||
data: data
|
||||
'node': data
|
||||
}
|
||||
}
|
||||
newNode.checked = this.zTree.getSelectedNodes()[0].checked
|
||||
this.zTree.addNodes(parentNode, 0, newNode)
|
||||
// vm.$refs.dataztree.refresh()
|
||||
const node = this.zTree.getNodeByParam('id', newNode.id, parentNode)
|
||||
this.currentNodeId = node.meta.data.id || newNode.id
|
||||
this.currentNodeId = node.meta.node.id || newNode.id
|
||||
this.zTree.editName(node)
|
||||
this.$message.success(this.$t('common.createSuccessMsg'))
|
||||
}).catch(error => {
|
||||
|
||||
@@ -840,7 +840,7 @@ export default {
|
||||
this.$emit('selection-change', val)
|
||||
},
|
||||
totalData(val) {
|
||||
if (val && val.length !== this.total) {
|
||||
if (val) {
|
||||
this.page = defaultFirstPage
|
||||
this.total = val.length
|
||||
this.getList()
|
||||
|
||||
@@ -28,20 +28,13 @@ export default {
|
||||
return this.formatter(this.item, this.value)
|
||||
}
|
||||
if (typeof this.value === 'boolean') {
|
||||
return (
|
||||
<span class='item-value'>{this.toChoicesDisplay(this.value)}</span>
|
||||
)
|
||||
return <span>{this.toChoicesDisplay(this.value)}</span>
|
||||
}
|
||||
return (
|
||||
<span class='item-value'>{this.value}</span>
|
||||
)
|
||||
return <span>{this.value}</span>
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.item-value {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<el-select
|
||||
ref="select"
|
||||
v-model="iValue"
|
||||
v-loading="!initialized"
|
||||
v-loadmore="loadMore"
|
||||
:options="iOptions"
|
||||
:remote="remote"
|
||||
@@ -98,7 +97,7 @@ export default {
|
||||
return {
|
||||
loading: false,
|
||||
initialized: false,
|
||||
iValue: this.multiple ? [] : '',
|
||||
iValue: this.value ? this.value : [],
|
||||
defaultParams: _.cloneDeep(defaultParams),
|
||||
params: _.cloneDeep(defaultParams),
|
||||
iOptions: this.options || [],
|
||||
@@ -162,14 +161,11 @@ export default {
|
||||
this.iValue = iNew
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
mounted() {
|
||||
// this.$log.debug('Select2 url is: ', this.iAjax.url)
|
||||
if (!this.initialized) {
|
||||
await this.initialSelect()
|
||||
setTimeout(() => {
|
||||
this.initialized = true
|
||||
this.iValue = this.value
|
||||
})
|
||||
this.initialSelect()
|
||||
this.initialized = true
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
// 因为elform存在问题,这个来清楚验证
|
||||
@@ -247,13 +243,9 @@ export default {
|
||||
// this.$log.debug('Select ajax config', this.iAjax)
|
||||
if (this.iAjax.url) {
|
||||
if (this.value && this.value.length !== 0) {
|
||||
this.$log.debug('Start init select2 value, ', this.value)
|
||||
let value = this.value
|
||||
if (!Array.isArray(value)) {
|
||||
value = [value]
|
||||
}
|
||||
const data = await createSourceIdCache(value)
|
||||
this.params.spm = data['spm']
|
||||
this.$log.debug('Start init select2 value')
|
||||
const data = await createSourceIdCache(this.value)
|
||||
this.params.spm = data.spm
|
||||
await this.getInitialOptions()
|
||||
}
|
||||
await this.getOptions()
|
||||
|
||||
@@ -22,12 +22,8 @@ export default {
|
||||
}
|
||||
},
|
||||
rules(item) {
|
||||
let userIsOrgAdmin = item.el.userIsOrgAdmin
|
||||
// undefined 个人信息更新或用户更改密码页面,使用当前用户;否则使用更新用户表单中传递的值
|
||||
userIsOrgAdmin = userIsOrgAdmin === undefined ? store.getters.currentUserIsAdmin : userIsOrgAdmin
|
||||
|
||||
const passwordRule = store.getters.publicSettings.PASSWORD_RULE
|
||||
const validatePassword = function(rule, value, callback) {
|
||||
const validatePassword = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
return callback()
|
||||
}
|
||||
@@ -50,10 +46,7 @@ export default {
|
||||
return callback(new Error(msg))
|
||||
}
|
||||
}
|
||||
let secureLength = passwordRule ? passwordRule.SECURITY_PASSWORD_MIN_LENGTH : 7
|
||||
if (userIsOrgAdmin) {
|
||||
secureLength = passwordRule ? passwordRule.SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH : 7
|
||||
}
|
||||
const secureLength = passwordRule ? passwordRule.SECURITY_PASSWORD_MIN_LENGTH : 7
|
||||
if (value.length < secureLength) {
|
||||
return callback(new Error(i18n.t('common.password.MIN_LENGTH_ERROR', [secureLength])))
|
||||
}
|
||||
@@ -66,12 +59,17 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
attrs: {
|
||||
secureLength: 7
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['publicSettings'])
|
||||
},
|
||||
created() {
|
||||
const passwordRule = this.publicSettings.PASSWORD_RULE || {}
|
||||
this.attrs.secureLength = passwordRule ? passwordRule.SECURITY_PASSWORD_MIN_LENGTH : 7
|
||||
},
|
||||
methods: {
|
||||
handleInput(value) {
|
||||
this.$emit('input', value)
|
||||
|
||||
@@ -27,7 +27,7 @@ export default {
|
||||
vm.tableConfig.initialUrl = vm.tableConfig.url
|
||||
}
|
||||
const initialUrl = vm.tableConfig.initialUrl
|
||||
const nodeId = node.meta.data.id
|
||||
const nodeId = node.meta.node.id
|
||||
const url = initialUrl.replace('/assets/', `/nodes/${nodeId}/assets/`)
|
||||
vm.tableConfig.url = url
|
||||
}
|
||||
|
||||
@@ -1,45 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
<MFAVerifyDialog
|
||||
v-if="mfaDialogShow"
|
||||
@MFAVerifyDone="showExportDialog"
|
||||
@MFAVerifyCancel="handleExportCancel"
|
||||
/>
|
||||
<Dialog
|
||||
v-if="exportDialogShow"
|
||||
:title="$t('common.Export')"
|
||||
:visible.sync="exportDialogShow"
|
||||
:destroy-on-close="true"
|
||||
@confirm="handleExportConfirm()"
|
||||
@cancel="handleExportCancel()"
|
||||
>
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item class="export-form" :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportOption">
|
||||
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</div>
|
||||
<Dialog v-if="showExportDialog" :title="$t('common.Export')" :visible.sync="showExportDialog" :destroy-on-close="true" @confirm="handleExportConfirm()" @cancel="handleExportCancel()">
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item class="export-form" :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportOption">
|
||||
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import MFAVerifyDialog from '@/components/MFAVerifyDialog'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import * as queryUtil from '@/components/DataTable/compenents/el-data-table/utils/query'
|
||||
|
||||
export default {
|
||||
name: 'ExportDialog',
|
||||
components: {
|
||||
Dialog,
|
||||
MFAVerifyDialog
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
selectedRows: {
|
||||
@@ -48,20 +32,12 @@ export default {
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
beforeExport: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
mfaVerifyRequired: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: () => ''
|
||||
},
|
||||
performExport: {
|
||||
type: Function,
|
||||
default(selectedRows, exportOptions, query, exportType) {
|
||||
return this.defaultPerformExport(selectedRows, exportOptions, query, exportType)
|
||||
default(selectedRows, exportOptions, query) {
|
||||
return this.defaultPerformExport(selectedRows, exportOptions, query)
|
||||
}
|
||||
},
|
||||
canExportAll: {
|
||||
@@ -79,12 +55,10 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
exportDialogShow: false,
|
||||
showExportDialog: false,
|
||||
exportOption: 'all',
|
||||
exportTypeOption: 'csv',
|
||||
meta: {},
|
||||
mfaVerified: false,
|
||||
mfaDialogShow: false
|
||||
meta: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -141,33 +115,20 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('showExportDialog', ({ selectedRows, url, name }) => {
|
||||
// Todo: 没有时间了,只能先这么处理了
|
||||
if (url === this.url || url.indexOf(this.url) > -1 || url.indexOf('account') > -1) {
|
||||
this.showExportDialog()
|
||||
this.$eventBus.$on('showExportDialog', ({ selectedRows, url }) => {
|
||||
if (url === this.url) {
|
||||
this.showExportDialog = true
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
showExportDialog() {
|
||||
if (!this.mfaVerifyRequired) {
|
||||
this.exportDialogShow = true
|
||||
return
|
||||
}
|
||||
// 这是需要校验 MFA 的
|
||||
if (!this.mfaDialogShow) {
|
||||
this.mfaDialogShow = true
|
||||
} else {
|
||||
this.exportDialogShow = true
|
||||
}
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
},
|
||||
async defaultPerformExport(selectRows, exportOption, q, exportTypeOption) {
|
||||
async defaultPerformExport(selectRows, exportOption, q) {
|
||||
const url = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)
|
||||
const query = Object.assign({}, q)
|
||||
if (exportOption === 'selected') {
|
||||
@@ -178,8 +139,13 @@ export default {
|
||||
}
|
||||
const spm = await createSourceIdCache(resources)
|
||||
query['spm'] = spm.spm
|
||||
} else if (exportOption === 'filtered') {
|
||||
// console.log(listTableRef)
|
||||
// console.log(listTableRef.dataTable)
|
||||
// delete query['limit']
|
||||
// delete query['offset']
|
||||
}
|
||||
query['format'] = exportTypeOption
|
||||
query['format'] = this.exportTypeOption
|
||||
const queryStr =
|
||||
(url.indexOf('?') > -1 ? '&' : '?') +
|
||||
queryUtil.stringify(query, '=', '&')
|
||||
@@ -187,20 +153,17 @@ export default {
|
||||
},
|
||||
async handleExport() {
|
||||
const listTableRef = this.$parent.$parent.$parent.$parent
|
||||
const query = listTableRef['dataTable'].getQuery()
|
||||
const query = listTableRef.dataTable.getQuery()
|
||||
delete query['limit']
|
||||
delete query['offset']
|
||||
await this.beforeExport()
|
||||
return this.performExport(this.selectedRows, this.exportOption, query, this.exportTypeOption)
|
||||
return this.performExport(this.selectedRows, this.exportOption, query)
|
||||
},
|
||||
async handleExportConfirm() {
|
||||
await this.handleExport()
|
||||
this.exportDialogShow = false
|
||||
this.mfaDialogShow = false
|
||||
this.showExportDialog = false
|
||||
},
|
||||
handleExportCancel() {
|
||||
this.exportDialogShow = false
|
||||
this.mfaDialogShow = false
|
||||
this.showExportDialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ExportDialog :selected-rows="selectedRows" v-bind="exportOptions" v-on="$listeners" />
|
||||
<ImportDialog :selected-rows="selectedRows" v-bind="importOptions" v-on="$listeners" />
|
||||
<ExportDialog :selected-rows="selectedRows" :url="url" v-bind="$attrs" v-on="$listeners" />
|
||||
<ImportDialog :selected-rows="selectedRows" :url="url" v-bind="$attrs" v-on="$listeners" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -10,7 +10,7 @@ import ExportDialog from './ExportDialog'
|
||||
import ImportDialog from './ImportDialog'
|
||||
|
||||
export default {
|
||||
name: 'ImExportDialog',
|
||||
name: 'DialogAction',
|
||||
components: {
|
||||
ExportDialog,
|
||||
ImportDialog
|
||||
@@ -20,13 +20,9 @@ export default {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
exportOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
importOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
url: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
<script>
|
||||
import DataTable from '@/components/DataTable'
|
||||
import { sleep, getUpdateObjURL } from '@/utils/common'
|
||||
import { sleep } from '@/utils/common'
|
||||
import { EditableInputFormatter, StatusFormatter } from '@/components/TableFormatters'
|
||||
export default {
|
||||
name: 'ImportTable',
|
||||
@@ -350,7 +350,7 @@ export default {
|
||||
}
|
||||
},
|
||||
async performUpdateObject(item) {
|
||||
const updateUrl = getUpdateObjURL(this.url, item.id)
|
||||
const updateUrl = `${this.url}${item.id}/`
|
||||
return this.$axios.put(
|
||||
updateUrl,
|
||||
item,
|
||||
|
||||
@@ -24,16 +24,6 @@ export default {
|
||||
hasLeftActions: defaultTrue,
|
||||
hasCreate: defaultTrue,
|
||||
canCreate: defaultTrue,
|
||||
createRoute: {
|
||||
type: [String, Object, Function],
|
||||
default: function() {
|
||||
return this.$route.name.replace('List', 'Create')
|
||||
}
|
||||
},
|
||||
createInNewPage: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hasBulkDelete: defaultTrue,
|
||||
hasBulkUpdate: defaultFalse,
|
||||
hasMoreActions: defaultTrue,
|
||||
@@ -41,6 +31,12 @@ export default {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
createRoute: {
|
||||
type: [String, Object, Function],
|
||||
default: function() {
|
||||
return this.$route.name.replace('List', 'Create')
|
||||
}
|
||||
},
|
||||
reloadTable: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
@@ -162,13 +158,8 @@ export default {
|
||||
} else if (typeof this.createRoute === 'object') {
|
||||
route = this.createRoute
|
||||
}
|
||||
this.$router.push(route)
|
||||
this.$log.debug('handle create')
|
||||
if (this.createInNewPage) {
|
||||
const { href } = this.$router.resolve(route)
|
||||
window.open(href, '_blank')
|
||||
} else {
|
||||
this.$router.push(route)
|
||||
}
|
||||
},
|
||||
defaultBulkDeleteCallback({ selectedRows, reloadTable }) {
|
||||
const msg = this.$t('common.deleteWarningMsg') + ' ' + selectedRows.length + ' ' + this.$t('common.rows') + ' ?'
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ActionsGroup :is-fa="true" :actions="rightSideActions" class="right-side-actions right-side-item" />
|
||||
<ImExportDialog
|
||||
:selected-rows="selectedRows"
|
||||
:export-options="iExportOptions"
|
||||
:import-options="iImportOptions"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<ActionsGroup :is-fa="true" :actions="rightSideActions" :url="tableUrl" class="right-side-actions right-side-item" />
|
||||
<ImExportDialog :selected-rows="selectedRows" :url="tableUrl" v-bind="$attrs" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +9,6 @@
|
||||
import ActionsGroup from '@/components/ActionsGroup'
|
||||
import ImExportDialog from './ImExportDialog'
|
||||
import { cleanActions } from './utils'
|
||||
import { assignIfNot } from '@/utils/common'
|
||||
|
||||
const defaultTrue = { type: Boolean, default: true }
|
||||
|
||||
@@ -30,32 +24,24 @@ export default {
|
||||
default: ''
|
||||
},
|
||||
hasExport: defaultTrue,
|
||||
exportOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
handleExportClick: {
|
||||
handleExport: {
|
||||
type: Function,
|
||||
default: function({ selectedRows }) {
|
||||
this.$eventBus.$emit('showExportDialog', { selectedRows, url: this.tableUrl, name: this.name })
|
||||
this.$eventBus.$emit('showExportDialog', { selectedRows, url: this.tableUrl })
|
||||
}
|
||||
},
|
||||
hasImport: defaultTrue,
|
||||
importOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
handleImportClick: {
|
||||
handleImport: {
|
||||
type: Function,
|
||||
default: function({ selectedRows }) {
|
||||
this.$eventBus.$emit('showImportDialog', { selectedRows, url: this.tableUrl, name: this.name })
|
||||
this.$eventBus.$emit('showImportDialog', { selectedRows, url: this.tableUrl })
|
||||
}
|
||||
},
|
||||
hasColumnSetting: defaultTrue,
|
||||
handleTableSettingClick: {
|
||||
handleColumnConfig: {
|
||||
type: Function,
|
||||
default: function({ selectedRows }) {
|
||||
this.$eventBus.$emit('showColumnSettingPopover', { url: this.tableUrl, row: selectedRows, name: this.name })
|
||||
this.$eventBus.$emit('showColumnSettingPopover', { url: this.tableUrl })
|
||||
}
|
||||
},
|
||||
hasRefresh: defaultTrue,
|
||||
@@ -75,12 +61,13 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
defaultRightSideActions: [
|
||||
{ name: 'actionColumnSetting', fa: 'fa-cog', tip: this.$t('common.CustomCol'), has: this.hasColumnSetting, callback: this.handleTableSettingClick.bind(this) },
|
||||
{ name: 'actionExport', fa: 'fa-download', tip: this.$t('common.Export'), has: this.hasExport, callback: this.handleExportClick.bind(this) },
|
||||
{ name: 'actionImport', fa: 'fa-upload', tip: this.$t('common.Import'), has: this.hasImport, callback: this.handleImportClick.bind(this) },
|
||||
{ name: 'actionColumnSetting', fa: 'fa-cog', tip: this.$t('common.CustomCol'), has: this.hasColumnSetting, callback: this.handleColumnConfig.bind(this) },
|
||||
{ name: 'actionExport', fa: 'fa-download', tip: this.$t('common.Export'), has: this.hasExport, callback: this.handleExport.bind(this) },
|
||||
{ name: 'actionImport', fa: 'fa-upload', tip: this.$t('common.Import'), has: this.hasImport, callback: this.handleImport.bind(this) },
|
||||
{ name: 'actionRefresh', fa: 'fa-refresh', tip: this.$t('common.Refresh'), has: this.hasRefresh, callback: this.handleRefresh }
|
||||
],
|
||||
dialogExportVisible: false
|
||||
dialogExportVisible: false,
|
||||
exportValue: 2
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -94,19 +81,18 @@ export default {
|
||||
},
|
||||
hasSelectedRows() {
|
||||
return this.selectedRows.length > 0
|
||||
},
|
||||
iImportOptions() {
|
||||
return assignIfNot(this.importOptions, { url: this.tableUrl })
|
||||
},
|
||||
iExportOptions() {
|
||||
const options = assignIfNot(this.exportOptions, { url: this.tableUrl })
|
||||
return options
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleTagSearch(val) {
|
||||
this.searchTable(val)
|
||||
},
|
||||
// handleExport({ selectedRows }) {
|
||||
// this.$eventBus.$emit('showExportDialog', { selectedRows })
|
||||
// },
|
||||
// handleImport({ selectedRows }) {
|
||||
// this.$eventBus.$emit('showImportDialog', { selectedRows })
|
||||
// },
|
||||
handleRefresh() {
|
||||
this.reloadTable()
|
||||
}
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<TableAction
|
||||
:table-url="tableUrl"
|
||||
:search-table="search"
|
||||
:date-pick="handleDateChange"
|
||||
:selected-rows="selectedRows"
|
||||
:reload-table="reloadTable"
|
||||
v-bind="headerActions"
|
||||
/>
|
||||
<TableAction :table-url="iTableConfig.url" :search-table="search" :date-pick="handleDateChange" v-bind="headerActions" :selected-rows="selectedRows" :reload-table="reloadTable" />
|
||||
<IBox class="table-content">
|
||||
<AutoDataTable
|
||||
ref="dataTable"
|
||||
:filter-table="filter"
|
||||
:config="iTableConfig"
|
||||
@selection-change="handleSelectionChange"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
<AutoDataTable ref="dataTable" :filter-table="filter" :config="iTableConfig" @selection-change="handleSelectionChange" v-on="$listeners" />
|
||||
</IBox>
|
||||
</div>
|
||||
</template>
|
||||
@@ -63,9 +50,6 @@ export default {
|
||||
this.$log.debug('Header actions', this.headerActions)
|
||||
this.$log.debug('ListTable: iTableConfig change', config)
|
||||
return config
|
||||
},
|
||||
tableUrl() {
|
||||
return this.iTableConfig.url
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -129,6 +113,15 @@ export default {
|
||||
& >>> .el-table__header thead > tr > th {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/*& >>> .el-table--striped .el-table__body tr.el-table__row--striped td {*/
|
||||
/*background: white;*/
|
||||
/*}*/
|
||||
|
||||
/*& >>> .el-table th, .el-table tr {*/
|
||||
/*background-color: red;*/
|
||||
/*!*background-color: #FAFAFA;*!*/
|
||||
/*}*/
|
||||
}
|
||||
|
||||
//修改颜色
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:title="$t('common.MFAVerify')"
|
||||
:width="'50'"
|
||||
:show-confirm="false"
|
||||
:show-cancel="false"
|
||||
:visible.sync="visible"
|
||||
:destroy-on-close="true"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4">
|
||||
<div style="line-height: 34px;text-align: center">MFA</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-input v-model="MFAToken" />
|
||||
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button size="mini" type="primary" style="line-height:20px " @click="verifyMFA">
|
||||
{{ this.$t('common.Confirm') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
|
||||
export default {
|
||||
name: 'MFAVerifyDialog',
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
MFAToken: '',
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (!val) {
|
||||
this.$emit('MFAVerifyCancel', true)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$axios.get('/api/v1/authentication/otp/verify/', { disableFlashErrorMsg: true }).then(() => {
|
||||
this.$emit('MFAVerifyDone', true)
|
||||
}).catch(err => {
|
||||
this.$log.debug('Verify otp code error: ', err)
|
||||
this.visible = true
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
verifyMFA() {
|
||||
if (this.MFAToken.length !== 6) {
|
||||
return this.$message.error(this.$tc('common.MFAErrorMsg'))
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/otp/verify/`, {
|
||||
code: this.MFAToken
|
||||
}
|
||||
).then(res => {
|
||||
this.$emit('MFAVerifyDone', true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -2,11 +2,15 @@
|
||||
<el-card shadow="never">
|
||||
<div slot="header" class="summary-header">
|
||||
<span class="header-title">{{ title }}</span>
|
||||
<span class="pull-right right-side">
|
||||
<slot name="header-right">
|
||||
<el-tag :type="rightSideLabel.type || 'success'" effect="dark" size="mini">{{ rightSideLabel.title }}</el-tag>
|
||||
</slot>
|
||||
</span>
|
||||
</div>
|
||||
<slot>
|
||||
<h1 class="no-margins">
|
||||
<span v-if="body.disabled" class="disabled-link">{{ body.count }}</span>
|
||||
<router-link v-else :to="body.route">
|
||||
<router-link :to="body.route">
|
||||
<span>{{ body.count }}</span>
|
||||
</router-link>
|
||||
</h1>
|
||||
@@ -70,8 +74,4 @@ export default {
|
||||
.no-margins {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.disabled-link {
|
||||
color: #428bca;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,12 +8,9 @@ import BaseFormatter from './base'
|
||||
|
||||
const defaultPerformDelete = function({ row, col }) {
|
||||
const id = row.id
|
||||
const url = new URL(this.url, location.origin)
|
||||
url.pathname += `${id}/`
|
||||
const deleteUrl = url.href
|
||||
return this.$axios.delete(deleteUrl)
|
||||
const url = `${this.url}${id}/`
|
||||
return this.$axios.delete(url)
|
||||
}
|
||||
|
||||
const defaultUpdateCallback = function({ row, col }) {
|
||||
const id = row.id
|
||||
let route = { params: { id: id }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip v-if="formatterArgs.hasTips" placement="bottom" effect="dark">
|
||||
<div slot="content" v-html="tips" />
|
||||
<div slot="content">{{ tipStatus }}<br>{{ tipTime }}</div>
|
||||
<i :class="'fa ' + iconClass" />
|
||||
</el-tooltip>
|
||||
<i v-else :class="'fa ' + iconClass" />
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
export default {
|
||||
name: 'ChoicesFormatter',
|
||||
extends: BaseFormatter,
|
||||
@@ -22,12 +23,21 @@ export default {
|
||||
true: 'fa-check text-primary',
|
||||
false: 'fa-times text-danger'
|
||||
},
|
||||
getIconKey({ row, cellValue }) {
|
||||
return cellValue
|
||||
typeChange(val) {
|
||||
return !!val
|
||||
},
|
||||
hasTips: false,
|
||||
getTips: ({ row, cellValue }) => {
|
||||
return cellValue
|
||||
tipStatus(val, vm) {
|
||||
if (!val) {
|
||||
return vm.$t('assets.Unknown')
|
||||
}
|
||||
if (val.status === 0) {
|
||||
return vm.$t('assets.Unreachable')
|
||||
} else if (val.status === 1) {
|
||||
return vm.$t('assets.Reachable')
|
||||
} else if (val.status === 2) {
|
||||
return vm.$t('assets.Unknown')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,11 +50,18 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
iconClass() {
|
||||
const key = this.formatterArgs.getIconKey({ row: this.row, cellValue: this.cellValue })
|
||||
const key = this.formatterArgs.typeChange(this.cellValue)
|
||||
return this.formatterArgs.iconChoices[key]
|
||||
},
|
||||
tips() {
|
||||
return this.formatterArgs.getTips({ cellValue: this.cellValue, row: this.row })
|
||||
tipStatus() {
|
||||
const vm = this
|
||||
return this.formatterArgs.tipStatus(this.cellValue, vm)
|
||||
},
|
||||
tipTime() {
|
||||
if (!this.cellValue) {
|
||||
return ''
|
||||
}
|
||||
return toSafeLocalDateStr(this.cellValue.datetime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default {
|
||||
defaultOnDelete(col, row, cellValue, reload) {
|
||||
const url = col.deleteUrl + cellValue
|
||||
this.$axios.delete(url).then(res => {
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
reload()
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$t('common.deleteErrorMsg') + ' ' + error)
|
||||
|
||||
@@ -77,9 +77,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleUrlChange(url) {
|
||||
this.$set(this.iTableConfig, 'url', url)
|
||||
this.$emit('urlChange', url)
|
||||
handleUrlChange(_url) {
|
||||
this.$set(this.iTableConfig, 'url', _url)
|
||||
this.$emit('urlChange', _url)
|
||||
this.forceRerender()
|
||||
},
|
||||
forceRerender() {
|
||||
|
||||
@@ -22,6 +22,5 @@ export { default as QuickActions } from './QuickActions'
|
||||
export { default as Switcher } from './FormFields/Swicher'
|
||||
export { default as SummaryCard } from './SummaryCard'
|
||||
export { default as UploadField } from './FormFields/UploadField'
|
||||
export { default as AccountListTable } from './AccountListTable/index'
|
||||
export { default as AssetUserTable } from './AssetUserTable'
|
||||
export { default as AssetRelationCard } from './AssetRelationCard'
|
||||
export { default as MFAVerifyDialog } from './MFAVerifyDialog'
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
},
|
||||
"applications": {
|
||||
"": "",
|
||||
"updateAccountMsg": "请更新系统用户的账号信息",
|
||||
"RemoteApp": "远程应用",
|
||||
"Database": "数据库",
|
||||
"Cloud": "云",
|
||||
@@ -84,14 +83,11 @@
|
||||
"DBInfo": "数据库信息"
|
||||
},
|
||||
"assets": {
|
||||
"AssociateSystemUsers": "关联系统用户",
|
||||
"AssociateAssets": "关联资产",
|
||||
"AssociateNodes": "关联节点",
|
||||
"Action": "动作",
|
||||
"ActiveSelected": "激活所选",
|
||||
"AdminUser": "特权用户",
|
||||
"AdminUser": "管理用户",
|
||||
"AdminUserDetail": "管理用户详情",
|
||||
"AdminUserListHelpMessage": "<b>特权用户</b> 是资产已存在的, 并且拥有 高级权限 的系统用户, 如 root 或 拥有 `NOPASSWD: ALL` sudo 权限的用户。 JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等。",
|
||||
"AdminUserListHelpMessage": "管理用户是资产(被控服务器)上的 root,或拥有 NOPASSWD: ALL sudo 权限的用户, JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等。\n",
|
||||
"Asset": "资产",
|
||||
"HardwareInfo": "硬件信息",
|
||||
"AssetDetail": "资产详情",
|
||||
@@ -102,11 +98,11 @@
|
||||
"TestGatewayHelpMessage": "如果使用了nat端口映射,请设置为ssh真实监听的端口",
|
||||
"SshPort": "SSH 端口",
|
||||
"AssetNumber": "资产编号",
|
||||
"AssetUserList": "账号列表",
|
||||
"AssetUserList": "资产用户列表",
|
||||
"Assets": "资产",
|
||||
"Auth": "认证",
|
||||
"AccountList": "账号列表",
|
||||
"AutoGenerateKey": "自动生成",
|
||||
"AutoGenerateKey": "自动生成密钥",
|
||||
"AutoPush": "自动推送",
|
||||
"BasePlatform": "基础平台",
|
||||
"Basic": "基本",
|
||||
@@ -119,7 +115,6 @@
|
||||
"CommandFilterRules": "命令过滤器规则",
|
||||
"Comment": "备注",
|
||||
"Cpu": "CPU",
|
||||
"CommonUser": "普通用户",
|
||||
"CreatedBy": "创建者",
|
||||
"Database": "数据库",
|
||||
"DateJoined": "创建日期",
|
||||
@@ -161,11 +156,9 @@
|
||||
"PriorityHelpMessage": "1-100, 1最低优先级,100最高优先级。授权多个用户时,高优先级的系统用户将会作为默认登录用户",
|
||||
"Protocol": "协议",
|
||||
"Protocols": "协议组",
|
||||
"LoginOption": "登录选项",
|
||||
"PublicIp": "公网IP",
|
||||
"Push": "推送",
|
||||
"PushSystemUserNow": "推送系统用户",
|
||||
"PushAllSystemUsersToAsset": "推送所有系统用户到资产",
|
||||
"QuickUpdate": "快速更新",
|
||||
"Reachable": "可连接",
|
||||
"Unreachable": "不可连接",
|
||||
@@ -185,17 +178,15 @@
|
||||
"PasswordHelpMessage": "密码或密钥密码",
|
||||
"SystemUser": "系统用户",
|
||||
"SystemUserDetail": "系统用户详情",
|
||||
"SystemUserListHelpMessage": "<b>系统用户</b> 是JumpServer 登录资产时使用的账号,如 root `ssh root@host`,而不是使用该用户名登录资产(ssh admin@host)`;<br><b>特权用户</b> 是资产已存在的, 并且拥有 高级权限 的系统用户, JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等;</br><b>普通用户</b> 可以在资产上预先存在,也可以由 特权用户 来自动创建。",
|
||||
"DynamicUsername": "动态用户名",
|
||||
"SystemUserListHelpMessage": "系统用户是 JumpServer 跳转登录资产时使用的用户,可以理解为登录资产用户,如 web,sa,dba(`ssh web@some-host`),而不是使用某个用户的用户名跳转登录服务器(`ssh xiaoming@some-host`); 简单来说是用户使用自己的用户名登录 JumpServer,JumpServer 使用系统用户登录资产。 系统用户创建时,如果选择了自动推送,JumpServer 会使用 Ansible 自动推送系统用户到资产中,如果资产(交换机)不支持 Ansible,请手动填写账号密码。\n",
|
||||
"SystemUsers": "系统用户",
|
||||
"Test": "测试",
|
||||
"TestAssetsConnective": "测试资产可连接性",
|
||||
"TestAllSystemUsersConnective": "测试所有系统用户可连接性",
|
||||
"TestConnection": "测试连接",
|
||||
"Type": "类型",
|
||||
"UnselectedAssets": "未选择资产或所选择的资产不支持SSH协议连接",
|
||||
"UnselectedNodes": "未选择节点",
|
||||
"UpdateAssetUserToken": "更新账号认证信息",
|
||||
"UpdateAssetUserToken": "更新资产用户认证信息",
|
||||
"Username": "用户名",
|
||||
"UsernameHelpMessage": "用户名是动态的,登录资产时使用当前用户的用户名登录",
|
||||
"Value": "值",
|
||||
@@ -206,7 +197,6 @@
|
||||
"sshKeyFingerprint": "SSH 指纹",
|
||||
"ip": "IP",
|
||||
"sshkey": "sshkey",
|
||||
"SSHKey": "SSH 密钥",
|
||||
"GroupsHelpMessage": "请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)",
|
||||
"HomeHelpMessage": "默认家目录 /home/系统用户名: /home/username",
|
||||
"Home": "家目录",
|
||||
@@ -230,8 +220,6 @@
|
||||
"ReLogin": "重新登录"
|
||||
},
|
||||
"common": {
|
||||
"MFAVerify": "验证 MFA",
|
||||
"ViewSecret": "查看密码",
|
||||
"ConnectWebSocketError": "连接 WebSocket 失败",
|
||||
"Action": "动作",
|
||||
"RequestTickets": "申请工单",
|
||||
@@ -250,9 +238,6 @@
|
||||
"bind": "绑定",
|
||||
"unbind": "解绑",
|
||||
"PushSelected":"推送所选",
|
||||
"PushSelectedSystemUsersToAsset": "推送所选系统用户到资产",
|
||||
"TestSelected": "测试所选",
|
||||
"TestSelectedSystemUsersConnective": "测试所选系统用户可连接性",
|
||||
"BadRequestErrorMsg": "请求错误,请检查填写内容",
|
||||
"BadRoleErrorMsg": "请求错误,无该操作权限",
|
||||
"BadConflictErrorMsg": "正在刷新中,请稍后再试",
|
||||
@@ -653,7 +638,6 @@
|
||||
"SystemUserDetail": "系统用户详情",
|
||||
"SystemUserList": "系统用户",
|
||||
"SystemUserUpdate": "更新系统用户",
|
||||
"AssetUserList": "资产用户",
|
||||
"TaskDetail": "任务详情",
|
||||
"TaskList": "任务列表",
|
||||
"TaskMonitor": "任务监控",
|
||||
@@ -679,10 +663,6 @@
|
||||
"SiteMessageList": "站内信"
|
||||
},
|
||||
"sessions": {
|
||||
"SetToDefaultStorage": "设置为默认存储",
|
||||
"SetToDefault": "设为默认",
|
||||
"SetSuccess": "设置成功",
|
||||
"SetFailed": "设置失败",
|
||||
"StorageConfiguration": "存储配置",
|
||||
"accountKey": "账户密钥",
|
||||
"accountName": "账户名称",
|
||||
@@ -894,8 +874,6 @@
|
||||
"DingTalk": "钉钉",
|
||||
"dingTalkTest": "测试",
|
||||
"weComTest": "测试",
|
||||
"FeiShu": "飞书",
|
||||
"feiShuTest": "测试",
|
||||
"setting": "设置"
|
||||
},
|
||||
"tickets": {
|
||||
@@ -986,7 +964,6 @@
|
||||
"Guide": "向导",
|
||||
"setWeCom": "设置企业微信认证",
|
||||
"setDingTalk": "设置钉钉认证",
|
||||
"setFeiShu": "设置飞书认证",
|
||||
"HelpText": {
|
||||
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "启用多因子认证,使账号更加安全。<br/> 启用之后您将会在下次登录时进入多因子认证绑定流程;您也可以在(个人信息->快速修改->更改多因子设置)中直接绑定!",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:设置复杂密码,并启用多因子认证)",
|
||||
@@ -1118,7 +1095,6 @@
|
||||
"NodeAmount": "节点数量",
|
||||
"PasswordLength": "密码长度",
|
||||
"PasswordStrategy": "密码策略",
|
||||
"SecretKeyStrategy": "密钥策略",
|
||||
"RegularlyPerform": "定期执行",
|
||||
"Result": "结果",
|
||||
"Retry": "重试",
|
||||
@@ -1129,11 +1105,8 @@
|
||||
"Username": "用户名"
|
||||
},
|
||||
"Cloud": {
|
||||
"IPNetworkSegment": "IP网段",
|
||||
"Aliyun": "阿里云",
|
||||
"Qcloud": "腾讯云",
|
||||
"QingyunPrivatecloud": "青云私有云",
|
||||
"HuaweiPrivatecloud": "华为私有云",
|
||||
"AWS_China": "AWS(中国)",
|
||||
"AWS_Int": "AWS(国际)",
|
||||
"HuaweiCloud": "华为云",
|
||||
@@ -1161,7 +1134,7 @@
|
||||
"Name": "名称",
|
||||
"Account":"账户",
|
||||
"Node": "节点",
|
||||
"AdminUser":"特权用户",
|
||||
"AdminUser":"管理用户",
|
||||
"Periodic":"执行周期",
|
||||
"PeriodicPerform":"定时执行",
|
||||
"RegularlyPerform": "定期执行",
|
||||
@@ -1223,7 +1196,7 @@
|
||||
"users_amount": "用户数量",
|
||||
"groups_amount": "用户组数量",
|
||||
"assets_amount": "资产数量",
|
||||
"admin_users_amount": "特权用户数量",
|
||||
"admin_users_amount": "管理用户数量",
|
||||
"system_users_amount": "系统用户数量",
|
||||
"applications_amount": "应用数量",
|
||||
"asset_perms_amount": "资产授权数量",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"": "",
|
||||
"accounts": {
|
||||
"PleaseClickLeftAssetToViewAssetAccount": "Asset account list, please click on the assets on the left to view",
|
||||
"PleaseClickLeftAssetToViewAssetAccount": "Asset account list, please click on the assets on the left to view",
|
||||
"PleaseClickLeftApplicationToViewApplicationAccount": "Application account list, please click on the application on the left to view",
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "Gathered user list, please click on the assets on the left to view"
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "Gathered user list, please click on the assets on the left to view"
|
||||
},
|
||||
"acl": {
|
||||
"name": "Name",
|
||||
@@ -28,7 +28,6 @@
|
||||
},
|
||||
"applications": {
|
||||
"": "",
|
||||
"updateAccountMsg": "Please update system user account info",
|
||||
"RemoteApp": "Remote app",
|
||||
"Database": "Database",
|
||||
"Cloud": "Cloud",
|
||||
@@ -83,21 +82,16 @@
|
||||
"DBInfo": "Database Info"
|
||||
},
|
||||
"assets": {
|
||||
"AssociateSystemUsers": "Associate system users",
|
||||
"AssociateAssets": "Associate assets",
|
||||
"AssociateNodes": "Associate nodes",
|
||||
"Action": "Action",
|
||||
"ActiveSelected": "Active selected",
|
||||
"AdminUser": "Admin user",
|
||||
"ReplaceNodeAssetsAdminUser":"Replace node assets admin user with this",
|
||||
"AdminUserDetail": "Admin user detail",
|
||||
"DynamicUsername": "Dynamic username",
|
||||
"AdminUserListHelpMessage": "Admin users are asset (charged server) on the root, or have NOPASSWD: ALL sudo permissions users, JumpServer users of the system using the user to `push system user`, `get assets hardware information`, etc.\n",
|
||||
"Asset": "Asset",
|
||||
"HardwareInfo": "Hardware info",
|
||||
"Hardware": "Hardware",
|
||||
"AccountList": "Account list",
|
||||
"LoginOption": "Login option",
|
||||
"AssetDetail": "Asset detail",
|
||||
"AssetList": "Asset list",
|
||||
"AssetListHelpMessage": "The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node\n",
|
||||
@@ -121,7 +115,6 @@
|
||||
"CommandFilterRules": "Command filter rules",
|
||||
"Comment": "Comment",
|
||||
"Cpu": "Cpu",
|
||||
"CommonUser": "Common user",
|
||||
"CreatedBy": "Created by",
|
||||
"Database": "Database",
|
||||
"DateJoined": "Date joined",
|
||||
@@ -165,7 +158,6 @@
|
||||
"PublicIp": "Public ip",
|
||||
"Push": "Push",
|
||||
"PushSystemUserNow": "Push system user now",
|
||||
"PushAllSystemUsersToAsset": "Push all system users to asset",
|
||||
"QuickUpdate": "Quick update",
|
||||
"Reachable": "Reachable",
|
||||
"Unreachable": "Unreachable",
|
||||
@@ -181,15 +173,14 @@
|
||||
"Rules": "Rules",
|
||||
"SFTPHelpMessage": "SFTP root dir, tmp, home or custom",
|
||||
"SerialNumber": "Serial number",
|
||||
"SudoHelpMessage": "Use comma split multi command, ex: /bin/whoami, /bin/ifconfig",
|
||||
"SudoHelpMessage": "Use comma split multi command, ex: /bin/whoami,/bin/ifconfig",
|
||||
"PasswordHelpMessage": "Password or private key password",
|
||||
"SystemUser": "System user",
|
||||
"SystemUserDetail": "System user detail",
|
||||
"SystemUserListHelpMessage": "<b>System user</b> is the account JumpServer used to log into the asset, such as using root `ssh root@host`, rather than the current user username(ssh admin@host)`;<br><b>Admin user</b> is the account that already exists on an asset, and have privileged permissions, JumpServer using this create common system user, and gather hardware Etc;</br><b>Common user</b> can pre-exist assets or created automatically by the admin user.",
|
||||
"SystemUserListHelpMessage": "System user is JumpServer jump login assets used by the users, can be understood as the user login assets, such as web, sa, the dba (` ssh web@some-host `), rather than using a user the username login server jump (` ssh xiaoming@some-host `); In simple terms, users log into JumpServer using their own username, and JumpServer uses system users to log into assets. When system users are created, if you choose auto push JumpServer to use Ansible push system users into the asset, if the asset (Switch) does not support ansible, please manually fill in the account password.\n",
|
||||
"SystemUsers": "System users",
|
||||
"Test": "Test",
|
||||
"TestAssetsConnective": "Test assets connective",
|
||||
"TestAllSystemUsersConnective": "Test all system users connective",
|
||||
"TestConnection": "Test connection",
|
||||
"Type": "Type",
|
||||
"UnselectedAssets": "No asset selected or the selected asset does not support SSH protocol connection",
|
||||
@@ -228,8 +219,6 @@
|
||||
"ReLogin": "Re-Login"
|
||||
},
|
||||
"common": {
|
||||
"MFAVerify": "Verify MFA",
|
||||
"ViewSecret": "View secret",
|
||||
"ConnectWebSocketError": "Connect Websocket failed",
|
||||
"Nothing": "Nothing",
|
||||
"Action": "Action",
|
||||
@@ -245,9 +234,6 @@
|
||||
"Add": "Add",
|
||||
"PleaseAgreeToTheTerms": "Please agree to the terms",
|
||||
"PushSelected":"Push selected",
|
||||
"PushSelectedSystemUsersToAsset": "Push selected system users to asset",
|
||||
"TestSelected": "Test selected",
|
||||
"TestSelectedSystemUsersConnective": "Test selected system users connective",
|
||||
"UpdateAssetDetail": "Update more detail",
|
||||
"AddSuccessMsg": "Add success",
|
||||
"Auth": "Authorization",
|
||||
@@ -264,7 +250,7 @@
|
||||
"Confirm": "Confirm",
|
||||
"Create": "Create",
|
||||
"CreatedBy": "Created by",
|
||||
"CrontabHelpTips": "eg: Every Sunday 03:05 run <5 3 * * 0> <br>Tips:Using 5 digits linux crontab expressions<min hour day month week> (<a href='https://tool.lu/crontab/' target='_blank'>Online tools</a>) <br>Note:If both Regularly perform and Cycle perform are set, give priority to Regularly perform",
|
||||
"CrontabHelpTips": "eg: Every Sunday 03:05 run <5 3 * * 0> <br>Tips:Using 5 digits linux crontab expressions<min hour day month week> (<a href='https://tool.lu/crontab/' target='_blank'>Online tools</a>) <br>Note:If both Regularly perform and Cycle perform are set,give priority to Regularly perform",
|
||||
"DateEnd": "End date",
|
||||
"Resource": "Resource",
|
||||
"DateLast24Hours": "Last 24 hours",
|
||||
@@ -306,7 +292,7 @@
|
||||
"RemoveSuccessMsg": "Remove success",
|
||||
"Reset": "Reset",
|
||||
"Search": "Search",
|
||||
"MFAErrorMsg": "MFA Error, please check",
|
||||
"MFAErrorMsg": "MFA Error,please check",
|
||||
"InputEmailAddress": "Please enter your email address",
|
||||
"Select": "Select",
|
||||
"SelectFile": "Select file",
|
||||
@@ -675,10 +661,6 @@
|
||||
"SiteMessageList": "Site message"
|
||||
},
|
||||
"sessions": {
|
||||
"SetToDefaultStorage": "Set to default storage",
|
||||
"SetToDefault": "Set to default",
|
||||
"SetSuccess": "Set success",
|
||||
"SetFailed": "Set failed",
|
||||
"StorageConfiguration": "Storage configuration",
|
||||
"accountKey": "Account key",
|
||||
"accountName": "Account name",
|
||||
@@ -743,7 +725,7 @@
|
||||
"sessionMonitor": "Session Monitor",
|
||||
"TerminateTaskSendSuccessMsg": "Terminate task has been send, Please check later",
|
||||
"helpText": {
|
||||
"esUrl": "Tip: If you have multiple hosts, use comma (, ) to split (eg: http://www.jumpserver.a.com, http://www.jumpserver.b.com)",
|
||||
"esUrl": "Tip: If you have multiple hosts, use comma (,) to split (eg: http://www.jumpserver.a.com,http://www.jumpserver.b.com)",
|
||||
"esIndex":"Es provides the default index: jumpserver",
|
||||
"esDocType": "Es provides the default document type: command",
|
||||
"s3Endpoint": "S3: http://s3.{REGION_NAME}.amazonaws.com<br>S3(China): http://s3.{REGION_NAME}.amazonaws.com.cn<br>Example: http://s3.cn-north-1.amazonaws.com.cn",
|
||||
@@ -810,7 +792,7 @@
|
||||
"ApiKeyList": "The API key is used to sign the request header. The header of each request is different. Please refer to the usage documentation",
|
||||
"authLdapSearchFilter": "Choice may be (cn|uid|sAMAccountName)=%(user)s)",
|
||||
"authLdapSearchOu": "Use | split User OUs",
|
||||
"authLdapUserAttrMap": "User attr map present how to map LDAP user attr to jumpserver, username, name, email is jumpserver attr",
|
||||
"authLdapUserAttrMap": "User attr map present how to map LDAP user attr to jumpserver, username,name,email is jumpserver attr",
|
||||
"emailCustomUserCreatedBody": "Tips:When creating a user, send the content of the email",
|
||||
"emailCustomUserCreatedHonorific": "Tips: When creating a user, send the honorific of the email (eg:Hello)",
|
||||
"emailCustomUserCreatedSignature": "Tips: Email signature (eg:jumpserver)",
|
||||
@@ -887,8 +869,6 @@
|
||||
"DingTalk": "DingTalk",
|
||||
"dingTalkTest": "Test",
|
||||
"weComTest": "Test",
|
||||
"FeiShu": "FeiShu",
|
||||
"feiShuTest": "Test",
|
||||
"setting": "Setting"
|
||||
},
|
||||
|
||||
@@ -969,7 +949,6 @@
|
||||
"DatePasswordLastUpdated": "Date password last updated",
|
||||
"setWeCom": "Set wecom login",
|
||||
"setDingTalk": "Set dingtalk login",
|
||||
"setFeiShu": "Set feishu login",
|
||||
"DatePasswordUpdated": "Date password updated",
|
||||
"DescribeOfGuide": "Welcome to JumpServer. Click here for more information",
|
||||
"Email": "Email",
|
||||
@@ -1120,11 +1099,8 @@
|
||||
"Username": "Username"
|
||||
},
|
||||
"Cloud": {
|
||||
"IPNetworkSegment": "Ip Network Segment",
|
||||
"Aliyun": "Ali Cloud",
|
||||
"Qcloud": "Tencent Cloud",
|
||||
"QingyunPrivatecloud": "Qingyun Private Cloud",
|
||||
"HuaweiPrivatecloud": "Huawei Private Cloud",
|
||||
"AWS_China": "AWS(China)",
|
||||
"AWS_Int": "AWS(International)",
|
||||
"HuaweiCloud": "Huawei Cloud",
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
</template>
|
||||
<script>
|
||||
import AutoDataForm from '@/components/AutoDataForm'
|
||||
import { getUpdateObjURL } from '@/utils/common'
|
||||
export default {
|
||||
name: 'GenericCreateUpdateForm',
|
||||
components: {
|
||||
@@ -130,31 +129,36 @@ export default {
|
||||
const params = this.$route.params
|
||||
let url = this.url
|
||||
if (params.id) {
|
||||
url = getUpdateObjURL(url, params.id)
|
||||
url = `${url}${params.id}/`
|
||||
}
|
||||
return url
|
||||
}
|
||||
},
|
||||
emitPerformSuccessMsg: {
|
||||
onPerformSuccess: {
|
||||
type: Function,
|
||||
default(method, res, addContinue) {
|
||||
default(res, method, vm, addContinue) {
|
||||
let msg = method === 'post' ? this.createSuccessMsg : this.updateSuccessMsg
|
||||
if (addContinue) {
|
||||
msg = this.saveSuccessContinueMsg
|
||||
}
|
||||
let msgLinkName = this.$tc('common.Resource')
|
||||
let msgLinkName = this.$t('common.Resource')
|
||||
if (res.name) {
|
||||
msgLinkName = res.name
|
||||
} else if (res.hostname) {
|
||||
msgLinkName = res.hostname
|
||||
}
|
||||
const detailRoute = this.objectDetailRoute
|
||||
detailRoute['params'] = { 'id': res.id }
|
||||
const route = this.getNextRoute(res, method)
|
||||
this.$emit('submitSuccess', res)
|
||||
const h = this.$createElement
|
||||
this.$log.debug('router is: ', detailRoute)
|
||||
if (this.hasDetailInMsg) {
|
||||
this.$message({
|
||||
message: h('p', null, [
|
||||
h('el-link', {
|
||||
on: {
|
||||
click: () => this.$router.push(this.objectDetailRoute)
|
||||
click: () => this.$router.push(detailRoute)
|
||||
},
|
||||
style: { 'vertical-align': 'top' }
|
||||
}, msgLinkName),
|
||||
@@ -170,18 +174,6 @@ export default {
|
||||
} else {
|
||||
this.$message.success(msg)
|
||||
}
|
||||
}
|
||||
},
|
||||
onPerformSuccess: {
|
||||
type: Function,
|
||||
default(res, method, vm, addContinue) {
|
||||
const route = this.getNextRoute(res, method)
|
||||
if (!(route.params && route.params.id)) {
|
||||
route['params'] = { 'id': res.id }
|
||||
}
|
||||
this.$emit('submitSuccess', res)
|
||||
|
||||
this.emitPerformSuccessMsg(method, res, addContinue)
|
||||
if (!addContinue) {
|
||||
setTimeout(() => this.$router.push(route), 100)
|
||||
}
|
||||
@@ -226,7 +218,6 @@ export default {
|
||||
return this.getMethod(this)
|
||||
},
|
||||
iUrl() {
|
||||
// 更新或创建的url
|
||||
return this.getUrl()
|
||||
},
|
||||
iHasSaveContinue() {
|
||||
@@ -274,7 +265,7 @@ export default {
|
||||
return Object.assign(this.form, this.initial)
|
||||
}
|
||||
let object = this.object
|
||||
if (!object || Object.keys(object).length === 0) {
|
||||
if (!object) {
|
||||
if (cloneFrom) {
|
||||
this.$log.debug('Clone from: ', cloneFrom)
|
||||
const url = `${this.url}${cloneFrom}/`
|
||||
|
||||
@@ -175,12 +175,8 @@ export default {
|
||||
},
|
||||
defaultUpdate() {
|
||||
const id = this.$route.params.id
|
||||
let route = this.validActions.updateRoute
|
||||
if (typeof route === 'string') {
|
||||
route = { name: route, params: {}}
|
||||
}
|
||||
route.params.id = id
|
||||
this.$router.push(route)
|
||||
const routeName = this.validActions.updateRoute
|
||||
this.$router.push({ name: routeName, params: { id: id }})
|
||||
},
|
||||
getObject() {
|
||||
const url = this.validActions.detailApiUrl
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ListTable ref="ListTable" v-bind="$attrs" v-on="$listeners" />
|
||||
<ListTable ref="ListTable" v-bind="iAttrs" v-on="$listeners" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -11,15 +11,16 @@ export default {
|
||||
ListTable
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentOrgIsRoot'])
|
||||
},
|
||||
created() {
|
||||
const headerActions = this.$attrs['header-actions'] || {}
|
||||
if (headerActions.canCreate === undefined && this.currentOrgIsRoot) {
|
||||
_.set(this.$attrs, 'header-actions.canCreate', false)
|
||||
}
|
||||
if (headerActions.hasImport === undefined && this.currentOrgIsRoot) {
|
||||
_.set(this.$attrs, 'header-actions.hasImport', false)
|
||||
...mapGetters(['currentOrgIsRoot']),
|
||||
iAttrs() {
|
||||
const attrs = _.cloneDeep(this.$attrs)
|
||||
const canCreate = _.get(attrs, 'header-actions.canCreate', null)
|
||||
this.$log.debug('Can create: ', canCreate)
|
||||
if (canCreate === null && this.currentOrgIsRoot) {
|
||||
_.set(attrs, 'header-actions.canCreate', false)
|
||||
}
|
||||
this.$log.debug('List table Attrs: ', attrs)
|
||||
return attrs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<template>
|
||||
<Page v-bind="$attrs">
|
||||
<Page>
|
||||
<el-alert v-if="helpMessage" type="success"> {{ helpMessage }} </el-alert>
|
||||
<TreeTable
|
||||
ref="TreeTable"
|
||||
v-bind="$attrs"
|
||||
:table-config="tableConfig"
|
||||
:header-actions="iHeaderActions"
|
||||
:tree-setting="treeSetting"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<template #table>
|
||||
@@ -24,16 +27,23 @@ export default {
|
||||
components: {
|
||||
Page, TreeTable
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentOrgIsRoot'])
|
||||
},
|
||||
created() {
|
||||
const headerActions = this.$attrs['header-actions'] || {}
|
||||
if (headerActions.canCreate === undefined && this.currentOrgIsRoot) {
|
||||
_.set(this.$attrs, 'header-actions.canCreate', false)
|
||||
props: {
|
||||
...TreeTable.props,
|
||||
helpMessage: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
if (headerActions.hasImport === undefined && this.currentOrgIsRoot) {
|
||||
_.set(this.$attrs, 'header-actions.hasImport', false)
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentOrg']),
|
||||
iHeaderActions() {
|
||||
const attrs = _.cloneDeep(this.headerActions)
|
||||
const canCreate = _.get(attrs, 'canCreate', null)
|
||||
// this.$log.debug('Current org: ', this.currentOrg)
|
||||
if (canCreate === null && this.currentOrg && this.currentOrg.is_root) {
|
||||
_.set(attrs, 'canCreate', false)
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<el-option
|
||||
v-for="item in userAdminOrgList"
|
||||
:key="item.id"
|
||||
:selected="item.id === currentOrg.id"
|
||||
selected="item.id == currentOrg.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
@@ -48,8 +48,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
needShow() {
|
||||
const hasValidLicense = this.$store.getters.hasValidLicense
|
||||
return !this.isCollapse && this.inAdminPage && hasValidLicense
|
||||
const otherOrgs = this.userAdminOrgList.filter(org => {
|
||||
return !org.is_root && !org.is_default
|
||||
})
|
||||
return !this.isCollapse && otherOrgs.length > 0 && this.inAdminPage
|
||||
},
|
||||
changeOrg(orgId) {
|
||||
orgUtil.changeOrg(orgId)
|
||||
|
||||
@@ -82,6 +82,7 @@ export default {
|
||||
case 'userPage':
|
||||
if (this.currentOrgUsePagePerm) {
|
||||
this.$store.dispatch('users/setCurrentRole', rolec.USER)
|
||||
// console.log('Switch to: ', rolec.USER)
|
||||
window.location.href = `/ui/`
|
||||
}
|
||||
break
|
||||
@@ -95,6 +96,11 @@ export default {
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
// Clean Status
|
||||
const statusList = ['currentOrg', 'currentRole', 'jms_current_org', 'jms_current_role', 'sidebarStatus', 'X-JMS-ORG', 'activeTab']
|
||||
for (const i in statusList) {
|
||||
this.$cookie.delete(statusList[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ export { default as GenericDetailPage } from './GenericDetailPage'
|
||||
export { default as TabPage } from './TabPage'
|
||||
export { default as Footer } from './Footer'
|
||||
export { default as GenericListPage } from './GenericListPage'
|
||||
export { default as GenericListTable } from './GenericListTable'
|
||||
export { default as GenericCreateUpdatePage } from './GenericCreateUpdatePage'
|
||||
export { default as GenericCreateUpdateForm } from './GenericCreateUpdateForm'
|
||||
export { default as GenericUpdateFormDialog } from './GenericUpdateFormDialog'
|
||||
|
||||
@@ -38,73 +38,57 @@ export default [
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'databases',
|
||||
path: 'database-apps',
|
||||
name: 'DatabaseAppList',
|
||||
component: empty,
|
||||
meta: { title: i18n.t('route.DatabaseApp') },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'DatabaseAppList',
|
||||
component: () => import('@/views/applications/DatabaseApp/DatabaseAppList'),
|
||||
meta: { title: i18n.t('route.DatabaseApp') }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
name: 'DatabaseAppCreate',
|
||||
component: () => import('@/views/applications/DatabaseApp/DatabaseAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.DatabaseAppCreate'), activeMenu: '/applications/databases', action: 'create' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
name: 'DatabaseAppUpdate',
|
||||
component: () => import('@/views/applications/DatabaseApp/DatabaseAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.DatabaseAppUpdate'), activeMenu: '/applications/databases', action: 'update' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
name: 'DatabaseAppDetail',
|
||||
component: () => import('@/views/applications/DatabaseApp/DatabaseAppDetail/index'),
|
||||
meta: { title: i18n.t('route.DatabaseAppDetail'), activeMenu: '/applications/databases' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
component: () => import('@/views/applications/DatabaseApp/DatabaseAppList'),
|
||||
meta: { title: i18n.t('route.DatabaseApp') }
|
||||
},
|
||||
{
|
||||
path: 'kubernetes',
|
||||
path: 'database-apps/create',
|
||||
name: 'DatabaseAppCreate',
|
||||
component: () => import('@/views/applications/DatabaseApp/DatabaseAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.DatabaseAppCreate'), activeMenu: '/applications/database-apps', action: 'create' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'database-apps/:id/update',
|
||||
name: 'DatabaseAppUpdate',
|
||||
component: () => import('@/views/applications/DatabaseApp/DatabaseAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.DatabaseAppUpdate'), activeMenu: '/applications/database-apps', action: 'update' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'database-apps/:id',
|
||||
name: 'DatabaseAppDetail',
|
||||
component: () => import('@/views/applications/DatabaseApp/DatabaseAppDetail/index'),
|
||||
meta: { title: i18n.t('route.DatabaseAppDetail'), activeMenu: '/applications/database-apps' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'kubernetes-apps',
|
||||
name: 'KubernetesAppList',
|
||||
component: empty,
|
||||
meta: { title: i18n.t('route.KubernetesApp') },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'KubernetesAppList',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppList'),
|
||||
meta: { title: i18n.t('route.KubernetesApp') }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
name: 'KubernetesAppCreate',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.KubernetesAppCreate'), activeMenu: '/applications/kubernetes', action: 'create' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
name: 'KubernetesAppUpdate',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.KubernetesAppUpdate'), activeMenu: '/applications/kubernetes', action: 'update' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
name: 'KubernetesAppDetail',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppDetail/index'),
|
||||
meta: { title: i18n.t('route.KubernetesAppDetail'), activeMenu: '/applications/kubernetes' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppList'),
|
||||
meta: { title: i18n.t('route.KubernetesApp') }
|
||||
},
|
||||
{
|
||||
path: 'kubernetes-apps/create',
|
||||
name: 'KubernetesAppCreate',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.KubernetesAppCreate'), activeMenu: '/applications/kubernetes-apps', action: 'create' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'kubernetes-apps/:id/update',
|
||||
name: 'KubernetesAppUpdate',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppCreateUpdate'),
|
||||
meta: { title: i18n.t('route.KubernetesAppUpdate'), activeMenu: '/applications/kubernetes-apps', action: 'update' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'kubernetes-apps/:id',
|
||||
name: 'KubernetesAppDetail',
|
||||
component: () => import('@/views/applications/KubernetesApp/KubernetesAppDetail/index'),
|
||||
meta: { title: i18n.t('route.KubernetesAppDetail'), activeMenu: '/applications/kubernetes-apps' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -101,6 +101,41 @@ export default [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'admin-users',
|
||||
component: empty,
|
||||
redirect: '',
|
||||
meta: { title: i18n.t('route.AdminUserList') },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'AdminUserList',
|
||||
component: () => import('@/views/assets/AdminUser/AdminUserList'),
|
||||
meta: { title: i18n.t('route.AdminUserList'), activeMenu: '/assets/admin-users' }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/assets/AdminUser/AdminUserCreateUpdate.vue'), // Parent router-view
|
||||
name: 'AdminUserCreate',
|
||||
meta: { title: i18n.t('route.AdminUserCreate'), activeMenu: '/assets/admin-users' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/assets/AdminUser/AdminUserCreateUpdate.vue'), // Parent router-view
|
||||
name: 'AdminUserUpdate',
|
||||
meta: { title: i18n.t('route.AdminUserUpdate'), activeMenu: '/assets/admin-users' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: () => import('@/views/assets/AdminUser/AdminUserDetail/index.vue'), // Parent router-view
|
||||
name: 'AdminUserDetail',
|
||||
meta: { title: i18n.t('route.AdminUserDetail'), activeMenu: '/assets/admin-users' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'system-users',
|
||||
component: empty,
|
||||
@@ -110,20 +145,20 @@ export default [
|
||||
{
|
||||
path: '',
|
||||
name: 'SystemUserList',
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserList/index.vue'),
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserList.vue'),
|
||||
meta: { title: i18n.t('route.SystemUserList'), activeMenu: '/assets/system-users' }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
name: 'SystemUserCreate',
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserCreateUpdate/index.vue'),
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserCreateUpdate.vue'),
|
||||
meta: { title: i18n.t('route.SystemUserCreate'), activeMenu: '/assets/system-users' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
name: 'SystemUserUpdate',
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserCreateUpdate/index.vue'),
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserCreateUpdate.vue'),
|
||||
meta: { title: i18n.t('route.SystemUserUpdate'), activeMenu: '/assets/system-users' },
|
||||
hidden: true
|
||||
},
|
||||
|
||||
@@ -3,8 +3,10 @@ const getters = {
|
||||
device: state => state.app.device,
|
||||
token: state => state.users.token,
|
||||
currentOrg: state => state.users.currentOrg,
|
||||
currentOrgIsDefault: state => state.users.currentOrg['is_default'],
|
||||
currentOrgIsRoot: state => state.users.currentOrg['is_root'],
|
||||
currentOrgIsDefault: state => state.users.currentOrg.is_default,
|
||||
currentOrgIsRoot: state => {
|
||||
return state.users.currentOrg && state.users.currentOrg.is_root
|
||||
},
|
||||
currentRole: state => state.users.currentRole,
|
||||
currentUser: state => state.users.profile,
|
||||
permission_routes: state => state.permission.routes,
|
||||
@@ -14,15 +16,16 @@ const getters = {
|
||||
currentOrgRoles: state => state.users.roles,
|
||||
currentOrgPerms: state => state.users.perms,
|
||||
MFAVerifyAt: state => state.users.MFAVerifyAt,
|
||||
MFA_TTl: state => state.settings.publicSettings['SECURITY_MFA_VERIFY_TTL'],
|
||||
MFA_TTl: state => state.settings.publicSettings.SECURITY_MFA_VERIFY_TTL,
|
||||
tableConfig: state => state.table.tableConfig,
|
||||
currentUserIsSuperAdmin: state => state.users.isSuperAdmin,
|
||||
currentUserIsAdmin: state => state.users.isAdmin,
|
||||
currentUserIsSuperAdmin: state => {
|
||||
return state.users.sysRole === 'Admin'
|
||||
},
|
||||
hasValidLicense: state => state.settings.hasValidLicense,
|
||||
userAdminOrgList: (state, getters) => {
|
||||
let orgs = state.users.orgs
|
||||
if (!getters.hasValidLicense) {
|
||||
orgs = orgs.filter(org => !org['is_root'])
|
||||
orgs = orgs.filter(org => !org.is_root)
|
||||
}
|
||||
return orgs
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ const actions = {
|
||||
getPublicSettings({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getPublicSettings().then(response => {
|
||||
const faviconURL = response.data['LOGO_URLS'].favicon
|
||||
const faviconURL = response.data.LOGO_URLS.favicon
|
||||
let link = document.querySelector("link[rel*='icon']")
|
||||
if (!link) {
|
||||
link = document.createElement('link')
|
||||
@@ -46,7 +46,7 @@ const actions = {
|
||||
link.href = faviconURL
|
||||
|
||||
// 动态修改Title
|
||||
document.title = response.data['LOGIN_TITLE']
|
||||
document.title = response.data.LOGIN_TITLE
|
||||
commit('SET_PUBLIC_SETTINGS', response.data)
|
||||
resolve(response)
|
||||
}).catch(error => {
|
||||
|
||||
@@ -1,28 +1,24 @@
|
||||
|
||||
import Vue from 'vue'
|
||||
|
||||
function getTableConfigFromLocal() {
|
||||
const configs = localStorage.getItem('tableConfig')
|
||||
try {
|
||||
return JSON.parse(configs)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
function getTableConfigfromCookie() {
|
||||
return localStorage.getItem('tableConfig') ? JSON.parse(localStorage.getItem('tableConfig')) : {}
|
||||
}
|
||||
|
||||
const state = {
|
||||
tableConfig: getTableConfigFromLocal()
|
||||
tableConfig: getTableConfigfromCookie()
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_TABLE_CONFIG: (state, item) => {
|
||||
const _tableConfig = getTableConfigFromLocal()
|
||||
Vue.set(_tableConfig, item.key, item.value)
|
||||
SET_TABLE_CONFIG: (state, tableConfig) => {
|
||||
const _tableConfig = localStorage.getItem('tableConfig') ? JSON.parse(localStorage.getItem('tableConfig')) : {}
|
||||
Vue.set(_tableConfig, tableConfig.key, tableConfig.value)
|
||||
localStorage.setItem('tableConfig', JSON.stringify(_tableConfig))
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { logout, getProfile } from '@/api/users'
|
||||
import {
|
||||
getCurrentOrgLocal,
|
||||
getCurrentRoleLocal,
|
||||
getTokenFromCookie,
|
||||
saveCurrentOrgLocal,
|
||||
saveCurrentRoleLocal
|
||||
getCurrentOrgFromCookie,
|
||||
saveCurrentOrgToCookie,
|
||||
getCurrentRoleFromCookie,
|
||||
saveCurrentRoleToCookie
|
||||
} from '@/utils/auth'
|
||||
import { resetRouter } from '@/router'
|
||||
import rolec from '@/utils/role'
|
||||
@@ -12,19 +12,15 @@ import rolec from '@/utils/role'
|
||||
const getDefaultState = () => {
|
||||
return {
|
||||
token: getTokenFromCookie(),
|
||||
currentOrg: '',
|
||||
currentRole: '',
|
||||
currentOrg: getCurrentOrgFromCookie(),
|
||||
currentRole: getCurrentRoleFromCookie(),
|
||||
profile: {},
|
||||
username: '',
|
||||
roles: {},
|
||||
sysRole: '',
|
||||
orgs: [],
|
||||
perms: 0b00000000,
|
||||
MFAVerifyAt: null,
|
||||
isSuperAdmin: false,
|
||||
isAdmin: false,
|
||||
hasAdminPerm: false,
|
||||
hasAuditPerm: false
|
||||
isSuperAdmin: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +35,6 @@ const mutations = {
|
||||
},
|
||||
SET_PROFILE: (state, profile) => {
|
||||
state.profile = profile
|
||||
const username = profile.username
|
||||
state.username = username
|
||||
state.currentOrg = getCurrentOrgLocal(username)
|
||||
state.currentRole = getCurrentRoleLocal(username)
|
||||
state.isAdmin = profile['is_org_admin']
|
||||
state.isSuperAdmin = profile['is_superuser']
|
||||
},
|
||||
SET_ORGS: (state, orgs) => {
|
||||
state.orgs = orgs
|
||||
@@ -63,23 +53,20 @@ const mutations = {
|
||||
},
|
||||
SET_ROLES(state, roles) {
|
||||
state.roles = roles
|
||||
// rolec.PERM_ADMIN &
|
||||
},
|
||||
SET_SYS_ROLE(state, role) {
|
||||
state.sysRole = role
|
||||
},
|
||||
SET_PERMS(state, perms) {
|
||||
state.perms = perms
|
||||
state.hasAdmin = (perms & rolec.PERM_ADMIN) === rolec.PERM_ADMIN
|
||||
state.hasAudit = (perms & rolec.PERM_AUDIT) === rolec.PERM_AUDIT
|
||||
},
|
||||
SET_CURRENT_ORG(state, org) {
|
||||
saveCurrentOrgToCookie(org)
|
||||
state.currentOrg = org
|
||||
saveCurrentOrgLocal(state.username, org)
|
||||
},
|
||||
SET_CURRENT_ROLE(state, role) {
|
||||
saveCurrentRoleToCookie(role)
|
||||
state.currentRole = role
|
||||
saveCurrentRoleLocal(state.username, role)
|
||||
},
|
||||
SET_MFA_VERIFY(state) {
|
||||
state.MFAVerifyAt = (new Date()).valueOf()
|
||||
|
||||
@@ -323,31 +323,6 @@ website: http://code.google.com/p/jquerytree/
|
||||
display: inline-block;
|
||||
color: #676a6c;
|
||||
}
|
||||
.ztree li span.button.chrome_ico_docu::before {
|
||||
content: "\f268";
|
||||
font-family: FontAwesome;
|
||||
padding-top: 10px;
|
||||
padding-left: 2px;
|
||||
display: inline-block;
|
||||
color: #676a6c;
|
||||
}
|
||||
.ztree li span.button.database_ico_docu::before {
|
||||
content: "\f1c0";
|
||||
font-family: FontAwesome;
|
||||
padding-top: 10px;
|
||||
padding-left: 2px;
|
||||
display: inline-block;
|
||||
color: #676a6c;
|
||||
}
|
||||
.ztree li span.button.cloud_ico_docu::before {
|
||||
content: "\f0c2";
|
||||
font-family: FontAwesome;
|
||||
padding-top: 10px;
|
||||
padding-left: 2px;
|
||||
display: inline-block;
|
||||
color: #676a6c;
|
||||
}
|
||||
|
||||
.ztree li span.button.windows_ico_docu::before {
|
||||
content: "\f17a";
|
||||
font-family: FontAwesome;
|
||||
|
||||
@@ -24,7 +24,7 @@ export default {
|
||||
refresh: () => {},
|
||||
onSelected: function(event, treeNode) {
|
||||
if (treeNode.meta.type === 'node') {
|
||||
const currentNodeId = treeNode.meta.data.id
|
||||
const currentNodeId = treeNode.meta.node.id
|
||||
this.tableConfig.url = `/api/v1/perms/users/nodes/${currentNodeId}/assets/?cache_policy=1`
|
||||
}
|
||||
}.bind(this)
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
<template>
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm v-bind="$data" />
|
||||
<GenericCreateUpdateForm
|
||||
:fields="fields"
|
||||
:fields-meta="fieldsMeta"
|
||||
:initial="object"
|
||||
:url="url"
|
||||
:update-success-next-route="updateSuccessNextRoute"
|
||||
:clean-form-value="cleanFormValue"
|
||||
:get-method="getMethod"
|
||||
:on-perform-success="onPerformSuccess"
|
||||
:perform-submit="performSubmit"
|
||||
/>
|
||||
</IBox>
|
||||
</template>
|
||||
|
||||
@@ -72,24 +82,24 @@ export default {
|
||||
delete value['mfa_level']
|
||||
}
|
||||
return value
|
||||
},
|
||||
performSubmit(validValues) {
|
||||
if (!validValues.terms) {
|
||||
this.$message.error(this.$t('common.PleaseAgreeToTheTerms'))
|
||||
return Promise.reject()
|
||||
}
|
||||
return this.$axios['put'](this.url, validValues)
|
||||
},
|
||||
onPerformSuccess() {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
setTimeout(() => this.$router.push({ name: 'UserGuide' }), 100)
|
||||
},
|
||||
getMethod() {
|
||||
return 'put'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMethod() {
|
||||
return 'put'
|
||||
},
|
||||
performSubmit(validValues) {
|
||||
if (!validValues.terms) {
|
||||
this.$message.error(this.$t('common.PleaseAgreeToTheTerms'))
|
||||
return Promise.reject()
|
||||
}
|
||||
return this.$axios['put'](this.url, validValues)
|
||||
},
|
||||
onPerformSuccess() {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
setTimeout(() => this.$router.push({ name: 'UserGuide' }), 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -88,21 +88,6 @@ export default {
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('users.setFeiShu'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$store.state.users.profile.is_feishu_bound ? this.$t('common.unbind') : this.$t('common.bind'),
|
||||
disabled: this.$store.state.users.profile.source !== 'local'
|
||||
},
|
||||
has: this.$store.getters.publicSettings.AUTH_FEISHU,
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.currentEdit = 'feishu'
|
||||
this.showPasswordDialog = true
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('users.SetMFA'),
|
||||
attrs: {
|
||||
|
||||
@@ -8,32 +8,34 @@ export function getTokenFromCookie() {
|
||||
return VueCookie.get(TOKEN_KEY)
|
||||
}
|
||||
|
||||
export function getCurrentRoleLocal(username) {
|
||||
const key = CURRENT_ROLE_KEY + '_' + username
|
||||
const role = localStorage.getItem(key)
|
||||
export function getCurrentRoleFromCookie() {
|
||||
const role = VueCookie.get(CURRENT_ROLE_KEY)
|
||||
if (role) {
|
||||
return parseInt(role) || null
|
||||
}
|
||||
return role
|
||||
}
|
||||
|
||||
export function saveCurrentRoleLocal(username, role) {
|
||||
const key = CURRENT_ROLE_KEY + '_' + username
|
||||
return localStorage.setItem(key, role)
|
||||
export function saveCurrentRoleToCookie(role) {
|
||||
// console.log('Save current role to cookie: ', role)
|
||||
return VueCookie.set(CURRENT_ROLE_KEY, role, 14)
|
||||
}
|
||||
|
||||
export function getCurrentOrgLocal(username) {
|
||||
const key = CURRENT_ORG_KEY + '_' + username
|
||||
const value = localStorage.getItem(key)
|
||||
export function getCurrentOrgFromCookie() {
|
||||
let org = null
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
org = JSON.parse(VueCookie.get(CURRENT_ORG_KEY))
|
||||
} catch (e) {
|
||||
return null
|
||||
// console.log('Current org in cookie: ', org)
|
||||
}
|
||||
return org
|
||||
}
|
||||
|
||||
export function saveCurrentOrgLocal(username, org) {
|
||||
const key = CURRENT_ORG_KEY + '_' + username
|
||||
localStorage.setItem(key, JSON.stringify(org))
|
||||
export function saveCurrentOrgToCookie(org) {
|
||||
VueCookie.set(CURRENT_ORG_KEY, JSON.stringify(org), 14)
|
||||
VueCookie.set('X-JMS-ORG', org.id)
|
||||
}
|
||||
|
||||
export function removeCurrentOrg() {
|
||||
return VueCookie.remove(CURRENT_ORG_KEY)
|
||||
}
|
||||
|
||||
@@ -214,22 +214,11 @@ export function newURL(url) {
|
||||
if (url.indexOf('//') > -1) {
|
||||
obj = new URL(url)
|
||||
} else {
|
||||
obj = new URL(url, location.origin)
|
||||
obj = new URL(url, 'http://localhost')
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
export function getUpdateObjURL(url, objId) {
|
||||
const urlObj = new URL(url, location.origin)
|
||||
let pathname = urlObj.pathname
|
||||
if (!pathname.endsWith('/')) {
|
||||
pathname += '/'
|
||||
}
|
||||
pathname += `${objId}/`
|
||||
urlObj.pathname = pathname
|
||||
return urlObj.href
|
||||
}
|
||||
|
||||
export const assignIfNot = _.partialRight(_.assignInWith, customizer)
|
||||
|
||||
const scheme = document.location.protocol
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export function openTaskPage(taskId) {
|
||||
window.open(`/#/ops/celery/task/${taskId}/log/`, '', 'width=900,height=600')
|
||||
}
|
||||
@@ -35,7 +35,6 @@ async function checkLogin({ to, from, next }) {
|
||||
try {
|
||||
return await store.dispatch('users/getProfile')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
const status = e.response.status
|
||||
if (status === 401 || status === 403) {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -1,109 +1,401 @@
|
||||
<template>
|
||||
<GenericTreeListPage ref="TreeTablePage" :tree-setting="treeSetting">
|
||||
<template #table>
|
||||
<AppAccountListTable ref="table" :url="accountsUrl" />
|
||||
</template>
|
||||
</GenericTreeListPage>
|
||||
<Page>
|
||||
<el-row>
|
||||
<el-col :span="11">
|
||||
<GenericListTable ref="LeftTable" class="application-table" :header-actions="leftTable.headerActions" :table-config="leftTable.tableConfig" @row-click="leftTable.tableConfig.rowClick" />
|
||||
</el-col>
|
||||
<el-col :span="13">
|
||||
<GenericListTable v-if="!isInit" ref="RightTable" class="application-user-table" :header-actions="rightTable.headerActions" :table-config="rightTable.tableConfig" />
|
||||
<div v-else class="noDataR">
|
||||
<div class="hintWrap">
|
||||
<div>{{ $t('accounts.PleaseClickLeftApplicationToViewApplicationAccount') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<Dialog v-if="showMFADialog" width="50" :title="this.$t('common.MFAConfirm')" :visible.sync="showMFADialog" :show-confirm="false" :show-cancel="false" :destroy-on-close="true">
|
||||
<div v-if="MFAConfirmed">
|
||||
<el-form label-position="right" label-width="80px" :model="MFAInfo">
|
||||
<el-form-item :label="this.$t('assets.Applications')">
|
||||
<el-input v-model="MFAInfo.application" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="MFAInfo.username" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.PasswordOrToken')">
|
||||
<el-input v-model="MFAInfo.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-row v-else :gutter="20">
|
||||
<el-col :span="4">
|
||||
<div style="line-height: 34px;text-align: center">MFA</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-input v-model="MFAInput" />
|
||||
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button size="mini" type="primary" style="line-height:20px " @click="MFAConfirm">{{ this.$t('common.Confirm') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
<Dialog :title="$t('common.Export')" :visible.sync="showExportDialog" :destroy-on-close="true" @confirm="handleExportConfirm()" @cancel="handleExportCancel()">
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportOption">
|
||||
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericTreeListPage from '@/layout/components/GenericTreeListPage'
|
||||
import AppAccountListTable from '@/components/AppAccountListTable'
|
||||
import { setUrlParam } from '@/utils/common'
|
||||
import Page from '@/layout/components/Page'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import { ActionsFormatter, DetailFormatter } from '@/components/TableFormatters'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import * as queryUtil from '@/components/DataTable/compenents/el-data-table/utils/query'
|
||||
|
||||
export default {
|
||||
name: 'AssetAccountList',
|
||||
components: {
|
||||
GenericTreeListPage, AppAccountListTable
|
||||
GenericListTable, Page, Dialog
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
isInit: true,
|
||||
clickedRow: null,
|
||||
iShowTree: true,
|
||||
accountsUrl: '/api/v1/applications/accounts/',
|
||||
treeSetting: {
|
||||
async: false,
|
||||
showMenu: false,
|
||||
showRefresh: true,
|
||||
showAssets: false,
|
||||
treeUrl: '/api/v1/applications/applications/tree/',
|
||||
callback: {
|
||||
onSelected: function(event, treeNode) {
|
||||
let url = '/api/v1/applications/accounts/'
|
||||
const nodeId = treeNode.id
|
||||
const value = treeNode.meta.data?.value
|
||||
if (treeNode.meta.type === 'category') {
|
||||
url = setUrlParam(url, 'category', value)
|
||||
url = setUrlParam(url, 'type', '')
|
||||
} else if (treeNode.meta.type === 'type') {
|
||||
url = setUrlParam(url, 'category', '')
|
||||
url = setUrlParam(url, 'type', value)
|
||||
} else if (treeNode.meta.type === 'application') {
|
||||
url = setUrlParam(url, 'category', '')
|
||||
url = setUrlParam(url, 'type', '')
|
||||
url = setUrlParam(url, 'app', nodeId)
|
||||
showMFADialog: false,
|
||||
MFAConfirmed: false,
|
||||
MFAInput: '',
|
||||
MFAInfo: {
|
||||
systemUser: '',
|
||||
application: '',
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
selectedRows: '',
|
||||
showExportDialog: false,
|
||||
dialogStatus: '',
|
||||
exportOption: 'all',
|
||||
exportTypeOption: 'csv',
|
||||
clickedRow: {},
|
||||
leftTable: {
|
||||
tableConfig: {
|
||||
url: '/api/v1/applications/applications/',
|
||||
columns: [
|
||||
'name', 'category_display', 'type_display', 'comment', 'org_name'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name'],
|
||||
default: ['name', 'category_display', 'type_display']
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
getRoute({ row, col, cellValue }) {
|
||||
return {
|
||||
'db': 'DatabaseAppDetail', 'remote_app': 'RemoteAppDetail', 'cloud': 'KubernetesAppDetail'
|
||||
}[row.category]
|
||||
}
|
||||
},
|
||||
showOverflowTooltip: true,
|
||||
sortable: false
|
||||
}
|
||||
},
|
||||
tableAttrs: {
|
||||
stripe: false, // 斑马纹表格
|
||||
border: true, // 表格边框
|
||||
fit: true, // 宽度自适应,
|
||||
tooltipEffect: 'dark',
|
||||
rowClassName({ row, rowIndex }) {
|
||||
if (row === vm.clickedRow) {
|
||||
return 'row-clicked'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
rowClick: function(row, column, event) {
|
||||
vm.rightTable.tableConfig.url = `/api/v1/applications/application-users/?application_id=${row.id}`
|
||||
vm.rightTable.tableConfig.extraQuery.application_id = row.id
|
||||
vm.clickedRow = row
|
||||
vm.MFAInfo.application = row.name
|
||||
vm.isInit = false
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasCreate: false,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasBulkDelete: false,
|
||||
hasBulkUpdate: false
|
||||
}
|
||||
},
|
||||
rightTable: {
|
||||
tableConfig: {
|
||||
url: `/api/v1/applications/application-users/?application_id=`,
|
||||
extraQuery: {
|
||||
application_id: ''
|
||||
},
|
||||
columns: [
|
||||
'name', 'username', 'username_same_with_user', 'protocol', 'login_mode', 'priority', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name', 'username', 'actions'],
|
||||
default: ['name', 'username', 'date_created', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
route: 'SystemUserDetail'
|
||||
},
|
||||
showOverflowTooltip: true,
|
||||
sortable: false
|
||||
},
|
||||
protocol: {
|
||||
sortable: false
|
||||
},
|
||||
login_mode: {
|
||||
sortable: false
|
||||
},
|
||||
actions: {
|
||||
label: this.$t('common.Action'),
|
||||
align: 'center',
|
||||
width: 150,
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
hasClone: false,
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: function(val) {
|
||||
this.dialogStatus = 'viewAuthInfo'
|
||||
this.MFAInfo.systemUser = val.row
|
||||
if (!this.needMFAVerify) {
|
||||
this.showMFADialog = true
|
||||
this.MFAConfirmed = true
|
||||
this.$axios.get(`/api/v1/assets/system-users/${this.MFAInfo.systemUser.id}/auth-info/`).then(res => {
|
||||
this.MFAConfirmed = true
|
||||
this.MFAInfo.username = res.username
|
||||
if (res.protocol === 'k8s') {
|
||||
this.MFAInfo.password = res.token
|
||||
} else {
|
||||
this.MFAInfo.password = res.password
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.showMFADialog = true
|
||||
}
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
tableAttrs: {
|
||||
stripe: false, // 斑马纹表格
|
||||
border: true, // 表格边框
|
||||
fit: true, // 宽度自适应,
|
||||
tooltipEffect: 'dark',
|
||||
rowClassName({ row, rowIndex }) {
|
||||
return 'row-background-color'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasImport: false,
|
||||
handleExport({ selectedRows }) {
|
||||
vm.selectedRows = selectedRows
|
||||
vm.dialogStatus = 'export'
|
||||
if (!vm.needMFAVerify) {
|
||||
vm.showMFADialog = false
|
||||
vm.showExportDialog = true
|
||||
} else {
|
||||
vm.showMFADialog = true
|
||||
}
|
||||
setTimeout(() => {
|
||||
vm.accountsUrl = url
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'MFA_TTl',
|
||||
'MFAVerifyAt',
|
||||
'publicSettings'
|
||||
]),
|
||||
needMFAVerify() {
|
||||
if (!this.publicSettings.SECURITY_VIEW_AUTH_NEED_MFA) {
|
||||
return false
|
||||
}
|
||||
const ttl = this.publicSettings.SECURITY_MFA_VERIFY_TTL
|
||||
const now = new Date()
|
||||
return !(this.MFAVerifyAt && (now - this.MFAVerifyAt) < ttl * 1000)
|
||||
},
|
||||
exportOptions() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('common.imExport.ExportAll'),
|
||||
value: 'all',
|
||||
can: true
|
||||
},
|
||||
{
|
||||
label: this.$t('common.imExport.ExportOnlySelectedItems'),
|
||||
value: 'selected',
|
||||
can: this.selectedRows.length > 0
|
||||
}
|
||||
]
|
||||
},
|
||||
exportTypeOptions() {
|
||||
return [
|
||||
{
|
||||
label: 'CSV',
|
||||
value: 'csv',
|
||||
can: true
|
||||
},
|
||||
{
|
||||
label: 'Excel',
|
||||
value: 'xlsx',
|
||||
can: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
MFAConfirm() {
|
||||
if (this.MFAInput.length !== 6) {
|
||||
return this.$message.error(this.$t('common.MFAErrorMsg'))
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/otp/verify/`, {
|
||||
code: this.MFAInput
|
||||
}
|
||||
).then(
|
||||
res => {
|
||||
this.$store.dispatch('users/setMFAVerify')
|
||||
if (this.dialogStatus === 'export') {
|
||||
this.showMFADialog = false
|
||||
this.showExportDialog = true
|
||||
} else {
|
||||
this.$axios.get(`/api/v1/assets/system-users/${this.MFAInfo.systemUser.id}/auth-info/`).then(res => {
|
||||
this.MFAConfirmed = true
|
||||
this.MFAInfo.username = res.username
|
||||
this.MFAInfo.password = res.password
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
},
|
||||
async performExport(selectRows, exportOption, q) {
|
||||
const url = `/api/v1/applications/application-user-auth-infos/`
|
||||
const query = Object.assign({}, q)
|
||||
if (exportOption === 'selected') {
|
||||
const resources = []
|
||||
const data = selectRows
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
resources.push(data[index].id)
|
||||
}
|
||||
const spm = await createSourceIdCache(resources)
|
||||
query['spm'] = spm.spm
|
||||
} else if (exportOption === 'filtered') {
|
||||
// console.log(listTableRef)
|
||||
// console.log(listTableRef.dataTable)
|
||||
// delete query['limit']
|
||||
// delete query['offset']
|
||||
}
|
||||
query['format'] = this.exportTypeOption
|
||||
const queryStr =
|
||||
(url.indexOf('?') > -1 ? '&' : '?') +
|
||||
queryUtil.stringify(query, '=', '&')
|
||||
return this.downloadCsv(url + queryStr)
|
||||
},
|
||||
async performExportConfirm() {
|
||||
const listTableRef = this.$refs.RightTable.$refs.ListTable
|
||||
const query = listTableRef.dataTable.getQuery()
|
||||
delete query['limit']
|
||||
delete query['offset']
|
||||
return this.performExport(this.selectedRows, this.exportOption, query)
|
||||
},
|
||||
async handleExportConfirm() {
|
||||
await this.performExportConfirm()
|
||||
this.showExportDialog = false
|
||||
},
|
||||
handleExportCancel() {
|
||||
this.showExportDialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.asset-table ::v-deep .row-clicked, .asset-user-table ::v-deep .row-background-color {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
.asset-table {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
.application-table ::v-deep .row-clicked, .application-user-table ::v-deep .row-background-color {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
& >>> .table-content {
|
||||
margin-left: 21px;
|
||||
}
|
||||
& ::v-deep .el-table__row{
|
||||
height: 40px;
|
||||
& > td{
|
||||
padding: 0;
|
||||
|
||||
.application-table {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
& ::v-deep .el-table__row{
|
||||
height: 40px;
|
||||
& > td{
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.mini-button{
|
||||
width: 12px;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
padding: 9px 0;
|
||||
background-color: #1ab394;
|
||||
border-color: #1ab394;
|
||||
color: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
line-height: 1.428;
|
||||
cursor:pointer;
|
||||
}
|
||||
.noDataR{
|
||||
width: 100%;
|
||||
height: 40vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
flex-direction: column;
|
||||
.hintWrap{
|
||||
color: #D4D6E6;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
.export-item {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
}
|
||||
.asset-user-table{
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.export-form >>> .el-form-item__label {
|
||||
line-height: 2
|
||||
}
|
||||
.application-user-table{
|
||||
padding-left:20px ;
|
||||
}
|
||||
.noDataR {
|
||||
width: 100%;
|
||||
height: 40vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
flex-direction: column;
|
||||
.hintWrap {
|
||||
color: #D4D6E6;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,20 +1,63 @@
|
||||
<template>
|
||||
<GenericTreeListPage ref="TreeTablePage" :tree-setting="treeSetting">
|
||||
<template #table>
|
||||
<AccountListTable ref="table" :url="accountsUrl" />
|
||||
</template>
|
||||
</GenericTreeListPage>
|
||||
<Page>
|
||||
<el-row>
|
||||
<el-col v-show="iShowTree" :span="iShowTree?4:0">
|
||||
<AutoDataZTree
|
||||
ref="AUtoDataZTree"
|
||||
:setting="treeSetting"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="iShowTree?9:11">
|
||||
<div class="mini">
|
||||
<div style="display:block" class="mini-button" @click="iShowTree=!iShowTree">
|
||||
<i v-show="iShowTree" class="fa fa-angle-left fa-x" />
|
||||
<i v-show="!iShowTree" class="fa fa-angle-right fa-x" />
|
||||
</div>
|
||||
</div>
|
||||
<GenericListTable
|
||||
ref="LeftTable"
|
||||
class="asset-table"
|
||||
:header-actions="leftTable.headerActions"
|
||||
:table-config="leftTable.tableConfig"
|
||||
@row-click="leftTable.tableConfig.rowClick"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="iShowTree?11:13">
|
||||
<AssetUserTable
|
||||
v-if="!isInit"
|
||||
ref="RightTable"
|
||||
class="asset-user-table"
|
||||
:url="rightTable.url"
|
||||
:search-exclude="rightTable.searchExclude"
|
||||
:extra-query="rightTable.extraQuery"
|
||||
:has-left-actions="rightTable.hasLeftActions"
|
||||
:table-config="rightTable.tableConfig"
|
||||
:has-clone="false"
|
||||
:has-import="false"
|
||||
/>
|
||||
<div v-else class="noDataR">
|
||||
<div class="hintWrap">
|
||||
<div>
|
||||
{{ $t('accounts.PleaseClickLeftAssetToViewAssetAccount') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericTreeListPage from '@/layout/components/GenericTreeListPage'
|
||||
import AccountListTable from '@/components/AccountListTable'
|
||||
import { setUrlParam } from '@/utils/common'
|
||||
import Page from '@/layout/components/Page'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import AutoDataZTree from '@/components/AutoDataZTree/index'
|
||||
import { AssetUserTable } from '@/components'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'AssetAccountList',
|
||||
components: {
|
||||
GenericTreeListPage, AccountListTable
|
||||
AutoDataZTree, GenericListTable, Page, AssetUserTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
@@ -22,30 +65,104 @@ export default {
|
||||
isInit: true,
|
||||
clickedRow: null,
|
||||
iShowTree: true,
|
||||
accountsUrl: '/api/v1/assets/accounts/',
|
||||
treeSetting: {
|
||||
showMenu: false,
|
||||
showRefresh: true,
|
||||
showRefresh: false,
|
||||
showAssets: false,
|
||||
url: '/api/v1/assets/accounts/',
|
||||
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1',
|
||||
url: '',
|
||||
treeUrl: '/api/v1/assets/nodes/children/tree/',
|
||||
callback: {
|
||||
onSelected: function(event, treeNode) {
|
||||
let url = '/api/v1/assets/accounts/'
|
||||
if (treeNode.meta.type === 'node') {
|
||||
const nodeId = treeNode.meta.data.id
|
||||
url = setUrlParam(url, 'asset', '')
|
||||
url = setUrlParam(url, 'node', nodeId)
|
||||
} else if (treeNode.meta.type === 'asset') {
|
||||
const assetId = treeNode.meta.data.id
|
||||
url = setUrlParam(url, 'node', '')
|
||||
url = setUrlParam(url, 'asset', assetId)
|
||||
}
|
||||
setTimeout(() => {
|
||||
vm.accountsUrl = url
|
||||
}, 100)
|
||||
vm.leftTable.tableConfig.url = `/api/v1/assets/assets/?node_id=${treeNode.meta.node.id}`
|
||||
vm.isInit = true
|
||||
}
|
||||
}
|
||||
},
|
||||
leftTable: {
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/assets/',
|
||||
columns: [
|
||||
'hostname', 'ip', 'protocols', 'platform', 'comment', 'org_name'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['hostname', 'ip'],
|
||||
default: ['hostname', 'ip', 'platform']
|
||||
},
|
||||
columnsMeta: {
|
||||
hostname: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
route: 'AssetDetail',
|
||||
routeQuery: {
|
||||
activeTab: 'Detail'
|
||||
}
|
||||
},
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
ip: {
|
||||
showOverflowTooltip: true
|
||||
}
|
||||
},
|
||||
tableAttrs: {
|
||||
stripe: false, // 斑马纹表格
|
||||
border: true, // 表格边框
|
||||
fit: true, // 宽度自适应,
|
||||
tooltipEffect: 'dark',
|
||||
rowClassName({ row, rowIndex }) {
|
||||
if (row === vm.clickedRow) {
|
||||
return 'row-clicked'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
rowClick: function(row, column, event) {
|
||||
vm.rightTable.url = `/api/v1/assets/asset-users/?asset_id=${row.id}`
|
||||
vm.rightTable.extraQuery.asset_id = row.id
|
||||
vm.clickedRow = row
|
||||
vm.isInit = false
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasCreate: false,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasBulkDelete: false,
|
||||
hasColumnSetting: true,
|
||||
hasRefresh: true,
|
||||
hasBulkUpdate: false
|
||||
}
|
||||
},
|
||||
rightTable: {
|
||||
url: `/api/v1/assets/asset-users/?hostname=ShowFirstAssetRelated`,
|
||||
extraQuery: {
|
||||
latest: 1
|
||||
},
|
||||
tableConfig: {
|
||||
columns: ['name', 'username', 'version', 'backend_display', 'date_created', 'org_name', 'actions'],
|
||||
columnsShow: {
|
||||
min: ['username', 'actions'],
|
||||
default: ['name', 'username', 'version', 'backend_display', 'date_created', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: null,
|
||||
showOverflowTooltip: true,
|
||||
sortable: false
|
||||
}
|
||||
},
|
||||
tableAttrs: {
|
||||
stripe: true, // 斑马纹表格
|
||||
border: false, // 表格边框
|
||||
fit: true, // 宽度自适应,
|
||||
tooltipEffect: 'dark',
|
||||
rowClassName({ row, rowIndex }) {
|
||||
return 'row-background-color'
|
||||
}
|
||||
}
|
||||
},
|
||||
hasLeftActions: false,
|
||||
searchExclude: ['hostname', 'id', 'ip']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage v-bind="$data" />
|
||||
<GenericCreateUpdatePage v-bind="$data" :clean-form-value="cleanFormValue" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -24,17 +24,13 @@ export default {
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name']],
|
||||
[this.$t('xpack.Asset'), ['username', 'assets', 'nodes']],
|
||||
[this.$t('xpack.ChangeAuthPlan.PasswordStrategy'), ['is_password', 'password_strategy', 'password', 'password_rules']],
|
||||
[this.$t('xpack.ChangeAuthPlan.SecretKeyStrategy'), ['is_ssh_key', 'ssh_key_strategy', 'private_key']],
|
||||
[this.$t('xpack.ChangeAuthPlan.PasswordStrategy'), ['password_strategy', 'password', 'password_rules']],
|
||||
[this.$t('xpack.Timer'), ['is_periodic', 'crontab', 'interval']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
initial: {
|
||||
password_strategy: 'custom',
|
||||
ssh_key_strategy: 'add',
|
||||
is_periodic: true,
|
||||
is_password: true,
|
||||
is_ssh_key: false,
|
||||
password_rules: {
|
||||
length: 30
|
||||
},
|
||||
@@ -54,7 +50,7 @@ export default {
|
||||
},
|
||||
password: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.password_strategy !== 'custom' || formValue.is_password === false
|
||||
return formValue.password_strategy !== 'custom'
|
||||
},
|
||||
rules: [
|
||||
{ required: this.$route.meta.action === 'create', message: this.$t('common.fieldRequiredError'), trigger: 'blur' }
|
||||
@@ -64,19 +60,6 @@ export default {
|
||||
type: 'group',
|
||||
items: this.generatePasswordRulesItemsFields()
|
||||
},
|
||||
private_key: {
|
||||
el: {
|
||||
type: 'textarea',
|
||||
placeholder: '-----BEGIN OPENSSH PRIVATE KEY-----',
|
||||
autosize: { minRows: 3 }
|
||||
},
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_ssh_key === false
|
||||
},
|
||||
rules: [
|
||||
{ required: this.$route.meta.action === 'create', message: this.$t('common.fieldRequiredError'), trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
nodes: {
|
||||
label: this.$t('xpack.Node'),
|
||||
el: {
|
||||
@@ -92,24 +75,6 @@ export default {
|
||||
is_periodic: {
|
||||
type: 'switch'
|
||||
},
|
||||
is_password: {
|
||||
type: 'switch'
|
||||
},
|
||||
is_ssh_key: {
|
||||
type: 'switch'
|
||||
},
|
||||
password_strategy: {
|
||||
label: this.$t('xpack.ChangeAuthPlan.PasswordStrategy'),
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_password === false
|
||||
}
|
||||
},
|
||||
ssh_key_strategy: {
|
||||
label: this.$t('xpack.ChangeAuthPlan.SecretKeyStrategy'),
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_ssh_key === false
|
||||
}
|
||||
},
|
||||
crontab: {
|
||||
label: this.$t('xpack.RegularlyPerform'),
|
||||
hidden: (formValue) => {
|
||||
@@ -127,21 +92,21 @@ export default {
|
||||
{ validator: validatorInterval }
|
||||
]
|
||||
}
|
||||
},
|
||||
cleanFormValue(data) {
|
||||
if (data['password_strategy'] === 'custom') {
|
||||
delete data['password_rules']
|
||||
} else {
|
||||
delete data['password']
|
||||
}
|
||||
if (data['interval'] === '') {
|
||||
delete data['interval']
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cleanFormValue(data) {
|
||||
if (data['password_strategy'] === 'custom') {
|
||||
delete data['password_rules']
|
||||
} else {
|
||||
delete data['password']
|
||||
}
|
||||
if (data['interval'] === '') {
|
||||
delete data['interval']
|
||||
}
|
||||
return data
|
||||
},
|
||||
generatePasswordRulesItemsFields() {
|
||||
const itemsFields = []
|
||||
const items = [
|
||||
@@ -150,7 +115,7 @@ export default {
|
||||
items.forEach((item, index, array) => {
|
||||
itemsFields.push({
|
||||
id: item.id, prop: item.prop, el: {}, attrs: {}, type: 'input', label: item.label, rules: [Required],
|
||||
hidden: (formValue) => { return ['random_one', 'random_all'].indexOf(formValue.password_strategy) === -1 || formValue.is_password === false }
|
||||
hidden: (formValue) => { return ['random_one', 'random_all'].indexOf(formValue.password_strategy) === -1 }
|
||||
})
|
||||
})
|
||||
return itemsFields
|
||||
|
||||
@@ -23,7 +23,7 @@ export default {
|
||||
url: `/api/v1/xpack/change-auth-plan/plan-execution/?plan_id=${this.object.id}`,
|
||||
columns: [
|
||||
'username', 'assets_amount', 'nodes_amount', 'result_summary', 'password_strategy_display',
|
||||
'timedelta', 'trigger_display', 'date_start', 'actions'
|
||||
'timedelta', 'date_start', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
username: {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<script>
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'ChangeAuthPlanList',
|
||||
@@ -21,10 +20,6 @@ export default {
|
||||
'name', 'username', 'assets_amount', 'nodes_amount', 'password_strategy_display',
|
||||
'periodic_display', 'run_times', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'username', 'password_strategy_display', 'periodic_display', 'run_times', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
@@ -74,7 +69,7 @@ export default {
|
||||
`/api/v1/xpack/change-auth-plan/plan-execution/`,
|
||||
{ plan: row.id }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '_blank', 'toolbar=yes, width=900, height=600')
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
|
||||
@@ -1,75 +1,249 @@
|
||||
<template>
|
||||
<TreeTable :table-config="tableConfig" :tree-setting="treeSetting" :header-actions="headerActions" />
|
||||
<div>
|
||||
<el-row>
|
||||
<el-col v-show="iShowTree" :span="iShowTree?4:0">
|
||||
<AutoDataZTree
|
||||
ref="AUtoDataZTree"
|
||||
:setting="treeSetting"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="iShowTree?9:11">
|
||||
<div class="mini">
|
||||
<div style="display:block" class="mini-button" @click="iShowTree=!iShowTree">
|
||||
<i v-show="iShowTree" class="fa fa-angle-left fa-x" />
|
||||
<i v-show="!iShowTree" class="fa fa-angle-right fa-x" />
|
||||
</div>
|
||||
</div>
|
||||
<GenericListTable
|
||||
ref="LeftTable"
|
||||
class="asset-table"
|
||||
:header-actions="leftTable.headerActions"
|
||||
:table-config="leftTable.tableConfig"
|
||||
@row-click="leftTable.tableConfig.rowClick"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="iShowTree?11:13">
|
||||
<GenericListTable
|
||||
v-if="!isInit"
|
||||
ref="RightTable"
|
||||
class="asset-user-table"
|
||||
:header-actions="rightTable.headerActions"
|
||||
:table-config="rightTable.tableConfig"
|
||||
/>
|
||||
<div v-else class="noDataR">
|
||||
<div class="hintWrap">
|
||||
<div>{{ $t('accounts.PleaseClickLeftAssetToViewGatheredUser') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TreeTable from '@/components/TreeTable'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import AutoDataZTree from '@/components/AutoDataZTree/index'
|
||||
import { ChoicesFormatter, DetailFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'AssetAccountList',
|
||||
components: {
|
||||
TreeTable
|
||||
AutoDataZTree, GenericListTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
isInit: true,
|
||||
clickedRow: {},
|
||||
iShowTree: true,
|
||||
treeSetting: {
|
||||
showMenu: false,
|
||||
showRefresh: true,
|
||||
showAssets: true,
|
||||
url: '/api/v1/assets/gathered-users/',
|
||||
nodeUrl: '/api/v1/assets/nodes/',
|
||||
// ?assets=0不显示资产. =1显示资产
|
||||
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1'
|
||||
},
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/gathered-users/',
|
||||
hasTree: true,
|
||||
columns: [
|
||||
'hostname', 'ip', 'username', 'date_last_login', 'present', 'ip_last_login', 'date_updated'
|
||||
],
|
||||
columnsMeta: {
|
||||
hostname: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
ip: {
|
||||
width: 120
|
||||
},
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
present: {
|
||||
width: 80
|
||||
},
|
||||
ip_last_login: {
|
||||
width: 120
|
||||
showRefresh: false,
|
||||
showAssets: false,
|
||||
url: '',
|
||||
treeUrl: '/api/v1/assets/nodes/children/tree/',
|
||||
callback: {
|
||||
onSelected: function(event, treeNode) {
|
||||
vm.leftTable.tableConfig.url = `/api/v1/assets/assets/?node_id=${treeNode.meta.node.id}`
|
||||
vm.isInit = true
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasCreate: false,
|
||||
hasLeftActions: false,
|
||||
hasImport: false,
|
||||
searchConfig: {
|
||||
exclude: ['asset'],
|
||||
options: [
|
||||
{
|
||||
label: this.$t('assets.Hostname'),
|
||||
value: 'asset__hostname'
|
||||
leftTable: {
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/assets/',
|
||||
columns: [
|
||||
'hostname', 'ip', 'public_ip', 'admin_user_display',
|
||||
'protocols', 'platform', 'connectivity',
|
||||
'created_by', 'date_created', 'comment', 'org_name'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['hostname', 'ip', 'platform'],
|
||||
default: ['hostname', 'ip', 'connectivity', 'platform']
|
||||
},
|
||||
columnsMeta: {
|
||||
hostname: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
route: 'AssetDetail',
|
||||
routeQuery: {
|
||||
activeTab: 'Detail'
|
||||
}
|
||||
},
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
label: 'IP',
|
||||
value: 'asset__ip'
|
||||
connectivity: {
|
||||
label: this.$t('assets.Reachable'),
|
||||
formatter: ChoicesFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
0: 'fa-times text-danger',
|
||||
1: 'fa-check text-primary',
|
||||
2: 'fa-circle text-warning'
|
||||
},
|
||||
typeChange: function(val) {
|
||||
if (!val) {
|
||||
return 2
|
||||
}
|
||||
return val.status
|
||||
},
|
||||
hasTips: true
|
||||
},
|
||||
width: '90px',
|
||||
align: 'center'
|
||||
}
|
||||
]
|
||||
},
|
||||
tableAttrs: {
|
||||
stripe: false, // 斑马纹表格
|
||||
border: true, // 表格边框
|
||||
fit: true, // 宽度自适应,
|
||||
tooltipEffect: 'dark',
|
||||
rowClassName({ row, rowIndex }) {
|
||||
if (row === vm.clickedRow) {
|
||||
return 'row-clicked'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
rowClick: function(row, column, event) {
|
||||
vm.rightTable.tableConfig.url = `/api/v1/assets/gathered-users/?asset_id=${row.id}`
|
||||
vm.clickedRow = row
|
||||
vm.isInit = false
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasCreate: false,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasBulkDelete: false,
|
||||
hasBulkUpdate: false
|
||||
}
|
||||
},
|
||||
rightTable: {
|
||||
tableConfig: {
|
||||
url: `/api/v1/assets/gathered-users/?asset__hostname=ShowFirstAssetRelated`,
|
||||
columns: [
|
||||
'username', 'date_last_login', 'present', 'ip_last_login', 'date_updated', 'org_name'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['username'],
|
||||
default: [
|
||||
'username', 'date_last_login', 'present', 'ip_last_login', 'date_updated'
|
||||
]
|
||||
},
|
||||
columnsMeta: {
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
present: {
|
||||
width: 80
|
||||
},
|
||||
ip_last_login: {
|
||||
width: 120
|
||||
}
|
||||
},
|
||||
tableAttrs: {
|
||||
stripe: true, // 斑马纹表格
|
||||
border: true, // 表格边框
|
||||
fit: true, // 宽度自适应,
|
||||
tooltipEffect: 'dark',
|
||||
rowClassName({ row, rowIndex }) {
|
||||
return 'row-background-color'
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasCreate: false,
|
||||
hasExport: true,
|
||||
hasImport: false,
|
||||
hasBulkDelete: false,
|
||||
hasBulkUpdate: false,
|
||||
searchConfig: {
|
||||
exclude: ['asset']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onGatherUserTasks() {
|
||||
this.$router.push({ name: 'GatherUserTaskList' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="scss" scoped>
|
||||
.asset-table ::v-deep .row-clicked, .asset-user-table ::v-deep .row-background-color {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
.asset-table {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
& >>> .table-content {
|
||||
margin-left: 21px;
|
||||
}
|
||||
& ::v-deep .el-table__row{
|
||||
height: 40px;
|
||||
& > td{
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.mini-button{
|
||||
width: 12px;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
padding: 9px 0;
|
||||
background-color: #1ab394;
|
||||
border-color: #1ab394;
|
||||
color: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
line-height: 1.428;
|
||||
cursor:pointer;
|
||||
}
|
||||
.noDataR{
|
||||
width: 100%;
|
||||
height: 40vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
flex-direction: column;
|
||||
.hintWrap{
|
||||
color: #D4D6E6;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
.asset-user-table{
|
||||
padding-left: 20px;
|
||||
& ::v-deep .el-table__header-wrapper thead tr{
|
||||
height: 40px;
|
||||
& > th{
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -38,7 +38,7 @@ export default {
|
||||
value: [],
|
||||
ajax: {
|
||||
transformOption: (item) => {
|
||||
return { label: item['full_value'], value: item.id }
|
||||
return { label: item.full_value, value: item.id }
|
||||
},
|
||||
url: '/api/v1/assets/nodes/'
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export default {
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.Cloud.PeriodicPerform'),
|
||||
value: this.object.is_periodic ? (this.$t('xpack.Cloud.True')) : (this.$t('xpack.Cloud.False'))
|
||||
value: this.object.is_periodic ? (this.$t('xpack.GatherUser.True')) : (this.$t('xpack.GatherUser.False'))
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.Cloud.Periodic'),
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<script>
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -66,7 +65,7 @@ export default {
|
||||
`/api/v1/xpack/gathered-user/task-executions/`,
|
||||
{ task: data.row.id }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '_blank', 'toolbar=yes, width=900, height=600')
|
||||
}).catch(res => {
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
<template>
|
||||
<GenericCreateUpdatePage v-bind="$data" />
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" :perform-submit="performSubmit" :after-get-form-value="afterGetFormValue" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -59,40 +60,59 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
url: '/api/v1/acls/login-asset-acls/',
|
||||
afterGetFormValue(formValue) {
|
||||
formValue.assets.ip_group = formValue.assets.ip_group.toString()
|
||||
formValue.assets.hostname_group = formValue.assets.hostname_group.toString()
|
||||
formValue.system_users.name_group = formValue.system_users.name_group.toString()
|
||||
formValue.system_users.protocol_group = formValue.system_users.protocol_group.toString()
|
||||
formValue.system_users.username_group = formValue.system_users.username_group.toString()
|
||||
formValue.users.username_group = formValue.users.username_group.toString()
|
||||
return formValue
|
||||
},
|
||||
cleanFormValue(value) {
|
||||
if (!Array.isArray(value.assets.ip_group)) {
|
||||
value.assets.ip_group = value.assets.ip_group ? value.assets.ip_group.split(',') : []
|
||||
}
|
||||
if (!Array.isArray(value.assets.hostname_group)) {
|
||||
value.assets.hostname_group = value.assets.hostname_group ? value.assets.hostname_group.split(',') : []
|
||||
}
|
||||
if (!Array.isArray(value.system_users.protocol_group)) {
|
||||
value.system_users.protocol_group = value.system_users.protocol_group ? value.system_users.protocol_group.split(',') : []
|
||||
}
|
||||
if (!Array.isArray(value.system_users.name_group)) {
|
||||
value.system_users.name_group = value.system_users.name_group ? value.system_users.name_group.split(',') : []
|
||||
}
|
||||
if (!Array.isArray(value.system_users.username_group)) {
|
||||
value.system_users.username_group = value.system_users.username_group ? value.system_users.username_group.split(',') : []
|
||||
}
|
||||
if (!Array.isArray(value.users.username_group)) {
|
||||
value.users.username_group = value.users.username_group ? value.users.username_group.split(',') : []
|
||||
}
|
||||
return value
|
||||
}
|
||||
url: '/api/v1/acls/login-asset-acls/'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getUrl() {
|
||||
const params = this.$route.params
|
||||
let url = `/api/v1/acls/login-asset-acls/`
|
||||
if (params.id) {
|
||||
url = `${url}${params.id}/`
|
||||
} else {
|
||||
url = `${url}`
|
||||
}
|
||||
return url
|
||||
},
|
||||
getMethod() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
}
|
||||
},
|
||||
afterGetFormValue(validValues) {
|
||||
validValues.assets.ip_group = validValues.assets.ip_group.toString()
|
||||
validValues.assets.hostname_group = validValues.assets.hostname_group.toString()
|
||||
validValues.system_users.name_group = validValues.system_users.name_group.toString()
|
||||
validValues.system_users.protocol_group = validValues.system_users.protocol_group.toString()
|
||||
validValues.system_users.username_group = validValues.system_users.username_group.toString()
|
||||
validValues.users.username_group = validValues.users.username_group.toString()
|
||||
return validValues
|
||||
},
|
||||
performSubmit(validValues) {
|
||||
if (!Array.isArray(validValues.assets.ip_group)) {
|
||||
validValues.assets.ip_group = validValues.assets.ip_group ? validValues.assets.ip_group.split(',') : []
|
||||
}
|
||||
if (!Array.isArray(validValues.assets.hostname_group)) {
|
||||
validValues.assets.hostname_group = validValues.assets.hostname_group ? validValues.assets.hostname_group.split(',') : []
|
||||
}
|
||||
if (!Array.isArray(validValues.system_users.protocol_group)) {
|
||||
validValues.system_users.protocol_group = validValues.system_users.protocol_group ? validValues.system_users.protocol_group.split(',') : []
|
||||
}
|
||||
if (!Array.isArray(validValues.system_users.name_group)) {
|
||||
validValues.system_users.name_group = validValues.system_users.name_group ? validValues.system_users.name_group.split(',') : []
|
||||
}
|
||||
if (!Array.isArray(validValues.system_users.username_group)) {
|
||||
validValues.system_users.username_group = validValues.system_users.username_group ? validValues.system_users.username_group.split(',') : []
|
||||
}
|
||||
if (!Array.isArray(validValues.users.username_group)) {
|
||||
validValues.users.username_group = validValues.users.username_group ? validValues.users.username_group.split(',') : []
|
||||
}
|
||||
const method = this.getMethod()
|
||||
return this.$axios[method](`${this.getUrl()}`, validValues)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -13,17 +13,10 @@ export default {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/acls/login-asset-acls/',
|
||||
columns: [
|
||||
'name', 'user_username_group', 'hostname_group', 'ip_group', 'name_group',
|
||||
'protocol_group', 'systemuser_username_group', 'reviewers', 'priority',
|
||||
'is_active', 'comment', 'actions'
|
||||
],
|
||||
columns: ['name', 'user_username_group', 'hostname_group', 'ip_group', 'name_group', 'protocol_group', 'systemuser_username_group', 'reviewers', 'priority', 'is_active', 'comment', 'actions'],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: [
|
||||
'name', 'user_username_group', 'hostname_group', 'ip_group', 'reviewers',
|
||||
'priority', 'is_active', 'comment', 'actions'
|
||||
]
|
||||
default: ['name', 'user_username_group', 'hostname_group', 'ip_group', 'reviewers', 'priority', 'is_active', 'comment', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
user_username_group: {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
|
||||
<template>
|
||||
<GenericCreateUpdatePage v-bind="$data" />
|
||||
<GenericCreateUpdatePage
|
||||
v-bind="$data"
|
||||
:perform-submit="performSubmit"
|
||||
:after-get-form-value="afterGetFormValue"
|
||||
:get-url="getUrl"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -37,54 +43,45 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
getUrl() {
|
||||
const query = this.$route.query
|
||||
const params = this.$route.params
|
||||
let url = `/api/v1/acls/login-acls/`
|
||||
if (params.id) {
|
||||
url = `${url}${params.id}/`
|
||||
}
|
||||
if (query.user) {
|
||||
url = `${url}?user=${query.user}`
|
||||
}
|
||||
return url
|
||||
},
|
||||
url: `/api/v1/acls/login-acls/`,
|
||||
updateSuccessNextRoute: { name: 'UserDetail', params: {
|
||||
id: this.$route.query.user
|
||||
}},
|
||||
createSuccessNextRoute: { name: 'UserDetail', params: {
|
||||
id: this.$route.query.user
|
||||
}},
|
||||
onPerformError(error, method, vm) {
|
||||
this.$emit('submitError', error)
|
||||
const response = error.response
|
||||
const data = response.data
|
||||
if (response.status === 400) {
|
||||
for (const key of Object.keys(data)) {
|
||||
let value = data[key]
|
||||
if (key === 'ip_group') {
|
||||
value = Object.values(data[key])
|
||||
}
|
||||
if (value instanceof Array) {
|
||||
value = value.join(';')
|
||||
}
|
||||
this.$refs.form.setFieldError(key, value)
|
||||
}
|
||||
}
|
||||
},
|
||||
afterGetFormValue(validValues) {
|
||||
validValues.ip_group = validValues.ip_group.toString()
|
||||
return validValues
|
||||
},
|
||||
cleanFormValue(value) {
|
||||
if (!Array.isArray(value.ip_group)) {
|
||||
value.ip_group = value.ip_group ? value.ip_group.split(',') : []
|
||||
}
|
||||
return value
|
||||
}
|
||||
}}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMethod() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
}
|
||||
},
|
||||
getUrl() {
|
||||
const params = this.$route.params
|
||||
let url = this.url
|
||||
if (params.id) {
|
||||
url = `${url}${params.id}/?user=${this.$route.query.user}`
|
||||
} else {
|
||||
url = `${url}?user=${this.$route.query.user}`
|
||||
}
|
||||
return url
|
||||
},
|
||||
afterGetFormValue(validValues) {
|
||||
validValues.ip_group = validValues.ip_group.toString()
|
||||
return validValues
|
||||
},
|
||||
performSubmit(validValues) {
|
||||
if (!Array.isArray(validValues.ip_group)) {
|
||||
validValues.ip_group = validValues.ip_group ? validValues.ip_group.split(',') : []
|
||||
}
|
||||
const method = this.getMethod()
|
||||
return this.$axios[method](`${this.getUrl()}`, validValues)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -46,20 +46,33 @@ export default {
|
||||
getUrl() {
|
||||
const params = this.$route.params
|
||||
let url = `/api/v1/applications/applications/`
|
||||
const method = this.getMethod()
|
||||
if (params.id) {
|
||||
url = `${url}${params.id}/`
|
||||
}
|
||||
return `${url}?type=${this.$route.query.type}`
|
||||
return method === 'post' ? `${url}?type=${this.$route.query.type}` : `${url}?category=db`
|
||||
},
|
||||
cleanFormValue(value) {
|
||||
value.category = 'db'
|
||||
return value
|
||||
performSubmit(validValues) {
|
||||
const params = this.$route.params
|
||||
const baseUrl = `/api/v1/applications/applications/`
|
||||
const url = (params.id) ? `${baseUrl}${params.id}/` : baseUrl
|
||||
const method = this.getMethod()
|
||||
validValues.category = 'db'
|
||||
return this.$axios[method](`${url}?type=${validValues.type}`, validValues)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
initial() {
|
||||
return this.$route.query
|
||||
},
|
||||
getMethod() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<GenericDetailPage :object.sync="app" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<GenericDetailPage :object.sync="DatabaseApp" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" :object="app" />
|
||||
<component :is="config.activeMenu" :object="DatabaseApp" />
|
||||
</keep-alive>
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
@@ -18,7 +18,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
app: {
|
||||
DatabaseApp: {
|
||||
name: '', get_type_display: '', host: '', port: '', database: '', date_created: '', created_by: '', comment: '', attrs: ''
|
||||
},
|
||||
config: {
|
||||
@@ -31,10 +31,7 @@ export default {
|
||||
],
|
||||
actions: {
|
||||
detailApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
|
||||
deleteApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
|
||||
updateCallback: (item) => {
|
||||
this.$router.push({ name: 'DatabaseAppUpdate', params: { id: this.app.id }, query: { type: this.app.type }})
|
||||
}
|
||||
deleteApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +45,6 @@ export default {
|
||||
onClone: ({ row }) => {
|
||||
vm.$router.push({ name: 'DatabaseAppCreate', query: { type: row.type, clone_from: row.id }})
|
||||
},
|
||||
onUpdate: ({ row }) => {
|
||||
vm.$router.push({ name: 'DatabaseAppUpdate', params: { id: row.id }, query: { type: row.type }})
|
||||
},
|
||||
performDelete: function({ row, col, cellValue, reload }) {
|
||||
this.$axios.delete(
|
||||
`/api/v1/applications/applications/${row.id}/`
|
||||
@@ -66,7 +63,7 @@ export default {
|
||||
hasCreate: false,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasBulkDelete: true,
|
||||
hasMoreActions: false,
|
||||
createRoute: 'DatabaseAppCreate',
|
||||
moreCreates: {
|
||||
callback: (item) => {
|
||||
|
||||
@@ -50,13 +50,25 @@ export default {
|
||||
}
|
||||
return `${url}?type=k8s`
|
||||
},
|
||||
cleanFormValue(value) {
|
||||
value.category = 'cloud'
|
||||
return value
|
||||
performSubmit(validValues) {
|
||||
const params = this.$route.params
|
||||
const baseUrl = `/api/v1/applications/applications/`
|
||||
const url = (params.id) ? `${baseUrl}${params.id}/` : baseUrl
|
||||
const method = this.getMethod()
|
||||
validValues.category = 'cloud'
|
||||
return this.$axios[method](`${url}?type=${validValues.type}`, validValues)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getMethod() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -11,6 +11,7 @@ import DetailCard from '@/components/DetailCard'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'DatabaseAppDetail',
|
||||
components: {
|
||||
DetailCard
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<GenericDetailPage :object.sync="app" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<GenericDetailPage :object.sync="KubernetesApp" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" :object="app" />
|
||||
<component :is="config.activeMenu" :object="KubernetesApp" />
|
||||
</keep-alive>
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
@@ -18,7 +18,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
app: {
|
||||
KubernetesApp: {
|
||||
name: '', type_display: '', cluster: '', date_created: '', created_by: '', comment: '', attrs: ''
|
||||
},
|
||||
config: {
|
||||
@@ -31,10 +31,7 @@ export default {
|
||||
],
|
||||
actions: {
|
||||
detailApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
|
||||
deleteApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
|
||||
updateCallback: (item) => {
|
||||
this.$router.push({ name: 'KubernetesAppUpdate', params: { id: this.app.id }, query: { type: this.app.type }})
|
||||
}
|
||||
deleteApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +53,9 @@ export default {
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasMoreActions: false,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasBulkDelete: true,
|
||||
createRoute: 'KubernetesAppCreate'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,13 +59,26 @@ export default {
|
||||
}
|
||||
return `${url}?type=${this.$route.query.type}`
|
||||
},
|
||||
cleanFormValue(value) {
|
||||
value.category = 'remote_app'
|
||||
return value
|
||||
performSubmit(validValues) {
|
||||
this.$log.debug('Validated data: ', validValues)
|
||||
const params = this.$route.params
|
||||
const baseUrl = `/api/v1/applications/applications/`
|
||||
const url = (params.id) ? `${baseUrl}${params.id}/` : baseUrl
|
||||
const method = this.getMethod()
|
||||
validValues.category = 'remote_app'
|
||||
return this.$axios[method](`${url}?type=${validValues.type}`, validValues)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getMethod() {
|
||||
const params = this.$route.params
|
||||
if (params.id) {
|
||||
return 'put'
|
||||
} else {
|
||||
return 'post'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import DetailCard from '@/components/DetailCard'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'RemoteAppDetail',
|
||||
components: {
|
||||
DetailCard
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<GenericDetailPage :object.sync="app" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<GenericDetailPage :object.sync="RemoteApp" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" :object="app" />
|
||||
<component :is="config.activeMenu" :object="RemoteApp" />
|
||||
</keep-alive>
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
@@ -17,8 +17,9 @@ export default {
|
||||
TabPage
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
app: {
|
||||
RemoteApp: {
|
||||
name: '', asset: '', get_type_display: '', path: '', date_created: '', created_by: '', comment: '', attrs: ''
|
||||
},
|
||||
config: {
|
||||
@@ -32,8 +33,8 @@ export default {
|
||||
actions: {
|
||||
detailApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
|
||||
deleteApiUrl: `/api/v1/applications/applications/${this.$route.params.id}/`,
|
||||
updateCallback: (item) => {
|
||||
this.$router.push({ name: 'RemoteAppUpdate', params: { id: this.app.id }, query: { type: this.app.type }})
|
||||
updateCallback: function(item) {
|
||||
vm.$router.push({ name: 'RemoteAppUpdate', params: { id: vm.RemoteApp.id }, query: { type: vm.RemoteApp.type }})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,8 @@ export default {
|
||||
},
|
||||
headerActions: {
|
||||
hasCreate: false,
|
||||
hasBulkDelete: true,
|
||||
hasMoreActions: false,
|
||||
hasBulkDelete: false,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
// createRoute: 'RemoteAppCreate',
|
||||
|
||||
65
src/views/assets/AdminUser/AdminUserCreateUpdate.vue
Normal file
65
src/views/assets/AdminUser/AdminUserCreateUpdate.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
<template>
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import { UploadKey } from '@/components'
|
||||
export default {
|
||||
name: 'AdminUserCreateUpdate',
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initial: {
|
||||
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'username', 'update_password', 'password', 'private_key']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
name: {
|
||||
el: {
|
||||
placeholder: this.$t('common.Name')
|
||||
}
|
||||
},
|
||||
username: {
|
||||
el: {
|
||||
placeholder: this.$t('common.Username')
|
||||
}
|
||||
},
|
||||
update_password: {
|
||||
label: this.$t('users.UpdatePassword'),
|
||||
type: 'checkbox',
|
||||
hidden: (formValue) => {
|
||||
if (formValue.update_password) {
|
||||
return true
|
||||
}
|
||||
return !this.$route.params.id
|
||||
}
|
||||
},
|
||||
password: {
|
||||
helpText: this.$t('common.passwordOrPassphrase'),
|
||||
hidden: (formValue) => {
|
||||
if (!this.$route.params.id) {
|
||||
return false
|
||||
}
|
||||
return !formValue.update_password
|
||||
}
|
||||
},
|
||||
private_key: {
|
||||
component: UploadKey
|
||||
}
|
||||
},
|
||||
url: '/api/v1/assets/admin-users/'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
60
src/views/assets/AdminUser/AdminUserDetail/AccountList.vue
Normal file
60
src/views/assets/AdminUser/AdminUserDetail/AccountList.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<AssetUserTable :url="assetUserUrl" :has-import="false" :has-clone="false" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { AssetUserTable } from '@/components'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
AssetUserTable
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
assetUserUrl: `/api/v1/assets/asset-users/?prefer_id=${this.object.id}&prefer=admin_user&latest=1`,
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('assets.TestAssetsConnective'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('assets.Test')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.get(
|
||||
`/api/v1/assets/admin-users/${this.object.id}/connective/`
|
||||
).then(res => {
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
}
|
||||
)
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
126
src/views/assets/AdminUser/AdminUserDetail/AssetList.vue
Normal file
126
src/views/assets/AdminUser/AdminUserDetail/AssetList.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<QuickActions type="primary" :actions="quickActions" />
|
||||
<RelationCard ref="RelationCard" type="info" style="margin-top: 15px" v-bind="nodeRelationConfig" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QuickActions from '@/components/QuickActions/index'
|
||||
import ListTable from '@/components/ListTable'
|
||||
import RelationCard from '@/components/RelationCard'
|
||||
import { ChoicesFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'AssetList',
|
||||
components: {
|
||||
QuickActions,
|
||||
ListTable,
|
||||
RelationCard
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: `/api/v1/assets/assets/?admin_user__id=${this.object.id}`,
|
||||
columns: [
|
||||
'hostname', 'ip', 'admin_user_display', 'connectivity'
|
||||
],
|
||||
columnsMeta: {
|
||||
admin_user_display: {
|
||||
label: this.$t('assets.AdminUser')
|
||||
},
|
||||
connectivity: {
|
||||
label: this.$t('assets.Reachable'),
|
||||
formatter: ChoicesFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
0: 'fa-times text-danger',
|
||||
1: 'fa-check text-primary',
|
||||
2: 'fa-circle text-warning'
|
||||
},
|
||||
typeChange: function(val) {
|
||||
return val.status
|
||||
},
|
||||
hasTips: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasBulkDelete: false,
|
||||
hasImport: false,
|
||||
hasExport: true,
|
||||
hasCreate: false,
|
||||
hasSearch: true,
|
||||
hasMoreActions: false
|
||||
},
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('assets.TestAssetsConnective'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('assets.Test')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.get(
|
||||
`/api/v1/assets/admin-users/${this.object.id}/connective/`
|
||||
).then(res => {
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
}
|
||||
)
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
],
|
||||
nodeRelationConfig: {
|
||||
icon: 'fa-info',
|
||||
title: this.$t('assets.ReplaceNodeAssetsAdminUser'),
|
||||
objectsAjax: {
|
||||
url: '/api/v1/assets/nodes/',
|
||||
transformOption: (item) => {
|
||||
return { label: item.full_value, value: item.id }
|
||||
}
|
||||
},
|
||||
performAdd: (items) => {
|
||||
const data = []
|
||||
const relationUrl = `/api/v1/assets/admin-users/${this.object.id}/nodes/`
|
||||
items.map(v => {
|
||||
data.push(v.value)
|
||||
})
|
||||
return this.$axios.patch(relationUrl, { nodes: data }).then(res => {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
}).catch(err => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
|
||||
})
|
||||
},
|
||||
onAddSuccess: () => {
|
||||
this.$refs.RelationCard.$refs.select2.clearSelected()
|
||||
this.$refs.ListTable.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
60
src/views/assets/AdminUser/AdminUserDetail/Detail.vue
Normal file
60
src/views/assets/AdminUser/AdminUserDetail/Detail.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="14">
|
||||
<DetailCard :items="detailCardItems" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DetailCard from '@/components/DetailCard'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
DetailCard
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
detailCardItems() {
|
||||
return [
|
||||
{
|
||||
key: this.$t('assets.Name'),
|
||||
value: this.object.name
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Username'),
|
||||
value: this.object.username
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.sshKeyFingerprint'),
|
||||
value: this.object.ssh_key_fingerprint
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.date_joined'),
|
||||
value: toSafeLocalDateStr(this.object.date_created)
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.CreatedBy'),
|
||||
value: this.object.created_by
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
50
src/views/assets/AdminUser/AdminUserDetail/index.vue
Normal file
50
src/views/assets/AdminUser/AdminUserDetail/index.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<GenericDetailPage :object.sync="TaskDetail" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" :object="TaskDetail" />
|
||||
</keep-alive>
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericDetailPage, TabPage } from '@/layout/components'
|
||||
import Detail from './Detail.vue'
|
||||
import AccountList from './AccountList.vue'
|
||||
import AssetList from './AssetList.vue'
|
||||
export default {
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
TabPage,
|
||||
Detail,
|
||||
AssetList,
|
||||
AccountList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
TaskDetail: {},
|
||||
config: {
|
||||
activeMenu: 'Detail',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('assets.AdminUserDetail'),
|
||||
name: 'Detail'
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.AssetList'),
|
||||
name: 'AssetList'
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.AccountList'),
|
||||
name: 'AccountList'
|
||||
}
|
||||
],
|
||||
hasRightSide: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
45
src/views/assets/AdminUser/AdminUserList.vue
Normal file
45
src/views/assets/AdminUser/AdminUserList.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" :help-message="helpMessage" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericListPage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/admin-users/',
|
||||
columns: [
|
||||
'name', 'username', 'assets_amount',
|
||||
'created_by', 'date_created', 'date_updated', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'username', 'assets_amount', 'comment', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
assets_amount: {
|
||||
width: '80px'
|
||||
}
|
||||
}
|
||||
},
|
||||
updateRoute: 'AdminUserUpdate',
|
||||
headerActions: {
|
||||
createRoute: 'AdminUserCreate'
|
||||
},
|
||||
helpMessage: this.$t('assets.AdminUserListHelpMessage')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage v-bind="$data" />
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -63,13 +63,13 @@ export default {
|
||||
el: {
|
||||
multiple: false,
|
||||
ajax: {
|
||||
url: '/api/v1/assets/system-users/?type=admin',
|
||||
url: '/api/v1/assets/admin-users/',
|
||||
transformOption: (item) => {
|
||||
const username = item.username || '*'
|
||||
return { label: item.name + '(' + username + ')', value: item.id }
|
||||
return { label: `${item.name}(${item.username})`, value: item.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
rules: [rules.RequiredChange]
|
||||
},
|
||||
nodes: {
|
||||
rules: [rules.RequiredChange],
|
||||
@@ -97,8 +97,7 @@ export default {
|
||||
type: 'switch'
|
||||
}
|
||||
},
|
||||
url: '/api/v1/assets/assets/',
|
||||
createSuccessNextRoute: { name: 'AssetDetail' }
|
||||
url: '/api/v1/assets/assets/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16">
|
||||
<AccountListTable ref="ListTable" :url="assetUserUrl" :has-import="false" :has-clone="false" />
|
||||
<AssetUserTable ref="ListTable" :url="assetUserUrl" :has-import="false" :has-clone="false" />
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<QuickActions type="primary" :actions="quickActions" />
|
||||
@@ -13,13 +13,12 @@
|
||||
|
||||
<script>
|
||||
import QuickActions from '@/components/QuickActions'
|
||||
import { AccountListTable } from '@/components'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import { AssetUserTable } from '@/components'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
AccountListTable,
|
||||
AssetUserTable,
|
||||
QuickActions
|
||||
},
|
||||
props: {
|
||||
@@ -30,7 +29,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
assetUserUrl: `/api/v1/assets/accounts/?asset=${this.object.id}`,
|
||||
assetUserUrl: `/api/v1/assets/asset-users/?asset_id=${this.object.id}&latest=1`,
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('assets.TestAssetsConnective'),
|
||||
@@ -41,10 +40,10 @@ export default {
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/accounts/tasks/?asset=${this.object.id}`,
|
||||
`/api/v1/assets/asset-users/tasks/?asset_id=${this.object.id}&latest=1`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
}
|
||||
)
|
||||
}.bind(this)
|
||||
@@ -17,7 +17,6 @@ import RelationCard from '@/components/RelationCard'
|
||||
import QuickActions from '@/components/QuickActions'
|
||||
import LabelCard from './components/LabelCard'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
@@ -68,7 +67,7 @@ export default {
|
||||
`/api/v1/assets/assets/${this.object.id}/tasks/`,
|
||||
{ action: 'refresh' }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
}
|
||||
)
|
||||
}.bind(this)
|
||||
@@ -86,12 +85,30 @@ export default {
|
||||
`/api/v1/assets/assets/${this.object.id}/tasks/`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
}
|
||||
)
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
// {
|
||||
// title: this.$t('assets.PushSystemUserNow'),
|
||||
// attrs: {
|
||||
// type: 'primary',
|
||||
// label: this.$t('assets.Push')
|
||||
// },
|
||||
// callbacks: {
|
||||
// click: function() {
|
||||
// this.$axios.post(
|
||||
// `api/v1/assets/system-users/${this.object.id}/tasks/`,
|
||||
// { action: 'push' }
|
||||
// ).then(res => {
|
||||
// window.open(`/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
// }
|
||||
// )
|
||||
// }.bind(this)
|
||||
// }
|
||||
// }
|
||||
],
|
||||
nodeRelationConfig: {
|
||||
icon: 'fa-info',
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<QuickActions type="primary" :actions="quickActions" />
|
||||
<RelationCard ref="systemUserRelation" style="margin-top: 15px" v-bind="systemUserRelationConfig" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QuickActions from '@/components/QuickActions/index'
|
||||
import RelationCard from '@/components/RelationCard'
|
||||
import ListTable from '@/components/ListTable'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
import { connectivityMeta } from '@/components/AccountListTable/const'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'SystemUserList',
|
||||
components: {
|
||||
QuickActions,
|
||||
RelationCard,
|
||||
ListTable
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
tableConfig: {
|
||||
url: `/api/v1/assets/system-users-assets-relations/?asset=${this.object.id}`,
|
||||
columns: ['systemuser_display', 'connectivity', 'actions'],
|
||||
columnsMeta: {
|
||||
systemuser_display: {
|
||||
label: this.$t('assets.SystemUser'),
|
||||
showOverflowTooltip: true,
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
name: 'SystemUserDetail',
|
||||
params: { id: row.systemuser }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
connectivity: connectivityMeta,
|
||||
actions: {
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
hasClone: false,
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'Test',
|
||||
title: this.$t('common.Test'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
const theUrl = `/api/v1/assets/system-users/${row.systemuser}/tasks/`
|
||||
const data = { action: 'test', assets: [this.object.id] }
|
||||
this.$axios.post(theUrl, data).then(resp => {
|
||||
openTaskPage(resp['task'])
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Push',
|
||||
title: this.$t('common.Push'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
const theUrl = `/api/v1/assets/system-users/${row.systemuser}/tasks/`
|
||||
const data = { action: 'push', assets: [this.object.id] }
|
||||
this.$axios.post(theUrl, data).then(resp => {
|
||||
openTaskPage(resp['task'])
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
title: this.$t('common.Delete'),
|
||||
type: 'danger',
|
||||
can: !this.$store.getters.currentOrgIsRoot,
|
||||
callback: (val) => {
|
||||
this.$axios.delete(`/api/v1/assets/system-users-assets-relations/${val.row.id}/`).then(() => {
|
||||
this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
this.$refs.ListTable.reloadTable()
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasBulkDelete: false,
|
||||
hasImport: false,
|
||||
hasCreate: false,
|
||||
extraMoreActions: [
|
||||
{
|
||||
title: this.$t('common.TestSelectedSystemUsersConnective'),
|
||||
name: 'TestSelected',
|
||||
can({ selectedRows }) {
|
||||
return selectedRows.length > 0
|
||||
},
|
||||
callback: this.bulkTestCallback.bind(this)
|
||||
},
|
||||
{
|
||||
title: this.$t('common.PushSelectedSystemUsersToAsset'),
|
||||
name: 'PushSelected',
|
||||
can({ selectedRows }) {
|
||||
return selectedRows.length > 0
|
||||
},
|
||||
callback: this.bulkPushCallback.bind(this)
|
||||
}
|
||||
]
|
||||
},
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('assets.TestAllSystemUsersConnective'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('common.Test')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
const theUrl = `/api/v1/assets/assets/${this.object.id}/tasks/`
|
||||
const data = { action: 'test_system_user' }
|
||||
this.$axios.post(theUrl, data).then(resp => {
|
||||
openTaskPage(resp['task'])
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.PushAllSystemUsersToAsset'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('common.Push')
|
||||
},
|
||||
callbacks: {
|
||||
click: function({ row }) {
|
||||
const theUrl = `/api/v1/assets/assets/${this.object.id}/tasks/`
|
||||
const data = { action: 'push_system_user' }
|
||||
this.$axios.post(theUrl, data).then(resp => {
|
||||
openTaskPage(resp['task'])
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
],
|
||||
systemUserRelationConfig: {
|
||||
icon: 'fa-link',
|
||||
type: 'info',
|
||||
title: this.$t('assets.AssociateSystemUsers'),
|
||||
objectsAjax: {
|
||||
url: `/api/v1/assets/system-users/`
|
||||
},
|
||||
performAdd: (items, that) => {
|
||||
const relationUrl = `/api/v1/assets/system-users-assets-relations/`
|
||||
const data = items.map((i) => {
|
||||
return {
|
||||
'asset': this.object.id,
|
||||
'systemuser': i.value
|
||||
}
|
||||
})
|
||||
if (data.length === 0) {
|
||||
return this.$message.error(this.$tc('assets.UnselectedSystemUsers'))
|
||||
}
|
||||
return this.$axios.post(relationUrl, data)
|
||||
},
|
||||
onAddSuccess: (items, that) => {
|
||||
vm.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
vm.$refs.ListTable.reloadTable()
|
||||
vm.$refs.systemUserRelation.$refs.select2.clearSelected()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
bulkPushCallback({ selectedRows }) {
|
||||
const theUrl = `/api/v1/assets/assets/${this.object.id}/tasks/`
|
||||
const systemUsers = selectedRows.map((v) => {
|
||||
return v.systemuser
|
||||
})
|
||||
const data = { action: 'push_system_user', system_users: systemUsers }
|
||||
this.$axios.post(theUrl, data).then(resp => {
|
||||
openTaskPage(resp['task'])
|
||||
})
|
||||
},
|
||||
bulkTestCallback({ selectedRows }) {
|
||||
const theUrl = `/api/v1/assets/assets/${this.object.id}/tasks/`
|
||||
const systemUsers = selectedRows.map((v) => {
|
||||
return v.systemuser
|
||||
})
|
||||
const data = { action: 'test_system_user', system_users: systemUsers }
|
||||
this.$axios.post(theUrl, data).then(resp => {
|
||||
openTaskPage(resp['task'])
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -9,17 +9,14 @@
|
||||
<script>
|
||||
import { GenericDetailPage, TabPage } from '@/layout/components'
|
||||
import Detail from './Detail.vue'
|
||||
import Account from './Account.vue'
|
||||
import SystemUserList from './SystemUser.vue'
|
||||
|
||||
import AssetUserList from './AssetUserList.vue'
|
||||
export default {
|
||||
name: 'AssetListDetail',
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
TabPage,
|
||||
Detail,
|
||||
Account,
|
||||
SystemUserList
|
||||
AssetUserList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -32,12 +29,8 @@ export default {
|
||||
name: 'Detail'
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.SystemUser'),
|
||||
name: 'SystemUserList'
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.AccountList'),
|
||||
name: 'Account'
|
||||
title: this.$t('assets.AssetUserList'),
|
||||
name: 'AssetUserList'
|
||||
}
|
||||
],
|
||||
hasRightSide: true,
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
<script>
|
||||
import GenericTreeListPage from '@/layout/components/GenericTreeListPage/index'
|
||||
import { DetailFormatter, ActionsFormatter } from '@/components/TableFormatters'
|
||||
import { DetailFormatter, ActionsFormatter, ChoicesFormatter } from '@/components/TableFormatters'
|
||||
import $ from '@/utils/jquery-vendor'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import TreeTable from '@/components/TreeTable'
|
||||
@@ -75,8 +75,6 @@ import { GenericUpdateFormDialog } from '@/layout/components'
|
||||
import rules from '@/components/DataForm/rules'
|
||||
import Protocols from '@/views/assets/Asset/components/Protocols/index'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { connectivityMeta } from '@/components/AccountListTable/const'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -103,16 +101,18 @@ export default {
|
||||
hasTree: true,
|
||||
columns: [
|
||||
'hostname', 'ip', 'public_ip', 'admin_user_display',
|
||||
'protocols', 'platform', 'hardware_info', 'model',
|
||||
'protocols',
|
||||
'platform', 'hardware_info', 'model',
|
||||
'cpu_model', 'cpu_cores', 'cpu_count', 'cpu_vcpus',
|
||||
'disk_info', 'disk_total', 'memory', 'os', 'os_arch',
|
||||
'os_version', 'number', 'vendor', 'sn',
|
||||
'disk_info', 'disk_total', 'memory',
|
||||
'os', 'os_arch', 'os_version',
|
||||
'number', 'vendor', 'sn',
|
||||
'connectivity',
|
||||
'created_by', 'date_created', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['hostname', 'ip', 'actions'],
|
||||
default: ['hostname', 'ip', 'platform', 'protocols', 'hardware_info', 'connectivity', 'actions']
|
||||
default: ['hostname', 'ip', 'hardware_info', 'connectivity', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
hostname: {
|
||||
@@ -120,11 +120,7 @@ export default {
|
||||
formatterArgs: {
|
||||
route: 'AssetDetail'
|
||||
},
|
||||
showOverflowTooltip: true,
|
||||
sortable: true
|
||||
},
|
||||
platform: {
|
||||
sortable: true
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
protocols: {
|
||||
formatter: function(row) {
|
||||
@@ -147,7 +143,26 @@ export default {
|
||||
comment: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
connectivity: connectivityMeta,
|
||||
connectivity: {
|
||||
label: this.$t('assets.Reachable'),
|
||||
formatter: ChoicesFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
0: 'fa-times text-danger',
|
||||
1: 'fa-check text-primary',
|
||||
2: 'fa-circle text-warning'
|
||||
},
|
||||
typeChange: function(val) {
|
||||
if (!val) {
|
||||
return 2
|
||||
}
|
||||
return val.status
|
||||
},
|
||||
hasTips: true
|
||||
},
|
||||
width: '90px',
|
||||
align: 'center'
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
@@ -177,7 +192,6 @@ export default {
|
||||
query: this.$route.query
|
||||
}
|
||||
},
|
||||
createInNewPage: true,
|
||||
searchConfig: {
|
||||
options: [
|
||||
{ label: this.$t('assets.Label'), value: 'label' }
|
||||
@@ -420,10 +434,10 @@ export default {
|
||||
return
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/nodes/${currentNode.meta.data.id}/tasks/`,
|
||||
`/api/v1/assets/nodes/${currentNode.meta.node.id}/tasks/`,
|
||||
{ 'action': 'refresh' }
|
||||
).then((res) => {
|
||||
openTaskPage(res['task'])
|
||||
window.open(`/core/ops/celery/task/${res.task}/log/`, '_blank', 'toolbar=yes, width=900, height=600')
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
|
||||
})
|
||||
@@ -435,10 +449,10 @@ export default {
|
||||
return
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/nodes/${currentNode.meta.data.id}/tasks/`,
|
||||
`/api/v1/assets/nodes/${currentNode.meta.node.id}/tasks/`,
|
||||
{ 'action': 'test' }
|
||||
).then((res) => {
|
||||
openTaskPage(res['task'])
|
||||
window.open(`/core/ops/celery/task/${res.task}/log/`, '_blank', 'toolbar=yes, width=900, height=600')
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
|
||||
})
|
||||
@@ -451,7 +465,7 @@ export default {
|
||||
}
|
||||
this.$cookie.set('show_current_asset', '1', 1)
|
||||
this.decorateRMenu()
|
||||
const url = `${this.treeSetting.url}?node_id=${currentNode.meta.data.id}&show_current_asset=1`
|
||||
const url = `${this.treeSetting.url}?node_id=${currentNode.meta.node.id}&show_current_asset=1`
|
||||
this.$refs.TreeList.$refs.TreeTable.handleUrlChange(url)
|
||||
},
|
||||
rMenuShowAssetAllChildrenNode: function() {
|
||||
@@ -462,7 +476,7 @@ export default {
|
||||
}
|
||||
this.$cookie.set('show_current_asset', '0', 1)
|
||||
this.decorateRMenu()
|
||||
const url = `${this.treeSetting.url}?node_id=${currentNode.meta.data.id}&show_current_asset=0`
|
||||
const url = `${this.treeSetting.url}?node_id=${currentNode.meta.node.id}&show_current_asset=0`
|
||||
this.$refs.TreeList.$refs.TreeTable.handleUrlChange(url)
|
||||
},
|
||||
rMenuShowNodeInfo: function() {
|
||||
@@ -472,7 +486,7 @@ export default {
|
||||
return
|
||||
}
|
||||
this.$axios.get(
|
||||
`/api/v1/assets/nodes/${currentNode.meta.data.id}/`
|
||||
`/api/v1/assets/nodes/${currentNode.meta.node.id}/`
|
||||
).then(res => {
|
||||
this.nodeInfoDialogSetting.dialogVisible = true
|
||||
this.nodeInfoDialogSetting.items = [
|
||||
@@ -489,7 +503,7 @@ export default {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/nodes/check_assets_amount_task/`
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$t('common.getErrorMsg' + ' ' + error))
|
||||
})
|
||||
@@ -512,9 +526,9 @@ export default {
|
||||
if (!currentNode || assetsSelected.length === 0) {
|
||||
return
|
||||
}
|
||||
let url = `/api/v1/assets/nodes/${currentNode.meta.data.id}/assets/add/`
|
||||
let url = `/api/v1/assets/nodes/${currentNode.meta.node.id}/assets/add/`
|
||||
if (this.assetTreeTableDialogSetting.action === 'move') {
|
||||
url = `/api/v1/assets/nodes/${currentNode.meta.data.id}/assets/replace/`
|
||||
url = `/api/v1/assets/nodes/${currentNode.meta.node.id}/assets/replace/`
|
||||
}
|
||||
this.$axios.put(
|
||||
url, { assets: assetsSelected }
|
||||
|
||||
@@ -33,8 +33,7 @@ export default {
|
||||
objectsAjax: {
|
||||
url: '/api/v1/assets/system-users/',
|
||||
transformOption: (item) => {
|
||||
const username = item.username || '*'
|
||||
return { label: item.name + '(' + username + ')', value: item.id }
|
||||
return { label: item.name + '(' + item.username + ')', value: item.id }
|
||||
}
|
||||
// processResults: (data) => {
|
||||
// let results = data.results
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage v-bind="$data" />
|
||||
<GenericCreateUpdatePage
|
||||
:fields="fields"
|
||||
:has-detail-in-msg="false"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
:get-next-route="getNextRoute"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -78,30 +85,23 @@ export default {
|
||||
params: {
|
||||
}
|
||||
},
|
||||
url: `/api/v1/assets/gateways/`,
|
||||
hasDetailInMsg: false,
|
||||
getNextRoute(res, method) {
|
||||
const domain = res.domain
|
||||
const route = {
|
||||
name: 'DomainDetail',
|
||||
params: {
|
||||
id: domain
|
||||
},
|
||||
query: {
|
||||
activeTab: 'GatewayList'
|
||||
}
|
||||
}
|
||||
return route
|
||||
},
|
||||
cleanFormValue(values) {
|
||||
if (this.$route.params.id && !values.update_password) {
|
||||
delete values['password']
|
||||
}
|
||||
return values
|
||||
}
|
||||
url: `/api/v1/assets/gateways/`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNextRoute(res, method) {
|
||||
const domain = res.domain
|
||||
const route = {
|
||||
name: 'DomainDetail',
|
||||
params: {
|
||||
id: domain
|
||||
},
|
||||
query: {
|
||||
activeTab: 'GatewayList'
|
||||
}
|
||||
}
|
||||
return route
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
:fields="fields"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import getFields from '../fields'
|
||||
import getFields from './fields'
|
||||
|
||||
export default {
|
||||
name: 'CommonUserDatabase',
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
const fields = getFields.bind(this)()
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import { Select2, UploadKey } from '@/components'
|
||||
|
||||
function getFields() {
|
||||
const login_mode = {
|
||||
helpText: i18n.t('assets.LoginModeHelpMessage'),
|
||||
on: {
|
||||
input: ([value], updateForm) => {
|
||||
if (value === 'manual') {
|
||||
@@ -24,14 +26,12 @@ function getFields() {
|
||||
},
|
||||
rules: [Object.assign({}, Required)],
|
||||
hidden: (form) => {
|
||||
if (['vnc'].includes(form.protocol)) {
|
||||
this.fieldsMeta.username.rules[0].required = false
|
||||
} else if (form.login_mode === 'manual') {
|
||||
this.fieldsMeta.username.rules[0].required = false
|
||||
} else if (form.username_same_with_user) {
|
||||
if (['mysql', 'postgresql', 'mariadb', 'oracle'].includes(form.protocol)) {
|
||||
this.fieldsMeta.username.rules[0].required = true
|
||||
} else if (['vnc'].includes(form.protocol)) {
|
||||
this.fieldsMeta.username.rules[0].required = false
|
||||
} else {
|
||||
this.fieldsMeta.username.rules[0].required = true
|
||||
this.fieldsMeta.username.rules[0].required = !(form.login_mode === 'manual' || form.username_same_with_user)
|
||||
}
|
||||
if (form.username_same_with_user) {
|
||||
this.fieldsMeta.username.el.disabled = true
|
||||
@@ -54,7 +54,6 @@ function getFields() {
|
||||
|
||||
const username_same_with_user = {
|
||||
type: 'switch',
|
||||
label: this.$t('assets.DynamicUsername'),
|
||||
helpText: this.$t('assets.UsernameHelpMessage'),
|
||||
el: {
|
||||
disabled: false
|
||||
@@ -78,10 +77,6 @@ function getFields() {
|
||||
if (form.protocol === 'k8s') {
|
||||
return true
|
||||
}
|
||||
if (form.type === 'admin') {
|
||||
form.auto_generate_key = false
|
||||
return true
|
||||
}
|
||||
|
||||
if (form.login_mode === 'manual') {
|
||||
this.fieldsMeta.auto_generate_key.el.disabled = true
|
||||
@@ -119,7 +114,7 @@ function getFields() {
|
||||
disabled: false
|
||||
},
|
||||
hidden: form => {
|
||||
if (form.login_mode === 'manual' || form.type === 'admin') {
|
||||
if (form.login_mode === 'manual') {
|
||||
this.fieldsMeta.auto_push.el.disabled = true
|
||||
} else {
|
||||
this.fieldsMeta.auto_push.el.disabled = false
|
||||
@@ -167,9 +162,6 @@ function getFields() {
|
||||
helpText: this.$t('assets.GroupsHelpMessage')
|
||||
}
|
||||
|
||||
const type = {
|
||||
}
|
||||
|
||||
return {
|
||||
login_mode: login_mode,
|
||||
username: username,
|
||||
@@ -181,8 +173,7 @@ function getFields() {
|
||||
auto_push: auto_push,
|
||||
update_password: update_password,
|
||||
password: password,
|
||||
system_groups: system_groups,
|
||||
type: type
|
||||
system_groups: system_groups
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -13,7 +12,7 @@ import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
|
||||
export default {
|
||||
name: 'CommonUserSSH',
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
return {
|
||||
@@ -21,9 +20,9 @@ export default {
|
||||
protocol: this.$route.query.protocol
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol']],
|
||||
[this.$t('common.Basic'), ['name', 'priority', 'protocol']],
|
||||
[this.$t('common.Auth'), ['token']],
|
||||
[this.$t('common.Other'), ['priority', 'comment']]
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
token: {
|
||||
@@ -1,19 +1,13 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
:fields="fields"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import getFields from '../fields'
|
||||
import getFields from './fields'
|
||||
|
||||
export default {
|
||||
name: 'CommonUserRDP',
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
const fields = getFields.bind(this)()
|
||||
@@ -24,14 +18,15 @@ export default {
|
||||
username_same_with_user: false,
|
||||
auto_generate_key: false,
|
||||
auto_push: false,
|
||||
sftp_root: 'tmp',
|
||||
sudo: '/bin/whoami',
|
||||
shell: '/bin/bash'
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol', 'username', 'username_same_with_user']],
|
||||
[this.$t('common.Auth'), ['login_mode', 'update_password', 'password', 'ad_domain']],
|
||||
[this.$t('common.Basic'), ['name', 'login_mode', 'username', 'username_same_with_user', 'priority', 'protocol']],
|
||||
[this.$t('assets.AutoPush'), ['auto_push', 'system_groups']],
|
||||
[this.$t('common.Other'), ['priority', 'comment']]
|
||||
[this.$t('common.Auth'), ['update_password', 'password', 'ad_domain']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
login_mode: fields.login_mode,
|
||||
@@ -1,20 +1,14 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
:fields="fields"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
import getFields from '../fields'
|
||||
import getFields from './fields'
|
||||
|
||||
export default {
|
||||
name: 'CommonUserSSH',
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
const fields = getFields.bind(this)()
|
||||
@@ -30,11 +24,11 @@ export default {
|
||||
shell: '/bin/bash'
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol', 'username', 'username_same_with_user']],
|
||||
[this.$t('common.Auth'), ['login_mode', 'auto_generate_key', 'update_password', 'password', 'private_key']],
|
||||
[this.$t('common.Basic'), ['name', 'login_mode', 'username', 'username_same_with_user', 'priority', 'protocol']],
|
||||
[this.$t('assets.AutoPush'), ['auto_push', 'sudo', 'shell', 'home', 'system_groups']],
|
||||
[this.$t('common.Auth'), ['auto_generate_key', 'update_password', 'password', 'private_key']],
|
||||
[this.$t('common.Command filter'), ['cmd_filters']],
|
||||
[this.$t('common.Other'), ['priority', 'sftp_root', 'comment']]
|
||||
[this.$t('common.Other'), ['sftp_root', 'comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
login_mode: fields.login_mode,
|
||||
@@ -1,19 +1,15 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
:fields="fields"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import getFields from '../fields'
|
||||
import getFields from '@/views/assets/SystemUser/SystemUserCreate/fields'
|
||||
|
||||
// const asciiProtocols = ['ssh', 'telnet', 'mysql']
|
||||
|
||||
export default {
|
||||
name: 'CommonUserVNC',
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
const fields = getFields.bind(this)()
|
||||
@@ -24,9 +20,9 @@ export default {
|
||||
username_same_with_user: false
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol', 'username', 'username_same_with_user']],
|
||||
[this.$t('common.Auth'), ['login_mode', 'update_password', 'password']],
|
||||
[this.$t('common.Other'), ['priority', 'comment']]
|
||||
[this.$t('common.Basic'), ['name', 'login_mode', 'username', 'username_same_with_user', 'priority', 'protocol']],
|
||||
[this.$t('common.Auth'), ['update_password', 'password']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
login_mode: fields.login_mode,
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user