mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-14 03:46:26 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cbafdf6b7 | ||
|
|
65eda50419 | ||
|
|
9736d94762 |
@@ -15,9 +15,7 @@
|
||||
"test:ci": "npm run lint && npm run test:unit",
|
||||
"svgo": "svgo -f src/icons/svg --config=src/icas/svgo.yml",
|
||||
"vue-i18n-extract": "vue-i18n-extract",
|
||||
"vue-i18n-report": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json'",
|
||||
"vue-i18n-report-json": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -o /tmp/abc.json",
|
||||
"vue-i18n-report-add-miss": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -a"
|
||||
"vue-i18n-report": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
||||
@@ -31,7 +29,6 @@
|
||||
"install": "^0.13.0",
|
||||
"jquery": "^3.5.0",
|
||||
"js-cookie": "2.2.0",
|
||||
"krry-transfer": "^1.7.3",
|
||||
"less": "^3.10.3",
|
||||
"less-loader": "^5.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
@@ -46,7 +43,6 @@
|
||||
"lodash.set": "^4.3.2",
|
||||
"lodash.topairs": "^4.3.0",
|
||||
"lodash.values": "^4.3.0",
|
||||
"moment": "^2.29.1",
|
||||
"moment-parseformat": "^3.0.0",
|
||||
"normalize.css": "7.0.0",
|
||||
"npm": "^7.8.0",
|
||||
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
@@ -8,6 +8,7 @@
|
||||
<meta http-equiv="Cache-control" content="no-cache">
|
||||
<meta http-equiv="Cache" content="no-cache">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= webpackConfig.name %></title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -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}/`,
|
||||
|
||||
BIN
src/assets/img/logo-text.png
Normal file
BIN
src/assets/img/logo-text.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
BIN
src/assets/img/logo.png
Normal file
BIN
src/assets/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -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,91 +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 => {
|
||||
this.$message.error(this.$tc('common.updateErrorMsg' + ' ' + err))
|
||||
this.$emit('update:visible', false)
|
||||
})
|
||||
},
|
||||
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.tc('assets.Reachable'),
|
||||
'failed': i18n.tc('assets.Unreachable'),
|
||||
'unknown': i18n.tc('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,180 +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'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
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: true
|
||||
}
|
||||
},
|
||||
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 => {
|
||||
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({ row }) {
|
||||
this.account = row
|
||||
this.showUpdateSecretDialog = true
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: this.hasLeftActions,
|
||||
hasMoreActions: false,
|
||||
hasImport: this.hasImport,
|
||||
hasExport: this.hasExport,
|
||||
exportOptions: {
|
||||
url: this.exportUrl,
|
||||
mfaVerifyRequired: true
|
||||
},
|
||||
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>
|
||||
@@ -23,8 +23,8 @@
|
||||
|
||||
<script>
|
||||
import TreeTable from '@/components/TreeTable'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
import Select2 from '@/components/FormFields/Select2'
|
||||
import { DetailFormatter } from '@/components/ListTable/formatters'
|
||||
import Select2 from '@/components/Select2'
|
||||
import Dialog from '@/components/Dialog'
|
||||
|
||||
export default {
|
||||
|
||||
371
src/components/AssetUserTable/index.vue
Normal file
371
src/components/AssetUserTable/index.vue
Normal file
@@ -0,0 +1,371 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :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>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { ActionsFormatter, DateFormatter } from '@/components/ListTable/formatters'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
ListTable,
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: 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
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
MFAConfirmed: false,
|
||||
MFAInput: '',
|
||||
MFAInfo: {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: ''
|
||||
},
|
||||
showDialog: false,
|
||||
showMFADialog: false,
|
||||
dialogInfo: {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: '',
|
||||
private_key: ''
|
||||
},
|
||||
tableConfig: {
|
||||
url: this.url,
|
||||
columns: [
|
||||
{
|
||||
prop: 'hostname',
|
||||
label: this.$t('assets.Hostname'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'ip',
|
||||
label: this.$t('assets.ip'),
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
prop: 'username',
|
||||
label: this.$t('assets.Username'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'version',
|
||||
label: this.$t('assets.Version'),
|
||||
width: '70px'
|
||||
},
|
||||
{
|
||||
prop: 'date_created',
|
||||
label: this.$t('assets.date_joined'),
|
||||
formatter: DateFormatter
|
||||
},
|
||||
{
|
||||
prop: 'id',
|
||||
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.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: {
|
||||
latest: 1
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: this.hasLeftActions,
|
||||
hasMoreActions: false,
|
||||
hasImport: this.hasImport,
|
||||
hasExport: this.hasExport,
|
||||
hasSearch: true,
|
||||
searchConfig: {
|
||||
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)
|
||||
}
|
||||
},
|
||||
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)
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.handleExport) {
|
||||
this.headerActions.handleExport = this.handleExport
|
||||
}
|
||||
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')
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -9,7 +9,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataForm from '@/components/DataForm'
|
||||
import { DataForm } from '@/components'
|
||||
|
||||
export default {
|
||||
name: 'NestedField',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
import Select2 from '@/components/FormFields/Select2'
|
||||
import Select2 from '@/components/Select2'
|
||||
import NestedField from '@/components/AutoDataForm/components/NestedField'
|
||||
import rules from '@/components/DataForm/rules'
|
||||
import { assignIfNot } from '@/utils/common'
|
||||
@@ -124,7 +124,7 @@ export class FormFieldGenerator {
|
||||
field.el = el
|
||||
field.rules = rules
|
||||
_.set(field, 'attrs.error', '')
|
||||
// Vue.$log.debug('Generate field: ', name, field)
|
||||
Vue.$log.debug('Generate field: ', name, field)
|
||||
return field
|
||||
}
|
||||
generateFieldGroup(field, fieldsMeta, remoteFieldsMeta) {
|
||||
|
||||
@@ -58,10 +58,6 @@ export default {
|
||||
minColumns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -71,17 +67,15 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('showColumnSettingPopover', ({ url }) => {
|
||||
if (url === this.url) {
|
||||
this.showColumnSettingPopover = true
|
||||
this.iCurrentColumns = this.currentColumns
|
||||
}
|
||||
this.$eventBus.$on('showColumnSettingPopover', () => {
|
||||
this.showColumnSettingPopover = true
|
||||
this.iCurrentColumns = this.currentColumns
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
handleColumnConfirm() {
|
||||
this.showColumnSettingPopover = false
|
||||
this.$emit('columnsUpdate', { columns: this.iCurrentColumns, url: this.url })
|
||||
this.$emit('columnsUpdate', this.iCurrentColumns)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
:current-columns="popoverColumns.currentCols"
|
||||
:total-columns-list="popoverColumns.totalColumnsList"
|
||||
:min-columns="popoverColumns.minCols"
|
||||
:url="config.url"
|
||||
@columnsUpdate="handlePopoverColumnsChange"
|
||||
/>
|
||||
</div>
|
||||
@@ -13,10 +12,9 @@
|
||||
|
||||
<script type="text/jsx">
|
||||
import DataTable from '../DataTable'
|
||||
import { DateFormatter, DetailFormatter, DisplayFormatter, BooleanFormatter, ActionsFormatter } from '@/components/TableFormatters'
|
||||
import { DateFormatter, DetailFormatter, DisplayFormatter, BooleanFormatter, ActionsFormatter } from '@/components/ListTable/formatters'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import ColumnSettingPopover from './components/ColumnSettingPopover'
|
||||
import { newURL } from '@/utils/common'
|
||||
export default {
|
||||
name: 'AutoDataTable',
|
||||
components: {
|
||||
@@ -170,12 +168,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,15 +221,14 @@ 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)
|
||||
|
||||
// 应该显示的列
|
||||
const _tableConfig = localStorage.getItem('tableConfig')
|
||||
? JSON.parse(localStorage.getItem('tableConfig'))
|
||||
: {}
|
||||
const tableName = this.config.name || this.$route.name + '_' + newURL(this.iConfig.url).pathname
|
||||
const configShowColumnsNames = _.get(_tableConfig[tableName], 'showColumns', null)
|
||||
const configShowColumnsNames = _.get(_tableConfig[this.$route.name], 'showColumns', null)
|
||||
let showColumnsNames = configShowColumnsNames || defaultColumnsNames
|
||||
if (showColumnsNames.length === 0) {
|
||||
showColumnsNames = totalColumnsNames
|
||||
@@ -251,7 +248,7 @@ export default {
|
||||
min: minColumnsNames,
|
||||
configShow: configShowColumnsNames
|
||||
}
|
||||
this.$log.debug('Cleaned columns show: ', this.cleanedColumnsShow)
|
||||
this.$log.debug('Cleaned colums show: ', this.cleanedColumnsShow)
|
||||
},
|
||||
filterShowColumns() {
|
||||
this.cleanColumnsShow()
|
||||
@@ -267,14 +264,13 @@ export default {
|
||||
this.popoverColumns.minCols = this.cleanedColumnsShow.min
|
||||
this.$log.debug('Popover cols: ', this.popoverColumns)
|
||||
},
|
||||
handlePopoverColumnsChange({ columns, url }) {
|
||||
this.$log.debug('Columns change: ', columns)
|
||||
handlePopoverColumnsChange(columns) {
|
||||
// this.$log.debug('Columns change: ', columns)
|
||||
this.popoverColumns.currentCols = columns
|
||||
const _tableConfig = localStorage.getItem('tableConfig')
|
||||
? JSON.parse(localStorage.getItem('tableConfig'))
|
||||
: {}
|
||||
const tableName = this.config.name || this.$route.name + '_' + newURL(url).pathname
|
||||
_tableConfig[tableName] = {
|
||||
_tableConfig[this.$route.name] = {
|
||||
'showColumns': columns
|
||||
}
|
||||
localStorage.setItem('tableConfig', JSON.stringify(_tableConfig))
|
||||
|
||||
@@ -37,13 +37,15 @@
|
||||
class="action-item"
|
||||
@click="handleClick(action)"
|
||||
>
|
||||
<el-tooltip :disabled="!action.tip" :content="action.tip" placement="top">
|
||||
<span>
|
||||
<i v-if="action.fa" :class="'fa ' + action.fa" />{{ action.title }}
|
||||
</span>
|
||||
<el-tooltip v-if="action.tip" effect="dark" :content="action.tip" placement="top">
|
||||
<i v-if="action.fa" :class="'fa ' + action.fa" />{{ action.title }}
|
||||
</el-tooltip>
|
||||
<span v-else>
|
||||
<i v-if="action.fa" :class="'fa ' + action.fa" />{{ action.title }}
|
||||
</span>
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -19,16 +19,3 @@ export default {
|
||||
RequiredChange,
|
||||
EmailCheck
|
||||
}
|
||||
|
||||
export const JsonRequired = {
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
validator: (rule, value, callback) => {
|
||||
try {
|
||||
JSON.parse(value)
|
||||
callback()
|
||||
} catch (e) {
|
||||
callback(new Error(i18n.t('common.InvalidJson')))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,6 +426,7 @@ export default {
|
||||
onEdit: {
|
||||
type: Function,
|
||||
default(row) {
|
||||
// console.log('On delete row')
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -861,9 +862,6 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.totalData) {
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getQuery() {
|
||||
@@ -927,9 +925,6 @@ export default {
|
||||
if (!this.hasPagination) {
|
||||
this.data = this.totalData
|
||||
this.loading = false
|
||||
if (this.isTree) {
|
||||
this.data = this.tree2Array(this.data, this.expandAll)
|
||||
}
|
||||
return this.data
|
||||
}
|
||||
// page
|
||||
@@ -940,7 +935,6 @@ export default {
|
||||
this.$log.debug(`page: ${page}, size: ${this.size}, start: ${start}, end: ${end}`)
|
||||
this.data = this.totalData.slice(start, end)
|
||||
this.loading = false
|
||||
this.data = this.tree2Array(this.data, this.expandAll)
|
||||
return this.data
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -74,7 +74,7 @@ export default {
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
padding-right: 20px;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<el-link @click="onClick">
|
||||
{{ title }}
|
||||
</el-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Link',
|
||||
props: {
|
||||
href: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
window.open(this.href)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,250 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="hours-container">
|
||||
<div v-for="(item, index) in hours" :key="index" class="hours-item">
|
||||
<div class="hours-item-header">{{ compItem(item) }}</div>
|
||||
<div class="hours-item-value">
|
||||
<div
|
||||
:class="compClass(2 * item)"
|
||||
@click="handleClick(2 * item)"
|
||||
@mouseover="handleHover(2 * item)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tips">{{ tips }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
model: {
|
||||
prop: 'sendTimeList'
|
||||
},
|
||||
props: {
|
||||
sendTimeList: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => []
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hours: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23], // 选项
|
||||
selectStart: false, // 开始
|
||||
startIndex: '', // 开始下标
|
||||
timeRangeList: [], // 选择的时间段
|
||||
timeRangeListIndex: [], // 选中的下标
|
||||
tempRangeIndex: [], // 预选下标
|
||||
tips: '向右选中,向左取消选择'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
watch: {
|
||||
timeRangeList: function(value) {
|
||||
this.$emit('change', value)
|
||||
this.$parent.$emit('el.form.change')// 触发父组件的校验规则
|
||||
},
|
||||
sendTimeList: {
|
||||
handler() {
|
||||
this.transformedIndex()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.transformedIndex()
|
||||
},
|
||||
methods: {
|
||||
// 时间区间转换成下标区间
|
||||
transformedIndex() {
|
||||
this.timeRangeListIndex = []
|
||||
this.timeRangeList = this.sendTimeList
|
||||
this.timeRangeList.forEach(element => {
|
||||
const [startTime, endTime] = element.match(/\d+\:\d+/g)
|
||||
if (startTime && endTime) {
|
||||
const [startHour, startMin] = startTime.split(':')
|
||||
const [endHour, endMin] = endTime.split(':')
|
||||
if (startHour && startMin && endHour && endMin) {
|
||||
let startNum, endNum
|
||||
if (startMin === '00') {
|
||||
startNum = 2 * parseInt(startHour)
|
||||
} else {
|
||||
startNum = 2 * parseInt(startHour) + 1
|
||||
}
|
||||
if (endMin === '00') {
|
||||
endNum = 2 * parseInt(endHour) - 1
|
||||
} else {
|
||||
endNum = 2 * parseInt(endHour)
|
||||
}
|
||||
while (endNum >= startNum) {
|
||||
this.timeRangeListIndex.push(startNum)
|
||||
startNum++
|
||||
}
|
||||
} else {
|
||||
this.$message.error('时间段格式不正确')
|
||||
}
|
||||
} else {
|
||||
this.$message.error('没有拿到开始时间或结束时间或者时间段格式不对')
|
||||
}
|
||||
})
|
||||
this.tips = this.timeRangeList && this.timeRangeList.length > 0 ? this.timeRangeList : '向右选中,向左取消选择'
|
||||
},
|
||||
// 下标区间转换成时间区间
|
||||
transformedSection() {
|
||||
this.timeRangeList = []
|
||||
let startTime = ''; let endTime = ''; const len = this.hours.length
|
||||
for (let index = this.hours[0] * 2; index < 2 * (len + 1); index++) {
|
||||
if (this.timeRangeListIndex.indexOf(index) > -1) {
|
||||
if (startTime) { // 如果有开始时间,直接确定结束时间
|
||||
const endHour = Math.floor((index + 1) / 2)
|
||||
const endMin = (index + 1) % 2 === 0 ? '00' : '30'
|
||||
endTime = `${endHour < 10 ? '0' + endHour : endHour}:${endMin}`
|
||||
} else { // 没有开始时间,确定当前点为开始时间
|
||||
const startHour = Math.floor(index / 2)
|
||||
const startMin = index % 2 === 0 ? '00' : '30'
|
||||
startTime = `${startHour < 10 ? '0' + startHour : startHour}:${startMin}`
|
||||
}
|
||||
if (index === 2 * this.hours.length + 1) { // 如果是最后一格,直接结束
|
||||
endTime = `${Math.floor((index + 1) / 2)}:00`
|
||||
this.timeRangeList.push(`${startTime || '23:30'}-${endTime}`)
|
||||
startTime = ''
|
||||
endTime = ''
|
||||
}
|
||||
} else { // 若这个点不在选择区间,确定一个时间段
|
||||
if (startTime && endTime) {
|
||||
this.timeRangeList.push(`${startTime}-${endTime}`)
|
||||
startTime = ''
|
||||
endTime = ''
|
||||
} else if (startTime && !endTime) { // 这里可能只选半个小时
|
||||
const endHour = Math.floor(index / 2)
|
||||
const endMin = index % 2 === 0 ? '00' : '30'
|
||||
endTime = `${endHour < 10 ? '0' + endHour : endHour}:${endMin}`
|
||||
this.timeRangeList.push(`${startTime}-${endTime}`)
|
||||
startTime = ''
|
||||
endTime = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
this.tips = this.timeRangeList && this.timeRangeList.length > 0 ? this.timeRangeList : '向右选中,向左取消选择'
|
||||
},
|
||||
// 点击事件
|
||||
handleClick(index) {
|
||||
if (this.selectStart) {
|
||||
if (index === this.startIndex) { // 双击取反
|
||||
if (this.timeRangeListIndex.indexOf(index) > -1) {
|
||||
this.timeRangeListIndex.splice(this.timeRangeListIndex.indexOf(index), 1)
|
||||
} else {
|
||||
this.timeRangeListIndex.push(this.startIndex)
|
||||
}
|
||||
} else if (index > this.startIndex) { // 选取数据--向右添加,向左取消
|
||||
while (index >= this.startIndex) {
|
||||
this.timeRangeListIndex.push(this.startIndex)
|
||||
this.startIndex++
|
||||
}
|
||||
this.timeRangeListIndex = Array.from(new Set(this.timeRangeListIndex))
|
||||
} else { // 删除数据
|
||||
while (this.startIndex >= index) {
|
||||
if (this.timeRangeListIndex.indexOf(index) > -1) {
|
||||
this.timeRangeListIndex.splice(this.timeRangeListIndex.indexOf(index), 1)
|
||||
}
|
||||
index++
|
||||
}
|
||||
}
|
||||
this.startIndex = ''
|
||||
this.tempRangeIndex = []
|
||||
this.transformedSection()
|
||||
} else {
|
||||
this.startIndex = index
|
||||
}
|
||||
this.selectStart = !this.selectStart
|
||||
},
|
||||
// 预选区间
|
||||
handleHover(index) {
|
||||
if (this.selectStart) {
|
||||
this.tempRangeIndex = []
|
||||
if (index > this.startIndex) { // 选取数据--向右添加,向左取消
|
||||
while (index >= this.startIndex) {
|
||||
this.tempRangeIndex.push(index)
|
||||
index--
|
||||
}
|
||||
} else { // 删除数据
|
||||
while (this.startIndex >= index) {
|
||||
this.tempRangeIndex.push(index)
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 是否选中,计算className
|
||||
compClass(index) {
|
||||
if (index === this.startIndex) {
|
||||
return 'hours-item-left preSelected'
|
||||
}
|
||||
if (index >= this.startIndex) {
|
||||
if (this.tempRangeIndex.indexOf(index) > -1) {
|
||||
return 'hours-item-left preSelected'
|
||||
}
|
||||
} else {
|
||||
if (this.tempRangeIndex.indexOf(index) > -1) {
|
||||
return 'hours-item-left unSelected'
|
||||
}
|
||||
}
|
||||
return this.timeRangeListIndex.indexOf(index) > -1 ? 'hours-item-left selected' : 'hours-item-left'
|
||||
},
|
||||
compItem(item) { // 不足10前面补0
|
||||
return item < 10 ? `0${item}` : item
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.hours-container {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
.hours-item {
|
||||
width: 30px;
|
||||
height: 60px;
|
||||
border: 1px solid #c2d0f3;
|
||||
border-right: none;
|
||||
text-align: center;
|
||||
&:last-child {
|
||||
border-right: 1px solid #c2d0f3;
|
||||
}
|
||||
.hours-item-header {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-bottom: 1px solid #c2d0f3;
|
||||
}
|
||||
.hours-item-value {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
}
|
||||
.selected {
|
||||
background-color: #4e84fe;
|
||||
border-bottom: 1px solid #c2d0f3;
|
||||
}
|
||||
.preSelected {
|
||||
background-color: #8eaffc;
|
||||
border-bottom: 1px solid #c2d0f3;
|
||||
}
|
||||
.unSelected {
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #c2d0f3;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tips {
|
||||
width: 100%;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,33 +0,0 @@
|
||||
import DatetimeRangePicker from './DatetimeRangePicker'
|
||||
import Link from './Link'
|
||||
import PasswordInput from './PasswordInput'
|
||||
import Select2 from './Select2'
|
||||
import Swicher from './Swicher'
|
||||
import UploadField from './UploadField'
|
||||
import UploadKey from './UploadKey'
|
||||
import UserPassword from './UserPassword'
|
||||
import WeekCronSelect from './WeekCronSelect'
|
||||
|
||||
export default {
|
||||
DatetimeRangePicker,
|
||||
Link,
|
||||
PasswordInput,
|
||||
Select2,
|
||||
Swicher,
|
||||
UploadKey,
|
||||
UploadField,
|
||||
UserPassword,
|
||||
WeekCronSelect
|
||||
}
|
||||
|
||||
export {
|
||||
DatetimeRangePicker,
|
||||
Link,
|
||||
PasswordInput,
|
||||
Select2,
|
||||
Swicher,
|
||||
UploadKey,
|
||||
UploadField,
|
||||
UserPassword,
|
||||
WeekCronSelect
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import { DetailFormatter, SystemUserFormatter } from '@/components/TableFormatters'
|
||||
import { DetailFormatter, SystemUserFormatter } from '@/components/ListTable/formatters'
|
||||
import TreeTable from '../TreeTable'
|
||||
|
||||
export default {
|
||||
|
||||
@@ -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,15 +32,7 @@ export default {
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
beforeExport: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
mfaVerifyRequired: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: () => ''
|
||||
},
|
||||
performExport: {
|
||||
type: Function,
|
||||
@@ -79,12 +55,10 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
exportDialogShow: false,
|
||||
showExportDialog: false,
|
||||
exportOption: 'all',
|
||||
exportTypeOption: 'csv',
|
||||
meta: {},
|
||||
mfaVerified: false,
|
||||
mfaDialogShow: false
|
||||
meta: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -141,26 +115,11 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('showExportDialog', ({ selectedRows, url, name }) => {
|
||||
// Todo: 没有时间了,只能先这么处理了
|
||||
if (url === this.url || url.indexOf('account') > -1) {
|
||||
this.showExportDialog()
|
||||
}
|
||||
this.$eventBus.$on('showExportDialog', (row) => {
|
||||
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
|
||||
@@ -178,6 +137,11 @@ 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'] = this.exportTypeOption
|
||||
const queryStr =
|
||||
@@ -190,17 +154,14 @@ export default {
|
||||
const query = listTableRef.dataTable.getQuery()
|
||||
delete query['limit']
|
||||
delete query['offset']
|
||||
await this.beforeExport()
|
||||
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() {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
:loading-status="loadStatus"
|
||||
width="80%"
|
||||
class="importDialog"
|
||||
:confirm-title="confirmTitle"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
@close="handleImportCancel"
|
||||
@@ -117,6 +118,9 @@ export default {
|
||||
} else {
|
||||
return this.$t('common.Import') + this.$t('common.Update')
|
||||
}
|
||||
},
|
||||
confirmTitle() {
|
||||
return '导入'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -125,10 +129,8 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('showImportDialog', ({ url }) => {
|
||||
if (url === this.url) {
|
||||
this.showImportDialog = true
|
||||
}
|
||||
this.$eventBus.$on('showImportDialog', (row) => {
|
||||
this.showImportDialog = true
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
|
||||
<script>
|
||||
import DataTable from '@/components/DataTable'
|
||||
import { sleep, getUpdateObjURL } from '@/utils/common'
|
||||
import { EditableInputFormatter, StatusFormatter } from '@/components/TableFormatters'
|
||||
import { sleep } from '@/utils/common'
|
||||
import { EditableInputFormatter, StatusFormatter } from '@/components/ListTable/formatters'
|
||||
export default {
|
||||
name: 'ImportTable',
|
||||
components: {
|
||||
@@ -207,7 +207,7 @@ export default {
|
||||
if (!d) {
|
||||
return 0
|
||||
}
|
||||
if (typeof itemColData !== 'number' && (!itemColData || !itemColData.length)) {
|
||||
if (!itemColData || !itemColData.length) {
|
||||
return 0
|
||||
}
|
||||
return itemColData.length
|
||||
@@ -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,
|
||||
|
||||
@@ -32,7 +32,7 @@ export default {
|
||||
default: ''
|
||||
},
|
||||
createRoute: {
|
||||
type: [String, Object, Function],
|
||||
type: [String, Object],
|
||||
default: function() {
|
||||
return this.$route.name.replace('List', 'Create')
|
||||
}
|
||||
@@ -149,13 +149,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleCreate() {
|
||||
let route
|
||||
let route = {}
|
||||
if (typeof this.createRoute === 'string') {
|
||||
route = { name: this.createRoute }
|
||||
route.name = this.createRoute
|
||||
} else if (typeof this.createRoute === 'function') {
|
||||
route = this.createRoute()
|
||||
} else if (typeof this.createRoute === 'object') {
|
||||
} else {
|
||||
route = this.createRoute
|
||||
}
|
||||
this.$router.push(route)
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
<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 })
|
||||
}
|
||||
},
|
||||
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 })
|
||||
}
|
||||
},
|
||||
hasColumnSetting: defaultTrue,
|
||||
handleTableSettingClick: {
|
||||
handleColumnConfig: {
|
||||
type: Function,
|
||||
default: function({ selectedRows }) {
|
||||
this.$eventBus.$emit('showColumnSettingPopover', { url: this.tableUrl, row: selectedRows, name: this.name })
|
||||
default: function() {
|
||||
this.$eventBus.$emit('showColumnSettingPopover')
|
||||
}
|
||||
},
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<script>
|
||||
import AutoDataSearch from '@/components/AutoDataSearch'
|
||||
import LeftSide from './LeftSide'
|
||||
import DatetimeRangePicker from '@/components/FormFields/DatetimeRangePicker'
|
||||
import DatetimeRangePicker from '@/components/DatetimeRangePicker'
|
||||
import RightSide from './RightSide'
|
||||
|
||||
const defaultTrue = { type: Boolean, default: true }
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
<template>
|
||||
<ActionsGroup v-loading="loadingStatus" :size="'mini'" :actions="actions" :more-actions="moreActions" :more-actions-title="moreActionsTitle" />
|
||||
<ActionsGroup :size="'mini'" :actions="actions" :more-actions="moreActions" :more-actions-title="moreActionsTitle" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionsGroup from '@/components/ActionsGroup'
|
||||
import ActionsGroup from '@/components/ActionsGroup/index'
|
||||
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 }}
|
||||
@@ -147,12 +144,10 @@ export default {
|
||||
let actions = [...this.defaultActions, ...this.extraActions]
|
||||
actions = _.cloneDeep(actions)
|
||||
actions = actions.map((v) => {
|
||||
v.has = this.cleanBoolean(v, 'has', true)
|
||||
v.can = this.cleanBoolean(v, 'can', true)
|
||||
v.callback = this.cleanCallback(v, 'callback')
|
||||
v.fa = this.cleanValue(v, 'fa')
|
||||
v.has = this.cleanBoolean(v, 'has')
|
||||
v.can = this.cleanBoolean(v, 'can')
|
||||
v.callback = this.cleanCallback(v)
|
||||
v.order = v.order || 100
|
||||
v.tip = this.cleanValue(v, 'tip')
|
||||
return v
|
||||
})
|
||||
actions = actions.filter((v) => v.has)
|
||||
@@ -170,21 +165,18 @@ export default {
|
||||
return []
|
||||
}
|
||||
return this.cleanedActions.slice(1, this.cleanedActions.length)
|
||||
},
|
||||
loadingStatus() {
|
||||
return this.col.formatterArgs.loading
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cleanBoolean(item, attr, defaults) {
|
||||
cleanBoolean(item, attr) {
|
||||
const ok = item[attr]
|
||||
if (typeof ok !== 'function') {
|
||||
return ok === undefined ? defaults : ok
|
||||
return ok === undefined ? true : ok
|
||||
}
|
||||
return this.cleanValue(item, attr)
|
||||
return ok(this.row, this.cellValue)
|
||||
},
|
||||
cleanCallback(item, attr) {
|
||||
const callback = item[attr]
|
||||
cleanCallback(item) {
|
||||
const callback = item.callback
|
||||
const attrs = {
|
||||
reload: this.reload,
|
||||
row: this.row,
|
||||
@@ -193,20 +185,6 @@ export default {
|
||||
tableData: this.tableData
|
||||
}
|
||||
return () => { return callback.bind(this)(attrs) }
|
||||
},
|
||||
cleanValue(item, attr) {
|
||||
const value = item[attr]
|
||||
if (!value || typeof value !== 'function') {
|
||||
return value
|
||||
}
|
||||
const attrs = {
|
||||
reload: this.reload,
|
||||
row: this.row,
|
||||
col: this.col,
|
||||
cellValue: this.cellValue,
|
||||
tableData: this.tableData
|
||||
}
|
||||
return value(attrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
116
src/components/ListTable/formatters/LoadingActionsFormatter.vue
Normal file
116
src/components/ListTable/formatters/LoadingActionsFormatter.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<ActionsGroup v-loading="loadStatus" :size="'mini'" :actions="actions" :more-actions="moreActions" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionsGroup from '@/components/ActionsGroup/index'
|
||||
import BaseFormatter from './base'
|
||||
|
||||
export default {
|
||||
name: 'LoadingActionsFormatter',
|
||||
components: { ActionsGroup },
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return {
|
||||
hasUpdate: true, // can set function(row, value)
|
||||
canUpdate: true, // can set function(row, value)
|
||||
hasDelete: true, // can set function(row, value)
|
||||
canDelete: true,
|
||||
updateRoute: this.$route.name.replace('List', 'Update'),
|
||||
extraActions: [] // format see defaultActions
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const colActions = Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
const defaultActions = [
|
||||
{
|
||||
name: 'update',
|
||||
title: this.$t('common.Update'),
|
||||
type: 'primary',
|
||||
has: colActions.hasUpdate,
|
||||
can: colActions.canUpdate,
|
||||
callback: colActions.onUpdate
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
title: this.$t('common.Delete'),
|
||||
type: 'danger',
|
||||
has: colActions.hasDelete,
|
||||
can: colActions.canDelete,
|
||||
callback: colActions.onDelete
|
||||
}
|
||||
]
|
||||
return {
|
||||
colActions: colActions,
|
||||
defaultActions: defaultActions,
|
||||
extraActions: colActions.extraActions
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cleanedActions() {
|
||||
let actions = [...this.defaultActions, ...this.extraActions]
|
||||
actions = _.cloneDeep(actions)
|
||||
actions = actions.map((v) => {
|
||||
v.has = this.cleanBoolean(v, 'has')
|
||||
v.can = this.cleanBoolean(v, 'can')
|
||||
v.fa = this.cleanFa(v, 'fa')
|
||||
v.callback = this.cleanCallback(v)
|
||||
return v
|
||||
})
|
||||
actions = actions.filter((v) => v.has)
|
||||
return actions
|
||||
},
|
||||
actions() {
|
||||
if (this.cleanedActions.length <= 2) {
|
||||
return this.cleanedActions
|
||||
}
|
||||
return this.cleanedActions.slice(0, 1)
|
||||
},
|
||||
moreActions() {
|
||||
if (this.cleanedActions.length <= 2) {
|
||||
return []
|
||||
}
|
||||
return this.cleanedActions.slice(1, this.cleanedActions.length)
|
||||
},
|
||||
loadStatus() {
|
||||
return this.col.formatterArgs.loading
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cleanBoolean(item, attr) {
|
||||
const ok = item[attr]
|
||||
if (typeof ok !== 'function') {
|
||||
return ok === undefined ? true : ok
|
||||
}
|
||||
return ok(this.row, this.cellValue)
|
||||
},
|
||||
cleanCallback(item) {
|
||||
const callback = item.callback
|
||||
const attrs = {
|
||||
reload: this.reload,
|
||||
row: this.row,
|
||||
col: this.col,
|
||||
cellValue: this.cellValue,
|
||||
tableData: this.tableData
|
||||
}
|
||||
return () => { return callback.bind(this)(attrs) }
|
||||
},
|
||||
cleanFa(item, attr) {
|
||||
const ok = item[attr]
|
||||
if (typeof ok !== 'function') {
|
||||
return ok === undefined ? false : ok
|
||||
}
|
||||
return ok(this.row, this.cellValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -7,8 +7,9 @@ import ActionsFormatter from './ActionsFormatter'
|
||||
import DeleteActionFormatter from './DeleteActionFormatter'
|
||||
import DateFormatter from './DateFormatter'
|
||||
import SystemUserFormatter from './GrantedSystemUsersShowFormatter'
|
||||
import ShowKeyFormatter from '@/components/TableFormatters/ShowKeyFormatter'
|
||||
import ShowKeyFormatter from '@/components/ListTable/formatters/ShowKeyFormatter'
|
||||
import DialogDetailFormatter from './DialogDetailFormatter'
|
||||
import LoadingActionsFormatter from './LoadingActionsFormatter'
|
||||
import EditableInputFormatter from './EditableInputFormatter'
|
||||
import StatusFormatter from './StatusFormatter'
|
||||
|
||||
@@ -23,6 +24,7 @@ export default {
|
||||
SystemUserFormatter,
|
||||
ShowKeyFormatter,
|
||||
DialogDetailFormatter,
|
||||
LoadingActionsFormatter,
|
||||
ArrayFormatter,
|
||||
EditableInputFormatter,
|
||||
StatusFormatter
|
||||
@@ -39,6 +41,7 @@ export {
|
||||
SystemUserFormatter,
|
||||
ShowKeyFormatter,
|
||||
DialogDetailFormatter,
|
||||
LoadingActionsFormatter,
|
||||
ArrayFormatter,
|
||||
EditableInputFormatter,
|
||||
StatusFormatter
|
||||
@@ -113,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>
|
||||
@@ -10,7 +10,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Switcher from '../FormFields/Swicher'
|
||||
import Switcher from '../Swicher'
|
||||
export default {
|
||||
name: 'ActionItem',
|
||||
components: {
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Select2 from '../FormFields/Select2'
|
||||
import Select2 from '../Select2'
|
||||
import IBox from '../IBox'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
@@ -4,16 +4,13 @@
|
||||
<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>
|
||||
<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>
|
||||
@@ -77,8 +74,4 @@ export default {
|
||||
.no-margins {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.disabled-link {
|
||||
color: #428bca;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
@@ -7,7 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PasswordInput from './PasswordInput'
|
||||
import PasswordInput from '../PasswordInput'
|
||||
import { mapGetters } from 'vuex'
|
||||
import store from '@/store'
|
||||
import i18n from '@/i18n/i18n'
|
||||
@@ -12,16 +12,14 @@ export { default as FormGroupHeader } from './FormGroupHeader'
|
||||
export { default as Hamburger } from './Hamburger'
|
||||
export { default as ListTable } from './ListTable'
|
||||
export { default as RelationCard } from './RelationCard'
|
||||
export { default as Select2 } from './FormFields/Select2'
|
||||
export { default as UploadKey } from './FormFields/UploadKey.vue'
|
||||
export { default as Select2 } from './Select2'
|
||||
export { default as AssetSelect } from './AssetSelect'
|
||||
export { default as SvgIcon } from './SvgIcon'
|
||||
export { default as TreeTable } from './TreeTable'
|
||||
export { default as IBox } from './IBox'
|
||||
export { default as QuickActions } from './QuickActions'
|
||||
export { default as Switcher } from './FormFields/Swicher'
|
||||
export { default as Switcher } from './Swicher'
|
||||
export { default as SummaryCard } from './SummaryCard'
|
||||
export { default as UploadField } from './FormFields/UploadField'
|
||||
export { default as AccountListTable } from './AccountListTable/index'
|
||||
export { default as UploadField } from './UploadField'
|
||||
export { default as AssetUserTable } from './AssetUserTable'
|
||||
export { default as AssetRelationCard } from './AssetRelationCard'
|
||||
export { default as MFAVerifyDialog } from './MFAVerifyDialog'
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
{
|
||||
"": "",
|
||||
"accounts": {
|
||||
"PleaseClickLeftAssetToViewAssetAccount": "资产账号列表,点击左侧资产进行查看",
|
||||
"PleaseClickLeftApplicationToViewApplicationAccount": "应用账号列表,点击左侧应用进行查看",
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "收集用户列表,点击左侧资产进行查看"
|
||||
},
|
||||
"acl": {
|
||||
"name": "名称",
|
||||
"username": "用户名",
|
||||
@@ -83,13 +78,11 @@
|
||||
"DBInfo": "数据库信息"
|
||||
},
|
||||
"assets": {
|
||||
"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": "资产详情",
|
||||
@@ -100,11 +93,11 @@
|
||||
"TestGatewayHelpMessage": "如果使用了nat端口映射,请设置为ssh真实监听的端口",
|
||||
"SshPort": "SSH 端口",
|
||||
"AssetNumber": "资产编号",
|
||||
"AssetUserList": "账号列表",
|
||||
"AssetUserList": "资产用户列表",
|
||||
"Assets": "资产",
|
||||
"Auth": "认证",
|
||||
"AccountList": "账号列表",
|
||||
"AutoGenerateKey": "自动生成",
|
||||
"AutoGenerateKey": "自动生成密钥",
|
||||
"AutoPush": "自动推送",
|
||||
"BasePlatform": "基础平台",
|
||||
"Basic": "基本",
|
||||
@@ -117,7 +110,6 @@
|
||||
"CommandFilterRules": "命令过滤器规则",
|
||||
"Comment": "备注",
|
||||
"Cpu": "CPU",
|
||||
"CommonUser": "普通用户",
|
||||
"CreatedBy": "创建者",
|
||||
"Database": "数据库",
|
||||
"DateJoined": "创建日期",
|
||||
@@ -159,7 +151,6 @@
|
||||
"PriorityHelpMessage": "1-100, 1最低优先级,100最高优先级。授权多个用户时,高优先级的系统用户将会作为默认登录用户",
|
||||
"Protocol": "协议",
|
||||
"Protocols": "协议组",
|
||||
"LoginOption": "登录选项",
|
||||
"PublicIp": "公网IP",
|
||||
"Push": "推送",
|
||||
"PushSystemUserNow": "推送系统用户",
|
||||
@@ -182,8 +173,7 @@
|
||||
"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": "测试资产可连接性",
|
||||
@@ -191,7 +181,7 @@
|
||||
"Type": "类型",
|
||||
"UnselectedAssets": "未选择资产或所选择的资产不支持SSH协议连接",
|
||||
"UnselectedNodes": "未选择节点",
|
||||
"UpdateAssetUserToken": "更新账号认证信息",
|
||||
"UpdateAssetUserToken": "更新资产用户认证信息",
|
||||
"Username": "用户名",
|
||||
"UsernameHelpMessage": "用户名是动态的,登录资产时使用当前用户的用户名登录",
|
||||
"Value": "值",
|
||||
@@ -202,7 +192,6 @@
|
||||
"sshKeyFingerprint": "SSH 指纹",
|
||||
"ip": "IP",
|
||||
"sshkey": "sshkey",
|
||||
"SSHKey": "SSH 密钥",
|
||||
"GroupsHelpMessage": "请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)",
|
||||
"HomeHelpMessage": "默认家目录 /home/系统用户名: /home/username",
|
||||
"Home": "家目录",
|
||||
@@ -210,25 +199,20 @@
|
||||
"ipDomain": "IP(域名)",
|
||||
"HostProtocol": "主机协议",
|
||||
"DatabaseProtocol": "数据库协议",
|
||||
"OtherProtocol": "其它协议",
|
||||
"PasswordOrToken": "密码 / 令牌"
|
||||
"OtherProtocol": "其它协议"
|
||||
|
||||
},
|
||||
"audits": {
|
||||
"Hosts": "主机",
|
||||
"RunUser": "运行用户",
|
||||
"User": "用户",
|
||||
"Username": "用户名",
|
||||
"View": "查看",
|
||||
"SystemUserName": "系统用户名"
|
||||
"View": "查看"
|
||||
},
|
||||
"auth": {
|
||||
"LoginRequiredMsg": "账号已退出,请重新登录",
|
||||
"ReLogin": "重新登录"
|
||||
},
|
||||
"common": {
|
||||
"MFAVerify": "验证 MFA",
|
||||
"ViewSecret": "查看密码",
|
||||
"ConnectWebSocketError": "连接 WebSocket 失败",
|
||||
"Action": "动作",
|
||||
"RequestTickets": "申请工单",
|
||||
"Actions": "操作",
|
||||
@@ -243,8 +227,6 @@
|
||||
"UpdateAssetDetail": "配置更多信息",
|
||||
"AddSuccessMsg": "添加成功",
|
||||
"Auth": "认证",
|
||||
"bind": "绑定",
|
||||
"unbind": "解绑",
|
||||
"PushSelected":"推送所选",
|
||||
"BadRequestErrorMsg": "请求错误,请检查填写内容",
|
||||
"BadRoleErrorMsg": "请求错误,无该操作权限",
|
||||
@@ -284,8 +266,6 @@
|
||||
"Info": "提示",
|
||||
"MFAConfirm": "MFA 认证",
|
||||
"MFARequireForSecurity": "为了安全请输入MFA",
|
||||
"PasswordConfirm": "密码认证",
|
||||
"PasswordRequireForSecurity": "为了安全请输入密码",
|
||||
"Members": "成员",
|
||||
"More": "更多",
|
||||
"Message": "消息",
|
||||
@@ -319,7 +299,6 @@
|
||||
"View": "查看",
|
||||
"Yes": "是",
|
||||
"action": "动作",
|
||||
"User": "用户",
|
||||
"activateSelected": "激活所选",
|
||||
"bulkDeleteErrorMsg": "批量删除失败: ",
|
||||
"bulkDeleteSuccessMsg": "批量删除成功",
|
||||
@@ -351,7 +330,6 @@
|
||||
"Pending": "等待",
|
||||
"Status": "状态",
|
||||
"InputEmailAddress": "请输入正确的邮箱地址",
|
||||
"Receivers": "接收人",
|
||||
"imExport": {
|
||||
"ExportAll": "导出所有",
|
||||
"ExportOnlyFiltered": "仅导出搜索结果",
|
||||
@@ -402,8 +380,7 @@
|
||||
"SPECIAL_CHAR_REQUIRED": "须包含特殊字符",
|
||||
"MIN_LENGTH_ERROR": "密码最小长度 {0} 位"
|
||||
},
|
||||
"lastCannotBeDeleteMsg": "最后一项,不能被删除",
|
||||
"InvalidJson": "不是合法 JSON"
|
||||
"lastCannotBeDeleteMsg": "最后一项,不能被删除"
|
||||
},
|
||||
"dashboard": {
|
||||
"ActiveAsset": "近期被登录过",
|
||||
@@ -540,11 +517,7 @@
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"Accounts": "账号管理",
|
||||
"AssetAccount": "资产账号",
|
||||
"ApplicationAccount": "应用账号",
|
||||
"Ticket":"工单",
|
||||
"CommandConfirm": "命令复核",
|
||||
"AdminUserCreate": "创建管理用户",
|
||||
"AdminUserDetail": "管理用户详情",
|
||||
"AdminUserList": "管理用户",
|
||||
@@ -646,7 +619,6 @@
|
||||
"SystemUserDetail": "系统用户详情",
|
||||
"SystemUserList": "系统用户",
|
||||
"SystemUserUpdate": "更新系统用户",
|
||||
"AssetUserList": "资产用户",
|
||||
"TaskDetail": "任务详情",
|
||||
"TaskList": "任务列表",
|
||||
"TaskMonitor": "任务监控",
|
||||
@@ -667,15 +639,9 @@
|
||||
"UserUpdate": "更新用户",
|
||||
"Users": "用户管理",
|
||||
"WebFTP": "文件管理",
|
||||
"WebTerminal": "Web终端",
|
||||
"Notifications": "通知",
|
||||
"SiteMessageList": "站内信"
|
||||
"WebTerminal": "Web终端"
|
||||
},
|
||||
"sessions": {
|
||||
"SetToDefaultStorage": "设置为默认存储",
|
||||
"SetToDefault": "设为默认",
|
||||
"SetSuccess": "设置成功",
|
||||
"SetFailed": "设置失败",
|
||||
"StorageConfiguration": "存储配置",
|
||||
"accountKey": "账户密钥",
|
||||
"accountName": "账户名称",
|
||||
@@ -736,7 +702,6 @@
|
||||
"common": "普通"
|
||||
},
|
||||
"Monitor": "监控",
|
||||
"XRDPNotSupport": "RDP 客户端会话, 暂不支持监控",
|
||||
"sessionMonitor": "监控",
|
||||
"TerminateTaskSendSuccessMsg": "终断任务已下发,请稍后刷新查看",
|
||||
"helpText": {
|
||||
@@ -748,7 +713,6 @@
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"InsecureCommandNotifyToSubscription": "危险命令通知已升级到消息订阅中,支持更多通知方式",
|
||||
"ApiKeyList": "API Key 列表",
|
||||
"AssetCount": "资产数量",
|
||||
"Basic": "基本设置",
|
||||
@@ -767,8 +731,6 @@
|
||||
"PasswordCheckRule": "密码校验规则",
|
||||
"Security": "安全设置",
|
||||
"SecuritySetting": "安全设置",
|
||||
"SystemMessageSubscription": "系统消息订阅",
|
||||
"insecureCommandEmailUpdate": "点我设置",
|
||||
"SubscriptionID": "订阅授权ID",
|
||||
"Terminal": "终端设置",
|
||||
"all": "全部",
|
||||
@@ -882,15 +844,12 @@
|
||||
"refreshLdapCache":"刷新Ldap缓存,请稍后",
|
||||
"LicenseExpired": "许可证已经过期",
|
||||
"LicenseWillBe": "许可证即将在 ",
|
||||
"Expire": " 过期",
|
||||
"WeCom": "企业微信",
|
||||
"DingTalk": "钉钉",
|
||||
"dingTalkTest": "测试",
|
||||
"weComTest": "测试",
|
||||
"Expire": " 过期"
|
||||
},
|
||||
"settings": {
|
||||
"setting": "设置"
|
||||
},
|
||||
"tickets": {
|
||||
"PermissionName": "授权规则名称",
|
||||
"Accept": "同意",
|
||||
"AssignedMe": "待我审批",
|
||||
"Assignee": "处理人",
|
||||
@@ -932,13 +891,7 @@
|
||||
"ips": "请输入逗号分割的IP地址组",
|
||||
"fuzzySearch": "支持模糊搜索",
|
||||
"application": "请输入逗号分割的应用名称组"
|
||||
},
|
||||
"ApplyRunUser": "申请运行的用户",
|
||||
"ApplyRunAsset": "申请运行的资产",
|
||||
"ApplyRunSystemUser": "申请运行的系统用户",
|
||||
"ApplyRunCommand": "申请运行的命令",
|
||||
"ApplyFromSession": "会话",
|
||||
"ApplyFromCMDFilterRule": "命令过滤规则"
|
||||
}
|
||||
},
|
||||
"tree": {
|
||||
"AddAssetToNode": "添加资产到节点",
|
||||
@@ -954,7 +907,6 @@
|
||||
"UpdateNodeAssetHardwareInfo": "更新节点资产硬件信息"
|
||||
},
|
||||
"users": {
|
||||
"UserName": "姓名",
|
||||
"Account": "账户",
|
||||
"Authentication": "认证",
|
||||
"Comment": "备注",
|
||||
@@ -975,8 +927,6 @@
|
||||
"Invite": "邀请",
|
||||
"InviteUserInOrg": "邀请用户加入此组织",
|
||||
"Guide": "向导",
|
||||
"setWeCom": "设置企业微信认证",
|
||||
"setDingTalk": "设置钉钉认证",
|
||||
"HelpText": {
|
||||
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "启用多因子认证,使账号更加安全。<br/> 启用之后您将会在下次登录时进入多因子认证绑定流程;您也可以在(个人信息->快速修改->更改多因子设置)中直接绑定!",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:设置复杂密码,并启用多因子认证)",
|
||||
@@ -1024,14 +974,7 @@
|
||||
"resetSSHKey": "重置SSH密钥",
|
||||
"resetSSHKeySuccessMsg": "发送邮件任务已提交, 用户稍后会收到重置密钥邮件",
|
||||
"resetSSHKeyWarningMsg": "你确定要发送重置用户的SSH Key的邮件吗?",
|
||||
"resetWechat": "解绑企业微信",
|
||||
"resetWechatLoginWarningMsg": "你确定要解绑用户的 企业微信 吗?",
|
||||
"resetWechatLoginSuccessMsg": "重置成功, 用户可以重新绑定企业微信了",
|
||||
"resetDingTalk": "解绑钉钉",
|
||||
"resetDingTalkLoginWarningMsg": "你确定要解绑用户的 钉钉 吗?",
|
||||
"resetDingTalkLoginSuccessMsg": "重置成功, 用户可以重新绑定钉钉了",
|
||||
"send": "发送",
|
||||
"unbind": "解绑",
|
||||
"unblock": "解锁",
|
||||
"unblockSuccessMsg": "解锁成功",
|
||||
"unblockUser": "解锁用户"
|
||||
@@ -1050,27 +993,12 @@
|
||||
"remoteAppPermissionRules": "远程应用授权规则"
|
||||
},
|
||||
"dateLastLogin": "最后登录日期",
|
||||
"needUpdatePasswordNextLogin": "下次登录须修改密码",
|
||||
"UpdatePassword": "更新密码",
|
||||
"SetPublicKey": "设置SSH公钥",
|
||||
"passwordExpired": "密码过期了",
|
||||
"passwordWillExpiredPrefixMsg": "密码即将在 ",
|
||||
"passwordWillExpiredSuffixMsg": "天 后过期,请尽快修改您的密码。"
|
||||
},
|
||||
"notifications": {
|
||||
"MessageType": "消息类型",
|
||||
"Receivers": "接收人",
|
||||
"Subscription": "消息订阅",
|
||||
"ChangeReceiver": "修改消息接收人",
|
||||
"Subject": "主题",
|
||||
"Message": "消息",
|
||||
"DeliveryTime": "发送时间",
|
||||
"HasRead": "是否已读",
|
||||
"Sender": "发送人",
|
||||
"MarkAsRead": "标记已读",
|
||||
"NoUnreadMsg": "暂无未读消息",
|
||||
"SiteMessage": "站内信"
|
||||
},
|
||||
"xpack": {
|
||||
"Admin": "管理员",
|
||||
"Asset": "资产",
|
||||
@@ -1120,8 +1048,6 @@
|
||||
"Cloud": {
|
||||
"Aliyun": "阿里云",
|
||||
"Qcloud": "腾讯云",
|
||||
"QingyunPrivatecloud": "青云私有云",
|
||||
"HuaweiPrivatecloud": "华为私有云",
|
||||
"AWS_China": "AWS(中国)",
|
||||
"AWS_Int": "AWS(国际)",
|
||||
"HuaweiCloud": "华为云",
|
||||
@@ -1149,7 +1075,7 @@
|
||||
"Name": "名称",
|
||||
"Account":"账户",
|
||||
"Node": "节点",
|
||||
"AdminUser":"特权用户",
|
||||
"AdminUser":"管理用户",
|
||||
"Periodic":"执行周期",
|
||||
"PeriodicPerform":"定时执行",
|
||||
"RegularlyPerform": "定期执行",
|
||||
@@ -1179,9 +1105,7 @@
|
||||
"GatherUserList": "收集用户",
|
||||
"GatherUserTaskCreate": "创建任务",
|
||||
"GatherUserTaskList": "任务列表",
|
||||
"GatherUserTaskUpdate": "更新任务",
|
||||
"GatherUserTaskDetail": "任务详情",
|
||||
"GatherUserTaskExecutionList": "任务执行列表"
|
||||
"GatherUserTaskUpdate": "更新任务"
|
||||
},
|
||||
"Import": "导入",
|
||||
"ImportLicense": "导入许可证",
|
||||
@@ -1211,7 +1135,7 @@
|
||||
"users_amount": "用户数量",
|
||||
"groups_amount": "用户组数量",
|
||||
"assets_amount": "资产数量",
|
||||
"admin_users_amount": "特权用户数量",
|
||||
"admin_users_amount": "管理用户数量",
|
||||
"system_users_amount": "系统用户数量",
|
||||
"applications_amount": "应用数量",
|
||||
"asset_perms_amount": "资产授权数量",
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
{
|
||||
"": "",
|
||||
"accounts": {
|
||||
"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"
|
||||
},
|
||||
"acl": {
|
||||
"name": "Name",
|
||||
"username": "Username",
|
||||
@@ -82,20 +77,16 @@
|
||||
"DBInfo": "Database Info"
|
||||
},
|
||||
"assets": {
|
||||
"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",
|
||||
@@ -119,7 +110,6 @@
|
||||
"CommandFilterRules": "Command filter rules",
|
||||
"Comment": "Comment",
|
||||
"Cpu": "Cpu",
|
||||
"CommonUser": "Common user",
|
||||
"CreatedBy": "Created by",
|
||||
"Database": "Database",
|
||||
"DateJoined": "Date joined",
|
||||
@@ -178,11 +168,11 @@
|
||||
"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",
|
||||
@@ -208,15 +198,12 @@
|
||||
"ipDomain": "IP(Domain)",
|
||||
"HostProtocol": "Host Protocol",
|
||||
"DatabaseProtocol": "Database Protocol",
|
||||
"OtherProtocol": "Other Protocol",
|
||||
"PasswordOrToken": "Password / Token"
|
||||
"Other Protocol": "Database Protocol"
|
||||
},
|
||||
"audits": {
|
||||
"Hosts": "Host",
|
||||
"RunUser": "Run user",
|
||||
"User": "User",
|
||||
"Username": "Username",
|
||||
"SystemUserName": "System username",
|
||||
"View": "View"
|
||||
},
|
||||
"auth": {
|
||||
@@ -224,9 +211,6 @@
|
||||
"ReLogin": "Re-Login"
|
||||
},
|
||||
"common": {
|
||||
"MFAVerify": "Verify MFA",
|
||||
"ViewSecret": "View secret",
|
||||
"ConnectWebSocketError": "Connect Websocket failed",
|
||||
"Nothing": "Nothing",
|
||||
"Action": "Action",
|
||||
"CustomCol":"Custom table col",
|
||||
@@ -257,7 +241,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",
|
||||
@@ -281,8 +265,6 @@
|
||||
"Info": "Info",
|
||||
"MFAConfirm": "MFA Confirm",
|
||||
"MFARequireForSecurity": "MFA required for security",
|
||||
"PasswordConfirm": "Password Confirm",
|
||||
"PasswordRequireForSecurity": "Password required for security",
|
||||
"Members": "Members",
|
||||
"More": "More",
|
||||
"Message": "Message",
|
||||
@@ -294,12 +276,11 @@
|
||||
"Other": "Other",
|
||||
"Others": "Others",
|
||||
"Push": "Push",
|
||||
"Receivers": "Receivers",
|
||||
"QuickUpdate": "Quick update",
|
||||
"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",
|
||||
@@ -310,8 +291,6 @@
|
||||
"TestSuccessMsg": "Test Success",
|
||||
"To": "To",
|
||||
"Update": "Update",
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"Upload": "Upload",
|
||||
"Clone": "Clone",
|
||||
"Username": "Username",
|
||||
@@ -399,8 +378,7 @@
|
||||
"SPECIAL_CHAR_REQUIRED": "Special char required",
|
||||
"MIN_LENGTH_ERROR": "Password minimum length {}"
|
||||
},
|
||||
"lastCannotBeDeleteMsg": "The last one can't be delete",
|
||||
"InvalidJson": "Not a valid json format"
|
||||
"lastCannotBeDeleteMsg": "The last one can't be delete"
|
||||
},
|
||||
"dashboard": {
|
||||
"ActiveAsset": "Asset active",
|
||||
@@ -537,33 +515,29 @@
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"Accounts": "Accounts",
|
||||
"AssetAccount": "Asset account",
|
||||
"ApplicationAccount": "Application account",
|
||||
"Ticket": "Tickets",
|
||||
"CommandConfirm": "Command confirm",
|
||||
"AdminUserCreate": "Admin user create",
|
||||
"AdminUserDetail": "Admin user detail",
|
||||
"AdminUserList": "Admin Users",
|
||||
"AdminUserList": "Admin users",
|
||||
"AdminUserUpdate": "Admin user update",
|
||||
"Applications": "Applications",
|
||||
"AssetCreate": "Asset create",
|
||||
"AssetDetail": "Asset detail",
|
||||
"AssetList": "Assets",
|
||||
"AssetPermission": "Asset Permissions",
|
||||
"AssetPermission": "Asset permissions",
|
||||
"AssetPermissionCreate": "Asset permissions create",
|
||||
"AssetPermissionDetail": "Asset permissions detail",
|
||||
"AssetPermissionUpdate": "Asset permissions update",
|
||||
"AssetUpdate": "Asset update",
|
||||
"Assets": "Assets",
|
||||
"Audits": "Audits",
|
||||
"BatchCommand": "Batch Command",
|
||||
"BatchCommandLog": "Batch Command Log",
|
||||
"BatchCommand": "Batch command",
|
||||
"BatchCommandLog": "Batch command log",
|
||||
"CeleryTaskLog": "Celery task log",
|
||||
"CommandExecutions": "CommandExecutions ",
|
||||
"CommandFilterCreate": "Command filter create",
|
||||
"CommandFilterDetail": "Command filter detail",
|
||||
"CommandFilterList": "Command Filters",
|
||||
"CommandFilterList": "Command filters",
|
||||
"CommandFilterRulesCreate": "Command filter rules create",
|
||||
"CommandFilterRulesUpdate": "Command filter rules update",
|
||||
"CommandFilterUpdate": "Command filter update",
|
||||
@@ -572,7 +546,7 @@
|
||||
"CreateCommandStorage": "Create command storage",
|
||||
"CreateReplayStorage": "Create replay storage",
|
||||
"Dashboard": "Dashboard",
|
||||
"DatabaseApp": "Database Apps",
|
||||
"DatabaseApp": "Database apps",
|
||||
"DatabaseAppCreate": "Database app create",
|
||||
"DatabaseAppDetail": "Database app detail",
|
||||
"DatabaseAppPermission": "Databases permissions",
|
||||
@@ -580,7 +554,7 @@
|
||||
"DatabaseAppPermissionDetail": "Databases permissions detail",
|
||||
"DatabaseAppPermissionUpdate": "Databases permissions update",
|
||||
"DatabaseAppUpdate": "Database app update",
|
||||
"KubernetesApp": "Kubernetes Apps",
|
||||
"KubernetesApp": "Kubernetes apps",
|
||||
"KubernetesAppCreate": "Kubernetes app create",
|
||||
"KubernetesAppDetail": "Kubernetes app detail",
|
||||
"KubernetesAppPermission": "Kubernetes permissions",
|
||||
@@ -588,13 +562,13 @@
|
||||
"KubernetesAppPermissionDetail": "Kubernetes permissions detail",
|
||||
"KubernetesAppPermissionUpdate": "Kubernetes permissions update",
|
||||
"KubernetesAppUpdate": "Kubernetes app update",
|
||||
"Acl": "Access Control",
|
||||
"Acl": "Access control",
|
||||
"UserAclList": "User acl list",
|
||||
"UserAclCreate": "User acl create",
|
||||
"UserAclUpdate": "User acl update",
|
||||
"UserAclLists": "User acl lists",
|
||||
"UserAclDetail": "User acl detail",
|
||||
"AssetAclList": "Asset Acl",
|
||||
"AssetAclList": "Asset acl list",
|
||||
"AssetAclCreate": "Asset acl create",
|
||||
"AssetAclUpdate": "Asset acl update",
|
||||
"AssetAclDetail": "Asset acl detail",
|
||||
@@ -602,29 +576,29 @@
|
||||
"DomainDetail": "Domain detail",
|
||||
"DomainList": "Domains",
|
||||
"DomainUpdate": "Domain update",
|
||||
"FileManager": "File Manager",
|
||||
"FtpLog": "FTP Logs",
|
||||
"FileManager": "File manager",
|
||||
"FtpLog": "FTP logs",
|
||||
"GatewayCreate": "Gateway create",
|
||||
"GatewayUpdate": "Gateway update",
|
||||
"JobCenter": "Jobcenter",
|
||||
"LabelCreate": "Label create",
|
||||
"LabelList": "Labels",
|
||||
"LabelUpdate": "Label update",
|
||||
"LoginLog": "Login Logs",
|
||||
"MyApps": "My Apps",
|
||||
"MyAssets": "My Assets",
|
||||
"OperateLog": "Operation Logs",
|
||||
"PasswordChangeLog": "Password Update Logs",
|
||||
"LoginLog": "Login logs",
|
||||
"MyApps": "My apps",
|
||||
"MyAssets": "My assets",
|
||||
"OperateLog": "Operation logs",
|
||||
"PasswordChangeLog": "Password update logs",
|
||||
"Perms": "Permissions",
|
||||
"PersonalInformationImprovement": "PersonalInformationImprovement",
|
||||
"PlatformCreate": "Platform create",
|
||||
"PlatformDetail": "Platform detail",
|
||||
"PlatformList": "Platforms",
|
||||
"PlatformUpdate": "Platform update",
|
||||
"RemoteApp": "Remote Apps",
|
||||
"RemoteApp": "Remote apps",
|
||||
"RemoteAppDetail": "Remote app detail",
|
||||
"RemoteAppPermission": "Remote apps permissions",
|
||||
"ApplicationPermission": "Application Permissions",
|
||||
"ApplicationPermission": "Application permissions",
|
||||
"RemoteAppPermissionCreate": "Remote apps permission create",
|
||||
"RemoteAppPermissionDetail": "Remote apps permissions detail",
|
||||
"RemoteAppPermissionUpdate": "Remote app permission update",
|
||||
@@ -641,11 +615,11 @@
|
||||
"Settings": "Settings",
|
||||
"SystemUserCreate": "System user create",
|
||||
"SystemUserDetail": "System user detail",
|
||||
"SystemUserList": "System Users",
|
||||
"SystemUserList": "System users",
|
||||
"SystemUserUpdate": "System user update",
|
||||
"TaskDetail": "Tasks detail",
|
||||
"TaskList": "Tasks",
|
||||
"TaskMonitor": "Task Monitor",
|
||||
"TaskMonitor": "Task monitor",
|
||||
"Terminal": "Terminal",
|
||||
"TicketDetail": "Ticket detail",
|
||||
"TicketCreate": "Ticket create",
|
||||
@@ -655,7 +629,7 @@
|
||||
"UserFirstLogin": "UserFirstLogin",
|
||||
"UserGroupCreate": "User group create",
|
||||
"UserGroupDetail": "User group detail",
|
||||
"UserGroupList": "User Groups",
|
||||
"UserGroupList": "User groups",
|
||||
"UserGroupUpdate": "User group update",
|
||||
"UserGuide": "UserGuide",
|
||||
"UserList": "Users",
|
||||
@@ -663,15 +637,9 @@
|
||||
"UserUpdate": "User update",
|
||||
"Users": "Users",
|
||||
"WebFTP": "WebFTP",
|
||||
"WebTerminal": "Web Terminal",
|
||||
"Notifications": "Notifications",
|
||||
"SiteMessageList": "Site message"
|
||||
"WebTerminal": "Web terminal"
|
||||
},
|
||||
"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",
|
||||
@@ -732,11 +700,10 @@
|
||||
"common": "common"
|
||||
},
|
||||
"Monitor": "Monitor",
|
||||
"XRDPNotSupport": "RDP Client session not support now",
|
||||
"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",
|
||||
@@ -744,7 +711,6 @@
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"InsecureCommandNotifyToSubscription": "Insecure command notification setting, change to system message subscription, support more notify method",
|
||||
"ApiKeyList": "Api key list",
|
||||
"AssetCount": "Asset count",
|
||||
"Basic": "Basic setting",
|
||||
@@ -764,8 +730,6 @@
|
||||
"Security": "Security setting",
|
||||
"SecuritySetting": "Security setting",
|
||||
"SubscriptionID": "Subscription ID",
|
||||
"SystemMessageSubscription": "System messages",
|
||||
"insecureCommandEmailUpdate": "Setting",
|
||||
"Terminal": "Terminal setting",
|
||||
"all": "All",
|
||||
"authLdap": "Enable LDAP auth",
|
||||
@@ -803,7 +767,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)",
|
||||
@@ -875,16 +839,12 @@
|
||||
"refreshLdapCache":"Refreshing Ldap cache ",
|
||||
"LicenseExpired": "License expired",
|
||||
"LicenseWillBe": "License will expire at ",
|
||||
"Expire": "",
|
||||
"WeCom": "WeCom",
|
||||
"DingTalk": "DingTalk",
|
||||
"dingTalkTest": "Test",
|
||||
"weComTest": "Test",
|
||||
"Expire": ""
|
||||
},
|
||||
"settings": {
|
||||
"setting": "Setting"
|
||||
},
|
||||
|
||||
"tickets": {
|
||||
"PermissionName": "Permission name",
|
||||
"Accept": "Accept",
|
||||
"AssignedMe": "Assigned me",
|
||||
"Assignee": "Assignee",
|
||||
@@ -926,13 +886,7 @@
|
||||
"ips": "Enter the IP address group, separated by commas",
|
||||
"fuzzySearch": "Support for fuzzy search",
|
||||
"application": "Enter the application group, separated by commas"
|
||||
},
|
||||
"ApplyRunUser": "Apply run user",
|
||||
"ApplyRunAsset": "Apply run asset",
|
||||
"ApplyRunSystemUser": "Apply run system user",
|
||||
"ApplyRunCommand": "Apply run command",
|
||||
"ApplyFromSession": "Session",
|
||||
"ApplyFromCMDFilterRule": "Command filter rule"
|
||||
}
|
||||
},
|
||||
"tree": {
|
||||
"AddAssetToNode": "Add asset to node",
|
||||
@@ -948,7 +902,6 @@
|
||||
"UpdateNodeAssetHardwareInfo": "Update node asset hardware information"
|
||||
},
|
||||
"users": {
|
||||
"UserName": "Name",
|
||||
"Account": "Account",
|
||||
"Existing":"Existing",
|
||||
"Authentication": "Account",
|
||||
@@ -958,8 +911,6 @@
|
||||
"DateJoined": "Date joined",
|
||||
"DateLastLogin": "Date last login",
|
||||
"DatePasswordLastUpdated": "Date password last updated",
|
||||
"setWeCom": "Set wecom login",
|
||||
"setDingTalk": "Set dingtalk login",
|
||||
"DatePasswordUpdated": "Date password updated",
|
||||
"DescribeOfGuide": "Welcome to JumpServer. Click here for more information",
|
||||
"Email": "Email",
|
||||
@@ -1019,11 +970,7 @@
|
||||
"resetSSHKey": "Reset SSH key",
|
||||
"resetSSHKeySuccessMsg": "An e-mail has been sent to the user`s mailbox",
|
||||
"resetSSHKeyWarningMsg": "This will reset the user public key and send a reset mail",
|
||||
"resetWechat": "Reset Wechat",
|
||||
"resetWechatLoginWarningMsg": "This will reset the user Wechat setting, user can reset it",
|
||||
"resetWechatLoginSuccessMsg": "Reset Wechat success",
|
||||
"send": "Send",
|
||||
"unbind": "Unbind",
|
||||
"unblock": "Unblock",
|
||||
"unblockSuccessMsg": "Account has unblocked",
|
||||
"unblockUser": "Unblock login"
|
||||
@@ -1041,28 +988,12 @@
|
||||
"ApplicationPermissionRules": "Application permission rules",
|
||||
"remoteAppPermissionRules": "Remote app permission rules"
|
||||
},
|
||||
"needUpdatePasswordNextLogin": "Update password next login",
|
||||
"UpdatePassword": "Update password",
|
||||
"SetPublicKey": "Set public key",
|
||||
"UpdatePassword": "",
|
||||
"UpdatePublicKey": "",
|
||||
"passwordExpired": "Password expired",
|
||||
"passwordWillExpiredPrefixMsg": "The password will expire in ",
|
||||
"passwordWillExpiredSuffixMsg": " days.Please change your password as soon as possible."
|
||||
},
|
||||
"notifications": {
|
||||
"MessageType": "Message Type",
|
||||
"Receivers": "Receivers",
|
||||
"Subscription": "Subscription",
|
||||
"ChangeReceiver": "Change Receivers",
|
||||
"Subject": "Subject",
|
||||
"Message": "Message",
|
||||
"DeliveryTime": "Delivery time",
|
||||
"HasRead": "Has read",
|
||||
"Sender": "Sender",
|
||||
"MarkAsRead": "Mark as read",
|
||||
"NoUnreadMsg": "No unread messages",
|
||||
"SiteMessage": "Site messages"
|
||||
},
|
||||
"xpack": {
|
||||
"Admin": "Admin",
|
||||
"Asset": "Asset",
|
||||
@@ -1074,7 +1005,7 @@
|
||||
"Asset": "Asset",
|
||||
"AssetAmount": "Asset",
|
||||
"AssetAndNode": "Asset and Node",
|
||||
"ChangeAuthPlan": "Change Auth Plan",
|
||||
"ChangeAuthPlan": "Change auth plan",
|
||||
"ChangeAuthPlanCreate": "Create change auth plan",
|
||||
"ChangeAuthPlanUpdate": "Update change auth plan",
|
||||
"CyclePerform": "Cycle perform",
|
||||
@@ -1112,8 +1043,6 @@
|
||||
"Cloud": {
|
||||
"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",
|
||||
@@ -1126,7 +1055,7 @@
|
||||
"AccountUpdate": "Update account",
|
||||
"AccountDetail": "Account detail",
|
||||
"Cloud": "Cloud center",
|
||||
"CloudCenter": "Cloud Center",
|
||||
"CloudCenter": "Cloud center",
|
||||
"Provider": "Provider",
|
||||
"Validity": "Validity",
|
||||
"IsAlwaysUpdateHelpTips": "Whether the asset information, including Hostname, IP, Platform, and AdminUser, is updated synchronously each time a synchronization task is performed",
|
||||
@@ -1167,18 +1096,16 @@
|
||||
"Execute": "Execute",
|
||||
"Expired": "Expired",
|
||||
"GatherUser": {
|
||||
"GatherUser": "Gather User",
|
||||
"GatherUser": "Gather user",
|
||||
"GatherUserList": "Gather user",
|
||||
"GatherUserTaskCreate": "Create gather user task",
|
||||
"GatherUserTaskList": "Gather user task list",
|
||||
"GatherUserTaskUpdate": "Update gather user task",
|
||||
"GatherUserTaskDetail": "Gather user detail",
|
||||
"GatherUserTaskExecutionList": "Gather user task execution list"
|
||||
"GatherUserTaskUpdate": "Update gather user task"
|
||||
},
|
||||
"Import": "Import",
|
||||
"ImportLicense": "Import license",
|
||||
"ImportLicenseTip": "Please Import License",
|
||||
"InterfaceSettings": "Interface Setting",
|
||||
"InterfaceSettings": "Interface setting",
|
||||
"License": "License",
|
||||
"LicenseDetail": "License detail",
|
||||
"SystemMonitor": "System Monitor",
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
</template>
|
||||
<script>
|
||||
import AutoDataForm from '@/components/AutoDataForm'
|
||||
import { getUpdateObjURL } from '@/utils/common'
|
||||
export default {
|
||||
name: 'GenericCreateUpdateForm',
|
||||
components: {
|
||||
@@ -130,7 +129,7 @@ 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
|
||||
}
|
||||
@@ -283,7 +282,6 @@ export default {
|
||||
if (object) {
|
||||
object = _.cloneDeep(object)
|
||||
this.$emit('update:object', object)
|
||||
this.$emit('getObjectDone', object)
|
||||
}
|
||||
return object
|
||||
},
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
<div class="sidebar-logo-container" :class="{'collapse':collapse}">
|
||||
<transition name="sidebarLogoFade">
|
||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||
<img :src="logoSrc" class="sidebar-logo">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo">
|
||||
<h1 v-else class="sidebar-title">{{ title }}</h1>
|
||||
</router-link>
|
||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||
<img :src="logoTextSrc" class="sidebar-logo-text">
|
||||
<img :src="logoSrc" class="sidebar-logo-text">
|
||||
<!-- <img v-else-if="logoText" :src="logoText" class="sidebar-logo-text">-->
|
||||
<!-- <h1 class="sidebar-title">{{ title }}</h1>-->
|
||||
</router-link>
|
||||
</transition>
|
||||
</div>
|
||||
@@ -23,6 +26,10 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: 'JumpServer',
|
||||
logoText: require('@/assets/img/logo-text.png'),
|
||||
logo: require('@/assets/img/logo.png'),
|
||||
xpackData: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -30,14 +37,16 @@ export default {
|
||||
'publicSettings'
|
||||
]),
|
||||
// eslint-disable-next-line vue/return-in-computed-property
|
||||
logoTextSrc() {
|
||||
return this.publicSettings.LOGO_URLS.logo_index
|
||||
},
|
||||
logoSrc() {
|
||||
return this.publicSettings.LOGO_URLS.logo_logout
|
||||
if (this.publicSettings.LOGO_URLS.logo_index !== '/static/img/logo_text.png') {
|
||||
return this.publicSettings.LOGO_URLS.logo_index
|
||||
} else {
|
||||
return this.logoText
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import ListTable from '@/components/ListTable'
|
||||
import { DateFormatter, ShowKeyFormatter } from '@/components/TableFormatters'
|
||||
import { DateFormatter, ShowKeyFormatter } from '@/components/ListTable/formatters'
|
||||
export default {
|
||||
name: 'ApiKey',
|
||||
components: {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Language',
|
||||
data() {
|
||||
@@ -46,22 +47,11 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.changeLang()
|
||||
this.changeMomentLang()
|
||||
if (this.currentLang.code !== this.$i18n.locale) {
|
||||
this.changeLangTo(this.currentLang)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeLang() {
|
||||
if (this.currentLang.code !== this.$i18n.locale) {
|
||||
this.changeLangTo(this.currentLang)
|
||||
}
|
||||
},
|
||||
changeMomentLang() {
|
||||
if (this.currentLang.code.indexOf('en') > -1) {
|
||||
this.$moment.locale('en')
|
||||
} else {
|
||||
this.$moment.locale('zh-cn')
|
||||
}
|
||||
},
|
||||
changeLangTo(item) {
|
||||
this.$i18n.locale = item.code
|
||||
localStorage.setItem('lang', item.code)
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-badge :value="unreadMsgCount" :hidden="unreadMsgCount === 0" :max="99" size="mini" type="primary">
|
||||
<a style="color: #606266 !important; width: 30px" @click="toggleDrawer">
|
||||
<i class="el-icon-message" style="font-size: 18px" />
|
||||
</a>
|
||||
</el-badge>
|
||||
<el-drawer
|
||||
:visible.sync="show"
|
||||
:before-close="handleClose"
|
||||
:modal="false"
|
||||
:title="$t('notifications.SiteMessage')"
|
||||
custom-class="site-msg"
|
||||
size="25%"
|
||||
@open="getMessages"
|
||||
>
|
||||
<div v-if="unreadMsgCount !== 0" class="msg-list">
|
||||
<div
|
||||
v-for="msg of messages"
|
||||
:key="msg.id"
|
||||
class="msg-item"
|
||||
:class="msg.has_read ? 'msg-read' : 'msg-unread'"
|
||||
@mouseover="hoverMsgId = msg.id"
|
||||
@mouseleave="hoverMsgId = ''"
|
||||
@click="showMsgDetail(msg)"
|
||||
>
|
||||
<div class="msg-item-head">
|
||||
<span class="msg-item-head-type">
|
||||
<i :class="msg.has_read ? 'fa-envelope-open-o' : 'fa-envelope'" class="fa msg-icon" />
|
||||
{{ msg.subject }}
|
||||
</span>
|
||||
<span v-if="hoverMsgId !== msg.id || msg.has_read" class="msg-item-head-time">
|
||||
{{ formatDate(msg.date_created) }}
|
||||
</span>
|
||||
<div v-else class="msg-item-read-btn" @click.stop="markAsRead(msg)">
|
||||
<a>{{ $t('notifications.MarkAsRead') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="msg-item-txt">
|
||||
<span v-html="msg.message" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="no-msg">
|
||||
{{ $t('notifications.NoUnreadMsg') }}
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<Dialog
|
||||
v-if="msgDetailVisible"
|
||||
:visible.sync="msgDetailVisible"
|
||||
:title="''"
|
||||
:close-on-click-modal="false"
|
||||
:confirm-title="$t('notifications.MarkAsRead')"
|
||||
@confirm="markAsRead(currentMsg)"
|
||||
@cancel="cancelRead"
|
||||
>
|
||||
<div class="msg-detail">
|
||||
<div class="msg-detail-head">
|
||||
<h3>{{ currentMsg.subject }}</h3>
|
||||
<h5>
|
||||
<span class="msg-detail-time">{{ formatDate(currentMsg.date_created) }}</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="msg-detail-txt">
|
||||
<span v-html="currentMsg.message" />
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import Dialog from '@/components/Dialog'
|
||||
|
||||
export default {
|
||||
name: 'SiteMessages',
|
||||
components: { Dialog },
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
messages: [],
|
||||
hoverMsgId: '',
|
||||
msgDetailVisible: false,
|
||||
currentMsg: null,
|
||||
unreadMsgCount: 0
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.enablePullMsgCount()
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.show = false
|
||||
},
|
||||
toggleDrawer() {
|
||||
this.show = !this.show
|
||||
},
|
||||
showMsgDetail(msg) {
|
||||
this.currentMsg = msg
|
||||
this.msgDetailVisible = true
|
||||
},
|
||||
getMessages() {
|
||||
const url = '/api/v1/notifications/site-message/?offset=0&limit=15&has_read=false'
|
||||
this.$axios.get(url).then(resp => {
|
||||
this.messages = [...resp.results]
|
||||
this.unreadMsgCount = resp.count
|
||||
})
|
||||
},
|
||||
formatDate(s) {
|
||||
if (!s) {
|
||||
return ''
|
||||
}
|
||||
const d = new Date(s)
|
||||
const now = new Date()
|
||||
if (now.getTime() - d.getTime() > (3600 * 24 * 7) * 1000) {
|
||||
return toSafeLocalDateStr(s)
|
||||
} else {
|
||||
return this.$moment(d).fromNow()
|
||||
}
|
||||
},
|
||||
markAsRead(msg) {
|
||||
const url = `/api/v1/notifications/site-message/mark-as-read/`
|
||||
this.$axios.patch(url, { ids: [msg.id] }).then(res => {
|
||||
this.msgDetailVisible = false
|
||||
this.getMessages()
|
||||
}).catch(err => {
|
||||
this.$message(err.detail)
|
||||
})
|
||||
},
|
||||
cancelRead() {
|
||||
this.msgDetailVisible = false
|
||||
},
|
||||
enablePullMsgCount() {
|
||||
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||
const port = document.location.port ? ':' + document.location.port : ''
|
||||
const url = '/ws/notifications/site-msg/'
|
||||
const wsURL = scheme + '://' + document.location.hostname + port + url
|
||||
|
||||
const ws = new WebSocket(wsURL)
|
||||
ws.onopen = (event) => {
|
||||
this.$log.debug('Websocket connected: ', event)
|
||||
}
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data)
|
||||
this.$log.debug('Data: ', data)
|
||||
const unreadCount = data['unread_count']
|
||||
if (unreadCount !== undefined) {
|
||||
this.unreadMsgCount = unreadCount
|
||||
}
|
||||
} catch (e) {
|
||||
this.$log.debug('Recv site message error')
|
||||
}
|
||||
}
|
||||
ws.onerror = (error) => {
|
||||
this.$message.error(this.$t('common.ConnectWebSocketError'))
|
||||
this.$log.debug('site message ws error: ', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-badge ::v-deep .el-badge__content.is-fixed{
|
||||
top:10px;
|
||||
}
|
||||
|
||||
.msg-list {
|
||||
padding: 0 25px 20px;
|
||||
}
|
||||
|
||||
>>> .site-msg {
|
||||
.el-drawer__header {
|
||||
border-bottom: solid 1px rgb(231, 234, 239);
|
||||
margin-bottom: 0;
|
||||
padding-top: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.el-drawer__body {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.msg-item {
|
||||
border-bottom: solid 1px rgb(231, 234, 239);
|
||||
padding: 15px 0 10px;
|
||||
position: relative;
|
||||
border-bottom: 1px solid #ddd;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #f2f2f2;
|
||||
padding: 15px 20px 10px;
|
||||
margin: 0 -20px;
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
|
||||
.msg-icon {
|
||||
font-size: 13px;
|
||||
line-height: 13px;
|
||||
}
|
||||
|
||||
&.msg-unread {
|
||||
.msg-item-txt {
|
||||
font-weight: bolder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.msg-item-head {
|
||||
line-height: 20px;
|
||||
color: #888;
|
||||
font-size: 12px;
|
||||
&:after {
|
||||
clear: both;
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.msg-item-head-type {
|
||||
float: left;
|
||||
width: 240px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.msg-item-head-time {
|
||||
float: right;
|
||||
}
|
||||
.msg-item-read-btn {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.msg-item-txt {
|
||||
overflow: hidden;
|
||||
color: #000;
|
||||
padding: 4px 0 0;
|
||||
line-height: 21px;
|
||||
max-height: 21px;
|
||||
display: -webkit-box;
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.msg-detail {
|
||||
padding-left: 20px;
|
||||
|
||||
.msg-detail-time {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.msg-detail-txt {
|
||||
margin-bottom: 20px;
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.no-msg {
|
||||
padding-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
>>> :focus{ outline:0; }
|
||||
</style>
|
||||
@@ -3,26 +3,30 @@
|
||||
<div class="navbar-header">
|
||||
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
</div>
|
||||
<ul class="navbar-right">
|
||||
<li class="header-item header-icon">
|
||||
<SiteMessages />
|
||||
</li>
|
||||
<li class="header-item" style="margin-left: 10px">
|
||||
<div class="navbar-right">
|
||||
<div class="header-item">
|
||||
<Help />
|
||||
</li>
|
||||
<li class="header-item">
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<Language />
|
||||
</li>
|
||||
<li v-if="showTickets" class="header-item">
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
publicSettings.TICKETS_ENABLED
|
||||
&& publicSettings.XPACK_LICENSE_IS_VALID
|
||||
&& !isOrgAuditor
|
||||
"
|
||||
class="header-item"
|
||||
>
|
||||
<Tickets />
|
||||
</li>
|
||||
<li class="header-item">
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<WebTerminal />
|
||||
</li>
|
||||
<li class="header-item header-profile">
|
||||
</div>
|
||||
<div class="header-item header-profile">
|
||||
<AccountDropdown />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -30,7 +34,6 @@
|
||||
import { mapGetters } from 'vuex'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import AccountDropdown from './AccountDropdown'
|
||||
import SiteMessages from './SiteMessages'
|
||||
import Help from './Help'
|
||||
import Language from './Language'
|
||||
import WebTerminal from './WebTerminal'
|
||||
@@ -45,8 +48,7 @@ export default {
|
||||
Language,
|
||||
Help,
|
||||
Tickets,
|
||||
WebTerminal,
|
||||
SiteMessages
|
||||
WebTerminal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -58,17 +60,13 @@ export default {
|
||||
]),
|
||||
isOrgAuditor() {
|
||||
return rolc.getRolesDisplay(this.currentOrgRoles).includes('OrgAuditor') || rolc.getRolesDisplay(this.currentOrgRoles).includes('Auditor')
|
||||
},
|
||||
showTickets() {
|
||||
return this.publicSettings.TICKETS_ENABLED &&
|
||||
this.publicSettings.XPACK_LICENSE_IS_VALID &&
|
||||
!this.isOrgAuditor
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleSideBar() {
|
||||
this.$store.dispatch('app/toggleSideBar')
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -93,19 +91,12 @@ export default {
|
||||
.navbar-right {
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.header-item {
|
||||
line-height: 50px;
|
||||
display: inline-block;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
&:hover {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
}
|
||||
.header-item {
|
||||
line-height: 50px;
|
||||
display: inline-block;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
@@ -117,9 +108,5 @@ export default {
|
||||
.el-header {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div>
|
||||
<el-tabs v-if="submenu.length > 0" slot="submenu" v-model="iActiveMenu" class="page-submenu" @tab-click="handleTabClick">
|
||||
<template v-for="item in submenu">
|
||||
<el-tab-pane :key="item.name" :label-content="item.labelContent" :name="item.name" :disabled="item.disabled">
|
||||
<el-tab-pane :key="item.name" :label-content="item.labelContent" :name="item.name">
|
||||
<span slot="label">
|
||||
{{ item.title }}
|
||||
<slot name="badge" :tab="item.name" />
|
||||
@@ -80,23 +80,22 @@ export default {
|
||||
},
|
||||
getPropActiveTab() {
|
||||
let activeTab = ''
|
||||
let tabObj = null
|
||||
|
||||
const preActiveTabs = [
|
||||
const activeTabs = [
|
||||
this.$route.query[ACTIVE_TAB_KEY],
|
||||
this.$cookie.get(ACTIVE_TAB_KEY),
|
||||
this.activeMenu
|
||||
]
|
||||
|
||||
for (const preTab of preActiveTabs) {
|
||||
for (const tabName in this.tabIndices) {
|
||||
if (preTab && tabName && preTab.toLowerCase() === tabName.toLowerCase()) {
|
||||
return tabName
|
||||
}
|
||||
for (activeTab of activeTabs) {
|
||||
tabObj = this.tabIndices[activeTab]
|
||||
if (tabObj !== undefined) {
|
||||
return activeTab
|
||||
}
|
||||
}
|
||||
|
||||
activeTab = this.submenu[0].name
|
||||
return activeTab
|
||||
return this.submenu[0].name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -40,13 +40,8 @@ Vue.config.productionTip = false
|
||||
import VueCookie from 'vue-cookie'
|
||||
Vue.use(VueCookie)
|
||||
window.$cookie = VueCookie
|
||||
|
||||
const moment = require('moment')
|
||||
require('moment/locale/zh-cn')
|
||||
Vue.use(require('vue-moment'), {
|
||||
moment
|
||||
})
|
||||
|
||||
import VueMoment from 'vue-moment'
|
||||
Vue.use(VueMoment)
|
||||
// logger
|
||||
import VueLogger from 'vuejs-logger'
|
||||
import loggerOptions from './utils/logger'
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
import empty from '@/layout/empty'
|
||||
export default [
|
||||
{
|
||||
path: 'asset-accounts',
|
||||
component: empty,
|
||||
meta: { title: i18n.t('route.AssetAccount') },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'AssetAccountList',
|
||||
component: () => import('@/views/accounts/AssetAccount/AssetAccountList'),
|
||||
meta: { title: i18n.t('route.AssetAccount') }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'application-accounts',
|
||||
component: empty,
|
||||
meta: { title: i18n.t('route.AssetAccount') },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'ApplicationAccountList',
|
||||
component: () => import('@/views/accounts/ApplicationAccount/ApplicationAccountList'),
|
||||
meta: { title: i18n.t('route.ApplicationAccount') }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'gathered-user',
|
||||
component: empty,
|
||||
redirect: '',
|
||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserList') },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/GatheredUser/index'),
|
||||
name: 'GatherUserListIndex',
|
||||
meta: { title: i18n.t('xpack.GatherUser.GatherUser'), activeMenu: '/accounts/gathered-user' }
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/GatheredUser/GatheredUserList'),
|
||||
name: 'GatherUserList',
|
||||
hidden: true,
|
||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserList'), activeMenu: '/accounts/gathered-user' }
|
||||
},
|
||||
{
|
||||
path: 'tasks',
|
||||
component: () => import('@/views/accounts/GatheredUser/TaskList'),
|
||||
name: 'GatherUserTaskList',
|
||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskList'), activeMenu: '/accounts/gathered-user' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tasks/:id',
|
||||
component: () => import('@/views/accounts/GatheredUser/TaskDetail/index'),
|
||||
name: 'GatherUserTaskDetail',
|
||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskDetail'), activeMenu: '/accounts/gathered-user' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tasks/create',
|
||||
component: () => import('@/views/accounts/GatheredUser/TaskCreateUpdate'),
|
||||
name: 'GatherUserTaskCreate',
|
||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskCreate'), activeMenu: '/accounts/gathered-user' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tasks/:id/update',
|
||||
component: () => import('@/views/accounts/GatheredUser/TaskCreateUpdate'),
|
||||
name: 'GatherUserTaskUpdate',
|
||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskUpdate'), action: 'update', activeMenu: '/accounts/gathered-user' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'change-auth-plan',
|
||||
component: empty,
|
||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'), activeMenu: '/accounts/change-auth-plan/plan' },
|
||||
children: [
|
||||
{
|
||||
path: 'plan',
|
||||
component: () => import('@/views/accounts/ChangeAuthPlan/ChangeAuthPlanList.vue'),
|
||||
name: 'ChangeAuthPlanList',
|
||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'), activeMenu: '/accounts/change-auth-plan/plan' }
|
||||
},
|
||||
{
|
||||
path: 'plan/create',
|
||||
component: () => import('@/views/accounts/ChangeAuthPlan/ChangeAuthPlanCreateUpdate.vue'),
|
||||
name: 'ChangeAuthPlanCreate',
|
||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlanCreate'), activeMenu: '/accounts/change-auth-plan/plan', action: 'create' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'plan/:id/update',
|
||||
component: () => import('@/views/accounts/ChangeAuthPlan/ChangeAuthPlanCreateUpdate.vue'),
|
||||
name: 'ChangeAuthPlanUpdate',
|
||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlanUpdate'), activeMenu: '/accounts/change-auth-plan/plan', action: 'update' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'plan/:id',
|
||||
component: () => import('@/views/accounts/ChangeAuthPlan/ChangeAuthPlanDetail/index.vue'),
|
||||
name: 'ChangeAuthPlanDetail',
|
||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'), activeMenu: '/accounts/change-auth-plan/plan' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'plan-execution/:id',
|
||||
component: () => import('@/views/accounts/ChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanExecution/ChangeAuthPlanExecutionDetail/index.vue'),
|
||||
name: 'ChangeAuthPlanExecutionDetail',
|
||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ExecutionDetail'), activeMenu: '/accounts/change-auth-plan/plan' },
|
||||
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
|
||||
},
|
||||
|
||||
@@ -37,7 +37,6 @@ import TicketsRoutes from './tickets'
|
||||
import AuditsRoutes from './audits'
|
||||
import commonRoutes from './common'
|
||||
import aclRoutes from './acl'
|
||||
import AccountRoutes from './accounts'
|
||||
|
||||
/**
|
||||
* constantRoutes
|
||||
@@ -111,18 +110,6 @@ export const allRoleRoutes = [
|
||||
meta: { title: i18n.t('route.Applications'), icon: 'th' },
|
||||
children: ApplicationsRoute
|
||||
},
|
||||
{
|
||||
path: '/accounts',
|
||||
component: Layout,
|
||||
redirect: '/accounts/asset-accounts/',
|
||||
name: 'Accounts',
|
||||
meta: {
|
||||
licenseRequired: true,
|
||||
title: i18n.t('route.Accounts'),
|
||||
icon: 'address-book'
|
||||
},
|
||||
children: AccountRoutes
|
||||
},
|
||||
{
|
||||
path: '/perms/',
|
||||
component: Layout,
|
||||
|
||||
@@ -48,12 +48,5 @@ export default [
|
||||
component: () => import('@/views/tickets/RequestApplicationPerm/Detail/index'),
|
||||
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tickets/command-confirm/:id',
|
||||
name: 'CommandConfirmDetail',
|
||||
component: () => import('@/views/tickets/CommandConfirm/Detail/index'),
|
||||
meta: { title: i18n.t('route.CommandConfirm'), activeMenu: '/tickets/tickets' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -65,7 +65,7 @@ export default [
|
||||
path: '',
|
||||
name: 'CommandExecutions',
|
||||
component: () => import('@/views/ops/CommandExecution'),
|
||||
meta: { title: i18n.t('route.BatchCommand'), icon: 'terminal', permissions: [rolec.PERM_USE] }
|
||||
meta: { title: i18n.t('route.CommandExecutions'), icon: 'terminal', permissions: [rolec.PERM_USE] }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -124,13 +124,6 @@ export default [
|
||||
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets', permissions: [rolec.PERM_USE] },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tickets/command-confirm/:id',
|
||||
name: 'CommandConfirmDetail',
|
||||
component: () => import('@/views/tickets/CommandConfirm/Detail/index'),
|
||||
meta: { title: i18n.t('route.CommandConfirm'), activeMenu: '/tickets/tickets', permissions: [rolec.PERM_USE] },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'tickets/:id',
|
||||
name: 'TicketDetail',
|
||||
@@ -162,7 +155,7 @@ export default [
|
||||
children: [
|
||||
{
|
||||
path: `${BASE_URL}/koko/elfinder/sftp/`,
|
||||
meta: { title: i18n.t('route.FileManager'), icon: 'file', activeMenu: '/assets', permissions: [rolec.PERM_USE] }
|
||||
meta: { title: i18n.t('route.WebFTP'), icon: 'file', activeMenu: '/assets', permissions: [rolec.PERM_USE] }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
title: '.',
|
||||
|
||||
title: 'JumpServer',
|
||||
|
||||
/**
|
||||
* @type {boolean} true | false
|
||||
|
||||
@@ -35,18 +35,15 @@ const actions = {
|
||||
getPublicSettings({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getPublicSettings().then(response => {
|
||||
const faviconURL = response.data.LOGO_URLS.favicon
|
||||
let link = document.querySelector("link[rel*='icon']")
|
||||
if (!link) {
|
||||
link = document.createElement('link')
|
||||
link.type = 'image/x-icon'
|
||||
link.rel = 'shortcut icon'
|
||||
document.getElementsByTagName('head')[0].appendChild(link)
|
||||
}
|
||||
link.href = faviconURL
|
||||
const link = document.querySelector("link[rel*='icon']") || document.createElement('link')
|
||||
link.type = 'image/x-icon'
|
||||
link.rel = 'shortcut icon'
|
||||
link.href = response.data.LOGO_URLS.favicon
|
||||
document.getElementsByTagName('head')[0].appendChild(link)
|
||||
|
||||
// 动态修改Title
|
||||
document.title = response.data.LOGIN_TITLE
|
||||
if (response.data.LOGIN_TITLE) { document.title = response.data.LOGIN_TITLE }
|
||||
|
||||
commit('SET_PUBLIC_SETTINGS', response.data)
|
||||
resolve(response)
|
||||
}).catch(error => {
|
||||
|
||||
@@ -20,9 +20,7 @@ const getDefaultState = () => {
|
||||
orgs: [],
|
||||
perms: 0b00000000,
|
||||
MFAVerifyAt: null,
|
||||
isSuperAdmin: false,
|
||||
hasAdminPerm: false,
|
||||
hasAuditPerm: false
|
||||
isSuperAdmin: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,15 +53,12 @@ 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)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import Page from '@/layout/components/Page/index'
|
||||
import { ActionsFormatter } from '@/components/TableFormatters'
|
||||
import { ActionsFormatter } from '@/components/ListTable/formatters'
|
||||
|
||||
export default {
|
||||
name: 'DatabaseApp',
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import Page from '@/layout/components/Page/index'
|
||||
import { ActionsFormatter } from '@/components/TableFormatters'
|
||||
import { ActionsFormatter } from '@/components/ListTable/formatters'
|
||||
|
||||
export default {
|
||||
name: 'KubernetesApp',
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import Page from '@/layout/components/Page/index'
|
||||
import { ActionsFormatter } from '@/components/TableFormatters'
|
||||
import { ActionsFormatter } from '@/components/ListTable/formatters'
|
||||
|
||||
export default {
|
||||
name: 'RemoteApp',
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<script>
|
||||
import GenericTreeListPage from '@/layout/components/GenericTreeListPage/index'
|
||||
import { ActionsFormatter, SystemUserFormatter, DialogDetailFormatter } from '@/components/TableFormatters'
|
||||
import { LoadingActionsFormatter, SystemUserFormatter, DialogDetailFormatter } from '@/components/ListTable/formatters'
|
||||
export default {
|
||||
components: {
|
||||
GenericTreeListPage
|
||||
@@ -104,7 +104,7 @@ export default {
|
||||
{
|
||||
prop: 'id',
|
||||
align: 'center',
|
||||
formatter: ActionsFormatter,
|
||||
formatter: LoadingActionsFormatter,
|
||||
width: '100px',
|
||||
label: this.$t('common.action'),
|
||||
formatterArgs: {
|
||||
@@ -117,7 +117,7 @@ export default {
|
||||
name: 'connect',
|
||||
fa: 'fa-terminal',
|
||||
type: 'primary',
|
||||
can: function({ row, cellValue }) {
|
||||
can: (row, cellValue) => {
|
||||
return row.is_active
|
||||
},
|
||||
callback: function({ row, col, cellValue, reload }) {
|
||||
@@ -127,7 +127,7 @@ export default {
|
||||
{
|
||||
name: 'favor',
|
||||
type: 'info',
|
||||
fa: function({ row, cellValue }) {
|
||||
fa: function(row, cellValue) {
|
||||
if (this.checkFavorite(row.id)) {
|
||||
return 'fa-star'
|
||||
}
|
||||
@@ -160,25 +160,20 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
refreshAllFavorites() {
|
||||
const actionsIndex = this.tableConfig.columns.length - 1
|
||||
this.tableConfig.columns[actionsIndex].formatterArgs.loading = true
|
||||
this.tableConfig.columns[this.tableConfig.columns.length - 1].formatterArgs.loading = true
|
||||
this.$axios.get('/api/v1/assets/favorite-assets/').then(resp => {
|
||||
this.allFavorites = resp
|
||||
this.tableConfig.columns[actionsIndex].formatterArgs.loading = false
|
||||
this.tableConfig.columns[this.tableConfig.columns.length - 1].formatterArgs.loading = false
|
||||
})
|
||||
},
|
||||
addOrDeleteFavorite(assetId) {
|
||||
if (this.checkFavorite(assetId)) {
|
||||
this.$axios.delete(`/api/v1/assets/favorite-assets/?asset=${assetId}`).then(
|
||||
res => this.removeFavorite(assetId)
|
||||
)
|
||||
this.$axios.delete(`/api/v1/assets/favorite-assets/?asset=${assetId}`).then(res => this.removeFavorite(assetId))
|
||||
} else {
|
||||
const data = {
|
||||
asset: assetId
|
||||
}
|
||||
this.$axios.post('/api/v1/assets/favorite-assets/', data).then(
|
||||
res => this.addFavorite(assetId)
|
||||
)
|
||||
this.$axios.post('/api/v1/assets/favorite-assets/', data).then(res => this.addFavorite(assetId))
|
||||
}
|
||||
},
|
||||
checkFavorite(assetId) {
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm'
|
||||
import UserPassword from '@/components/FormFields/UserPassword'
|
||||
import UserPassword from '@/components/UserPassword'
|
||||
import { IBox } from '@/components'
|
||||
import rules from '@/components/DataForm/rules'
|
||||
|
||||
export default {
|
||||
name: 'PasswordUpdate',
|
||||
@@ -43,8 +42,8 @@ export default {
|
||||
},
|
||||
new_password: {
|
||||
label: this.$t('users.NewPassword'),
|
||||
rules: [rules.RequiredChange],
|
||||
component: UserPassword
|
||||
component: UserPassword,
|
||||
rules: []
|
||||
},
|
||||
new_password_again: {
|
||||
label: this.$t('users.ConfirmPassword'),
|
||||
|
||||
@@ -1,49 +1,23 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="14">
|
||||
<DetailCard :items="detailCardItems" />
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<QuickActions type="primary" :actions="quickActions" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<Dialog
|
||||
width="50"
|
||||
top="20vh"
|
||||
:title="this.$t('common.PasswordConfirm')"
|
||||
:visible.sync="showPasswordDialog"
|
||||
:show-confirm="false"
|
||||
:show-cancel="false"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4">
|
||||
<div style="line-height: 34px;text-align: center">{{ $t('assets.Password') }}</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-input v-model="passwordInput" type="password" />
|
||||
<span class="help-tips help-block">{{ $t('common.PasswordRequireForSecurity') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button size="mini" type="primary" style="line-height:20px " @click="passConfirm">{{ this.$t('common.Confirm') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="14">
|
||||
<DetailCard :items="detailCardItems" />
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<QuickActions type="primary" :actions="quickActions" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import DetailCard from '@/components/DetailCard'
|
||||
import QuickActions from '@/components/QuickActions'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
export default {
|
||||
name: 'ProfileInfo',
|
||||
components: {
|
||||
DetailCard,
|
||||
QuickActions,
|
||||
Dialog
|
||||
QuickActions
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
@@ -54,40 +28,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
url: `/api/v1/users/profile/`,
|
||||
showPasswordDialog: false,
|
||||
passwordInput: '',
|
||||
currentEdit: '',
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('users.setWeCom'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$store.state.users.profile.is_wecom_bound ? this.$t('common.unbind') : this.$t('common.bind'),
|
||||
disabled: this.$store.state.users.profile.source !== 'local'
|
||||
},
|
||||
has: this.$store.getters.publicSettings.AUTH_WECOM,
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.currentEdit = 'wecom'
|
||||
this.showPasswordDialog = true
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('users.setDingTalk'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$store.state.users.profile.is_dingtalk_bound ? this.$t('common.unbind') : this.$t('common.bind'),
|
||||
disabled: this.$store.state.users.profile.source !== 'local'
|
||||
},
|
||||
has: this.$store.getters.publicSettings.AUTH_DINGTALK,
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.currentEdit = 'dingtalk'
|
||||
this.showPasswordDialog = true
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('users.SetMFA'),
|
||||
attrs: {
|
||||
@@ -138,7 +79,7 @@ export default {
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('common.Update'),
|
||||
disabled: !this.$store.state.users.profile.can_public_key_auth
|
||||
disabled: this.$store.state.users.profile.source !== 'local'
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
@@ -150,8 +91,7 @@ export default {
|
||||
title: this.$t('users.ResetPublicKeyAndDownload'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('common.Reset'),
|
||||
disabled: !this.$store.state.users.profile.can_public_key_auth
|
||||
label: this.$t('common.Reset')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
@@ -230,24 +170,6 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
passConfirm() {
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/password/verify/`, {
|
||||
password: this.passwordInput
|
||||
}
|
||||
).then(res => {
|
||||
if (!this.object[`is_${this.currentEdit}_bound`]) {
|
||||
window.location.href = `/core/auth/${this.currentEdit}/qr/bind/?redirect_url=${this.$route.fullPath}`
|
||||
} else {
|
||||
this.$axios.post(`/api/v1/authentication/${this.currentEdit}/qr/unbind/`).then(res => {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
this.$store.dispatch('users/getProfile')
|
||||
})
|
||||
}
|
||||
})
|
||||
this.passwordInput = ''
|
||||
this.showPasswordDialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -38,7 +38,7 @@ export default {
|
||||
],
|
||||
fieldsMeta: {
|
||||
public_key_comment: {
|
||||
label: this.$t('common.Name'),
|
||||
label: this.$t('users.Name'),
|
||||
disabled: true
|
||||
},
|
||||
public_key_hash_md5: {
|
||||
|
||||
@@ -37,7 +37,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getSubmenu() {
|
||||
return [
|
||||
let submenu = [
|
||||
{
|
||||
title: this.$t('common.BasicInfo'),
|
||||
name: 'ProfileInfo'
|
||||
@@ -45,18 +45,21 @@ export default {
|
||||
{
|
||||
title: this.$t('users.ProfileSetting'),
|
||||
name: 'ProfileUpdate'
|
||||
},
|
||||
{
|
||||
title: this.$t('users.LoginPasswordSetting'),
|
||||
name: 'PasswordUpdate',
|
||||
disabled: this.$store.state.users.profile.source !== 'local'
|
||||
},
|
||||
{
|
||||
title: this.$t('users.SSHKeySetting'),
|
||||
name: 'SSHUpdate',
|
||||
disabled: !this.$store.state.users.profile.can_public_key_auth
|
||||
}
|
||||
]
|
||||
if (this.$store.state.users.profile.source === 'local') {
|
||||
submenu = submenu.concat([
|
||||
{
|
||||
title: this.$t('users.LoginPasswordSetting'),
|
||||
name: 'PasswordUpdate'
|
||||
},
|
||||
{
|
||||
title: this.$t('users.SSHKeySetting'),
|
||||
name: 'SSHUpdate'
|
||||
}
|
||||
])
|
||||
}
|
||||
return submenu
|
||||
},
|
||||
handleUpdate(value) {
|
||||
this.config.activeMenu = value
|
||||
|
||||
@@ -88,14 +88,11 @@ export function toSafeLocalDateStr(d) {
|
||||
}
|
||||
|
||||
export function getApiPath(that) {
|
||||
let pagePath = that.$route.path
|
||||
const pagePathArray = pagePath.split('/')
|
||||
if (pagePathArray.indexOf('orgs') !== -1) {
|
||||
pagePathArray[pagePathArray.indexOf('xpack')] = 'orgs'
|
||||
} else if (pagePathArray.indexOf('gathered-user') !== -1 || pagePathArray.indexOf('change-auth-plan') !== -1) {
|
||||
pagePathArray[pagePathArray.indexOf('accounts')] = 'xpack'
|
||||
const pagePath = that.$route.path
|
||||
const isOrgPath = pagePath.split('/').indexOf('orgs') !== -1
|
||||
if (isOrgPath) {
|
||||
return `/api/v1/orgs/orgs/${pagePath.split('/').pop()}/`
|
||||
}
|
||||
pagePath = pagePathArray.join('/')
|
||||
return `/api/v1${pagePath}/`
|
||||
}
|
||||
|
||||
@@ -209,27 +206,6 @@ function customizer(objValue, srcValue) {
|
||||
return _.isUndefined(objValue) ? srcValue : objValue
|
||||
}
|
||||
|
||||
export function newURL(url) {
|
||||
let obj
|
||||
if (url.indexOf('//') > -1) {
|
||||
obj = new URL(url)
|
||||
} else {
|
||||
obj = new URL(url, location.origin)
|
||||
}
|
||||
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
|
||||
|
||||
@@ -80,11 +80,6 @@ export function hasUserPagePerm(userPerm) {
|
||||
}
|
||||
|
||||
export function hasPerm(source, target) {
|
||||
if (target === null) {
|
||||
return true
|
||||
} else if (source === null) {
|
||||
return false
|
||||
}
|
||||
if (typeof source !== 'object') {
|
||||
source = [source]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// import getPageTitle from '@/utils/get-page-title'
|
||||
import getPageTitle from '@/utils/get-page-title'
|
||||
import store from '@/store'
|
||||
import router from '@/router'
|
||||
import { Message } from 'element-ui'
|
||||
@@ -15,9 +15,9 @@ function reject(msg) {
|
||||
return new Promise((resolve, reject) => reject(msg))
|
||||
}
|
||||
|
||||
// function setHeadTitle({ to, from, next }) {
|
||||
// document.title = getPageTitle(to.meta.title)
|
||||
// }
|
||||
function setHeadTitle({ to, from, next }) {
|
||||
document.title = getPageTitle(to.meta.title)
|
||||
}
|
||||
|
||||
async function checkLogin({ to, from, next }) {
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
@@ -145,7 +145,7 @@ export async function startup({ to, from, next }) {
|
||||
|
||||
// set page title
|
||||
await getPublicSetting({ to, from, next })
|
||||
// await setHeadTitle({ to, from, next })
|
||||
await setHeadTitle({ to, from, next })
|
||||
await checkLogin({ to, from, next })
|
||||
await changeCurrentOrgIfNeed({ to, from, next })
|
||||
await changeCurrentRoleIfNeed({ to, from, next })
|
||||
|
||||
@@ -1,401 +0,0 @@
|
||||
<template>
|
||||
<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 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: {
|
||||
GenericListTable, Page, Dialog
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
isInit: true,
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
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>
|
||||
.application-table ::v-deep .row-clicked, .application-user-table ::v-deep .row-background-color {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.application-table {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
& ::v-deep .el-table__row{
|
||||
height: 40px;
|
||||
& > td{
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.export-item {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 10px 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,185 +0,0 @@
|
||||
<template>
|
||||
<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"
|
||||
v-bind="leftTable"
|
||||
@row-click="leftTable.tableConfig.rowClick"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="iShowTree?11:13">
|
||||
<AccountListTable
|
||||
ref="RightTable"
|
||||
class="asset-user-table"
|
||||
v-bind="rightTable"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Page from '@/layout/components/Page'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import AutoDataZTree from '@/components/AutoDataZTree/index'
|
||||
import { AccountListTable } from '@/components'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'AssetAccountList',
|
||||
components: {
|
||||
AutoDataZTree, GenericListTable, Page, AccountListTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
isInit: true,
|
||||
clickedRow: null,
|
||||
iShowTree: true,
|
||||
treeSetting: {
|
||||
showMenu: false,
|
||||
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
|
||||
}
|
||||
}
|
||||
},
|
||||
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/accounts/?asset=${row.id}`
|
||||
vm.rightTable.extraQuery.asset = 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/accounts/`,
|
||||
name: 'AssetAccountListTable',
|
||||
extraQuery: {
|
||||
latest: 1
|
||||
},
|
||||
hasLeftActions: false,
|
||||
searchExclude: ['hostname', 'id', 'ip']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</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;
|
||||
}
|
||||
& >>> .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;
|
||||
}
|
||||
</style>
|
||||
@@ -1,249 +0,0 @@
|
||||
<template>
|
||||
<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 GenericListTable from '@/layout/components/GenericListTable'
|
||||
import AutoDataZTree from '@/components/AutoDataZTree/index'
|
||||
import { ChoicesFormatter, DetailFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'AssetAccountList',
|
||||
components: {
|
||||
AutoDataZTree, GenericListTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
isInit: true,
|
||||
clickedRow: {},
|
||||
iShowTree: true,
|
||||
treeSetting: {
|
||||
showMenu: false,
|
||||
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
|
||||
}
|
||||
}
|
||||
},
|
||||
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
|
||||
},
|
||||
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']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</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;
|
||||
}
|
||||
& >>> .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>
|
||||
@@ -1,70 +0,0 @@
|
||||
<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('common.Name'),
|
||||
value: this.object.name
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.Cloud.PeriodicPerform'),
|
||||
value: this.object.is_periodic ? (this.$t('xpack.Cloud.True')) : (this.$t('xpack.Cloud.False'))
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.Cloud.Periodic'),
|
||||
value: this.object.periodic_display
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.Cloud.DateLastSync'),
|
||||
value: this.object.date_last_sync ? toSafeLocalDateStr(this.object.date_created) : ''
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.Cloud.DateCreated'),
|
||||
value: this.object.date_created ? toSafeLocalDateStr(this.object.date_created) : ''
|
||||
},
|
||||
{
|
||||
key: this.$t('common.Comment'),
|
||||
value: this.object.comment
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,64 +0,0 @@
|
||||
<template>
|
||||
<GenericListTable ref="GenericListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericListTable from '@/layout/components/GenericListTable/index'
|
||||
import { BooleanFormatter, DateFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'TaskExecutionList',
|
||||
components: { GenericListTable },
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasBulkDelete: false,
|
||||
hasImport: false,
|
||||
hasExport: false,
|
||||
hasSearch: true
|
||||
},
|
||||
tableConfig: {
|
||||
url: `/api/v1/xpack/gathered-user/task-executions/?task=${this.object.id}`,
|
||||
columns: [
|
||||
{
|
||||
prop: 'timedelta',
|
||||
label: this.$t('ops.timeDelta'),
|
||||
formatter: function(row) {
|
||||
return row.timedelta.toFixed(2) + 's'
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'date_start',
|
||||
label: this.$t('common.DateStart'),
|
||||
formatter: DateFormatter
|
||||
},
|
||||
{
|
||||
prop: 'success',
|
||||
label: this.$t('common.Success'),
|
||||
width: '90px',
|
||||
align: 'center',
|
||||
formatter: BooleanFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
false: 'fa-times text-danger',
|
||||
true: 'fa-check text-primary'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -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: {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user