mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-29 21:28:52 +00:00
perf: 修改我的资产 table
This commit is contained in:
@@ -33,6 +33,10 @@ export default {
|
||||
vm.tableConfig.url = url
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
getShowUrl: {
|
||||
type: Function,
|
||||
default({ row, col }) {
|
||||
@@ -62,7 +66,7 @@ export default {
|
||||
columnsExclude: ['spec_info'],
|
||||
columnsShow: {
|
||||
min: ['name', 'address', 'accounts'],
|
||||
default: ['name', 'address', 'platform', 'view_account', 'connectivity']
|
||||
default: ['name', 'address', 'platform', 'connectivity', 'view_account', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
@@ -71,8 +75,14 @@ export default {
|
||||
route: 'AssetDetail'
|
||||
}
|
||||
},
|
||||
labels: {
|
||||
formatterArgs: {
|
||||
showEditBtn: false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
has: false
|
||||
// has: this.actions !== null,
|
||||
...this.actions
|
||||
},
|
||||
view_account: {
|
||||
label: this.$t('Account'),
|
||||
@@ -80,6 +90,11 @@ export default {
|
||||
width: '100px'
|
||||
},
|
||||
connectivity: connectivityMeta
|
||||
},
|
||||
tableAttrs: {
|
||||
rowClassName({ row }) {
|
||||
return !row.is_active ? 'row_disabled' : ''
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
@@ -102,4 +117,8 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.row_disabled,.row_disabled:hover,.row_disabled:hover > td{
|
||||
cursor: not-allowed;
|
||||
background-color:rgba(192,196,204,0.28) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<IBox v-if="loading" style="width: 100%; height: 200px" />
|
||||
<div v-else>
|
||||
<DetailCard v-if="hasObject && items.length > 0" :items="items" :loading="loading" v-bind="$attrs" />
|
||||
<DetailCard v-if="hasObject && items.length > 0" :items="validItems" :loading="loading" v-bind="$attrs" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -59,6 +59,9 @@ export default {
|
||||
},
|
||||
hasObject() {
|
||||
return Object.keys(this.iObject).length > 0
|
||||
},
|
||||
validItems() {
|
||||
return this.items.filter(item => this.isHidden(item))
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
@@ -82,6 +85,77 @@ export default {
|
||||
}
|
||||
return formatter
|
||||
},
|
||||
isHidden(item) {
|
||||
let has = item.has
|
||||
if (typeof has === 'function') {
|
||||
has = has()
|
||||
}
|
||||
if (has === undefined) {
|
||||
has = true
|
||||
}
|
||||
return has
|
||||
},
|
||||
parseValue(value, tp) {
|
||||
if (value === null || value === '') {
|
||||
value = '-'
|
||||
} else if (value === 0) {
|
||||
value = 0
|
||||
} else if (tp === 'datetime') {
|
||||
value = toSafeLocalDateStr(value)
|
||||
} else if (tp === 'labeled_choice') {
|
||||
value = value?.['label']
|
||||
} else if (tp === 'related_field' || tp === 'nested object' || value?.name) {
|
||||
value = value?.['name']
|
||||
} else if (tp === 'm2m_related_field') {
|
||||
value = value?.map(item => item['name']).join(', ')
|
||||
} else if (tp === 'boolean') {
|
||||
value = value ? this.$t('Yes') : this.$t('No')
|
||||
}
|
||||
return value
|
||||
},
|
||||
parseArrayValue(value, excludes, label) {
|
||||
if (Array.isArray(value)) {
|
||||
const tp = typeof value[0]
|
||||
for (const [index, item] of value.entries()) {
|
||||
let object = {}
|
||||
if (tp === 'object') {
|
||||
const firstValue = value[0]
|
||||
if (firstValue.hasOwnProperty('name')) {
|
||||
value.forEach(item => {
|
||||
const fieldName = `${name}.${item.name}`
|
||||
if (excludes.includes(fieldName)) {
|
||||
return
|
||||
}
|
||||
object = {
|
||||
key: item.label,
|
||||
value: item.value
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const fieldName = `${name}.${item.name}`
|
||||
if (excludes.includes(fieldName)) {
|
||||
continue
|
||||
}
|
||||
object = {
|
||||
key: item.label,
|
||||
value: item.value
|
||||
}
|
||||
}
|
||||
} else if (tp === 'string') {
|
||||
object = {
|
||||
value: value[index]
|
||||
}
|
||||
if (index === 0) {
|
||||
object['key'] = label
|
||||
}
|
||||
}
|
||||
if (index !== value.length - 1) {
|
||||
object['class'] = 'array-item'
|
||||
}
|
||||
this.items.push(object)
|
||||
}
|
||||
}
|
||||
},
|
||||
async optionAndGenFields() {
|
||||
const data = await this.$store.dispatch('common/getUrlMeta', { url: this.url })
|
||||
let remoteMeta = data.actions['GET'] || {}
|
||||
@@ -94,6 +168,7 @@ export default {
|
||||
const excludes = (this.excludes || []).concat(defaultExcludes)
|
||||
fields = fields.filter(item => !excludes.includes(item))
|
||||
const defaultFormatter = this.defaultFormatter(fields)
|
||||
|
||||
for (const name of fields) {
|
||||
if (typeof name === 'object') {
|
||||
this.items.push(name)
|
||||
@@ -121,62 +196,10 @@ export default {
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const tp = typeof value[0]
|
||||
for (const [index, item] of value.entries()) {
|
||||
let object = {}
|
||||
if (tp === 'object') {
|
||||
const firstValue = value[0]
|
||||
if (firstValue.hasOwnProperty('name')) {
|
||||
value.forEach(item => {
|
||||
const fieldName = `${name}.${item.name}`
|
||||
if (excludes.includes(fieldName)) {
|
||||
return
|
||||
}
|
||||
object = {
|
||||
key: item.label,
|
||||
value: item.value
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const fieldName = `${name}.${item.name}`
|
||||
if (excludes.includes(fieldName)) {
|
||||
continue
|
||||
}
|
||||
object = {
|
||||
key: item.label,
|
||||
value: item.value
|
||||
}
|
||||
}
|
||||
} else if (tp === 'string') {
|
||||
object = {
|
||||
value: value[index]
|
||||
}
|
||||
if (index === 0) {
|
||||
object['key'] = label
|
||||
}
|
||||
}
|
||||
if (index !== value.length - 1) {
|
||||
object['class'] = 'array-item'
|
||||
}
|
||||
this.items.push(object)
|
||||
}
|
||||
this.parseArrayValue(value, excludes, label)
|
||||
continue
|
||||
}
|
||||
if (value === null || value === '') {
|
||||
value = '-'
|
||||
} else if (value === 0) {
|
||||
value = 0
|
||||
} else if (fieldMeta.type === 'datetime') {
|
||||
value = toSafeLocalDateStr(value)
|
||||
} else if (fieldMeta.type === 'labeled_choice') {
|
||||
value = value?.['label']
|
||||
} else if (fieldMeta.type === 'related_field' || fieldMeta.type === 'nested object' || value?.name) {
|
||||
value = value?.['name']
|
||||
} else if (fieldMeta.type === 'm2m_related_field') {
|
||||
value = value?.map(item => item['name']).join(', ')
|
||||
} else if (fieldMeta.type === 'boolean') {
|
||||
value = value ? this.$t('Yes') : this.$t('No')
|
||||
}
|
||||
value = this.parseValue(value, fieldMeta.type)
|
||||
|
||||
if (value === undefined) {
|
||||
if (this.showUndefine) {
|
||||
|
||||
@@ -85,13 +85,8 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
}
|
||||
|
||||
>>> .el-form-item__label {
|
||||
padding-right: 8%;
|
||||
//white-space: nowrap;
|
||||
//text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
span {
|
||||
@@ -102,6 +97,7 @@ export default {
|
||||
|
||||
>>> .el-form-item__content {
|
||||
font-size: 13px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
>>> .el-tag--mini {
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
@show="getAsyncItems"
|
||||
>
|
||||
<div class="detail-content">
|
||||
<div v-if="accountData.length === 0" class="empty-item">
|
||||
<span>{{ $t('No accounts') }}</span>
|
||||
</div>
|
||||
<div v-for="account of accountData" :key="account.id" class="detail-item">
|
||||
<span>{{ account.name }}({{ account.username }})</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-button slot="reference" size="mini" type="primary">{{ $t('View') }}</el-button>
|
||||
<el-button slot="reference" size="mini" type="text">{{ $t('View') }}</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
@@ -34,7 +37,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async getAsyncItems() {
|
||||
const userId = this.$route.params.id
|
||||
const userId = this.$route.params.id || 'self'
|
||||
const url = `/api/v1/perms/users/${userId}/assets/${this.row.id}`
|
||||
this.$axios.get(url).then(res => {
|
||||
this.accountData = res?.permed_accounts || []
|
||||
@@ -48,6 +51,7 @@ export default {
|
||||
.detail-content {
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
@@ -59,4 +63,12 @@ export default {
|
||||
background-color: #F5F7FA;
|
||||
}
|
||||
}
|
||||
|
||||
.el-button--text {
|
||||
color: var(--color-link);
|
||||
|
||||
&:hover {
|
||||
color: var(--color-link);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -246,11 +246,11 @@ export default {
|
||||
|
||||
.organization {
|
||||
border-radius: 3px;
|
||||
background-color: rgba(255, 255, 255, .15);
|
||||
background-color: rgba(0, 0, 0, .12);
|
||||
padding-left: 10px !important;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, .12);
|
||||
background-color: rgba(0, 0, 0, .18);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -98,7 +98,7 @@ export default {
|
||||
}
|
||||
|
||||
.page-content {
|
||||
height: calc(100% - 90px);
|
||||
height: calc(100% - 10px);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -31,11 +31,7 @@
|
||||
.el-menu-item, .el-submenu-sidebar .el-menu-item {
|
||||
background-color: $subMenuBg;
|
||||
color: $menuText;
|
||||
list-style: circle inside;
|
||||
|
||||
span {
|
||||
//padding-left: 10px;
|
||||
}
|
||||
list-style: disc inside;
|
||||
|
||||
&.submenu-title-noDropdown {
|
||||
list-style: none;
|
||||
@@ -56,10 +52,6 @@
|
||||
color: $subMenuActiveText;
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
i {
|
||||
//color: $menuText;
|
||||
}
|
||||
}
|
||||
|
||||
i.fa {
|
||||
|
||||
@@ -200,10 +200,10 @@ export default {
|
||||
label: 'PostgreSQL', value: 'postgresql'
|
||||
},
|
||||
{
|
||||
label: 'SQL Server', value: 'sqlserver'
|
||||
label: 'SQLServer', value: 'sqlserver'
|
||||
},
|
||||
{
|
||||
label: 'HUAWEI', value: 'huawei'
|
||||
label: 'CloudEngine', value: 'huawei'
|
||||
}
|
||||
],
|
||||
callback: (option) => {
|
||||
|
||||
@@ -18,8 +18,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
treeUrl: `/api/v1/perms/users/${this.object.id}/nodes/children/tree/?cache_policy=1`,
|
||||
tableUrl: `/api/v1/perms/users/${this.object.id}/assets/?cache_policy=1&all=1`
|
||||
treeUrl: `/api/v1/perms/users/${this.object.id}/nodes/children/tree/`,
|
||||
tableUrl: `/api/v1/perms/users/${this.object.id}/assets/?all=1`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +202,9 @@ export default {
|
||||
key: this.$t('OrgsAndRoles'),
|
||||
has: this.$store.getters.currentOrgIsRoot,
|
||||
formatter: (item, val) => {
|
||||
if (!this.$store.getters.currentOrgIsRoot) {
|
||||
return ''
|
||||
}
|
||||
const doms = []
|
||||
const orgsRoles = this.object.orgs_roles
|
||||
const allowKeyMaxLength = 50
|
||||
@@ -211,14 +214,14 @@ export default {
|
||||
prettyKey = key.substring(0, allowKeyMaxLength - 3) + '...'
|
||||
}
|
||||
const item = prettyKey + ': ' + value.join(', ')
|
||||
doms.push([item, <br/>])
|
||||
doms.push([item, <br />])
|
||||
})
|
||||
return <div>{doms}</div>
|
||||
}
|
||||
},
|
||||
'mfa_level', 'source',
|
||||
'wecom_id', 'dingtalk_id', 'feishu_id',
|
||||
'mfa_level', 'source', 'created_by', 'date_joined', 'date_expired',
|
||||
'wecom_id', 'dingtalk_id', 'feishu_id', 'mfa_level',
|
||||
'source', 'labels',
|
||||
'created_by', 'date_joined', 'date_expired',
|
||||
'date_password_last_updated', 'last_login', 'comment'
|
||||
],
|
||||
relationConfig: {
|
||||
|
||||
@@ -1,178 +1,54 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericTreeListPage
|
||||
ref="GenericTreeListPage"
|
||||
:header-actions="headerActions"
|
||||
:table-config="tableConfig"
|
||||
:tree-setting="treeSetting"
|
||||
/>
|
||||
</div>
|
||||
<Page>
|
||||
<GrantedAssets :actions="actions" :table-url="tableUrl" :tree-url="treeUrl" />
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericTreeListPage from '@/layout/components/GenericTreeListPage'
|
||||
import { AccountShowFormatter, DialogDetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import { connectivityMeta } from '@/components/Apps/AccountListTable/const'
|
||||
import GrantedAssets from '@/components/Apps/GrantedAssets/index.vue'
|
||||
import Page from '@/layout/components/Page/index.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericTreeListPage
|
||||
Page,
|
||||
GrantedAssets
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
allFavorites: [],
|
||||
treeSetting: {
|
||||
showMenu: false,
|
||||
showRefresh: true,
|
||||
showAssets: false,
|
||||
url: '/api/v1/perms/users/self/users/assets/',
|
||||
nodeUrl: '/api/v1/perms/users/self/nodes/',
|
||||
// ?assets=0不显示资产. =1显示资产
|
||||
treeUrl: '/api/v1/perms/users/self/nodes/children/tree/',
|
||||
callback: {
|
||||
refresh: () => {},
|
||||
onSelected: function(event, treeNode) {
|
||||
if (treeNode.meta.type === 'node') {
|
||||
const currentNodeId = treeNode.meta.data.id
|
||||
this.tableConfig.url = `/api/v1/perms/users/self/nodes/${currentNodeId}/assets/?cache_policy=1`
|
||||
}
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
tableConfig: {
|
||||
url: '/api/v1/perms/users/self/assets/',
|
||||
hasTree: true,
|
||||
columnsExclude: ['spec_info'],
|
||||
columnsShow: {
|
||||
default: ['name', 'address', 'platform', 'accounts', 'is_active', 'actions'],
|
||||
min: ['name', 'address', 'actions']
|
||||
},
|
||||
columns: [
|
||||
'name', 'address', 'domain', 'platform', 'connectivity', 'is_active',
|
||||
'nodes', 'org_name', 'created_by', 'labels', 'accounts', 'comment', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
prop: 'name',
|
||||
formatter: DialogDetailFormatter,
|
||||
formatterArgs: {
|
||||
getDialogTitle: function({ col, row, cellValue }) { this.$t('AssetDetail') }.bind(this),
|
||||
getDetailItems: function({ col, row, cellValue }) {
|
||||
return [
|
||||
{
|
||||
key: this.$t('Name'),
|
||||
value: row.name
|
||||
},
|
||||
{
|
||||
key: this.$t('AssetAddress'),
|
||||
value: row.address
|
||||
},
|
||||
{
|
||||
key: this.$t('Protocols'),
|
||||
formatter: () => {
|
||||
return this.$axios.get(`/api/v1/perms/users/self/assets/${row.id}/`).then(res => {
|
||||
const protocols = res.permed_protocols
|
||||
const names = protocols.map(item => item.name).join(', ')
|
||||
return names
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
key: this.$t('Category'),
|
||||
value: row.category.label
|
||||
},
|
||||
{
|
||||
key: this.$t('Type'),
|
||||
value: row.type.label
|
||||
},
|
||||
{
|
||||
key: this.$t('Platform'),
|
||||
value: row.platform?.name || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('Active'),
|
||||
value: row.is_active
|
||||
},
|
||||
{
|
||||
key: this.$t('Comment'),
|
||||
value: row.comment
|
||||
}
|
||||
]
|
||||
}.bind(this)
|
||||
},
|
||||
sortable: true
|
||||
},
|
||||
labels: {
|
||||
formatterArgs: {
|
||||
showEditBtn: false
|
||||
}
|
||||
},
|
||||
address: {
|
||||
sortable: 'custom',
|
||||
width: '150px'
|
||||
},
|
||||
accounts: {
|
||||
align: 'center',
|
||||
label: this.$t('Account'),
|
||||
width: '120px',
|
||||
formatter: AccountShowFormatter,
|
||||
formatterArgs: {
|
||||
getUrl: ({ row }) => {
|
||||
return `/api/v1/perms/users/self/assets/${row.id}/`
|
||||
treeUrl: `/api/v1/perms/users/self/nodes/children/tree/`,
|
||||
tableUrl: `/api/v1/perms/users/self/assets/`,
|
||||
actions: {
|
||||
width: '88px',
|
||||
align: 'center',
|
||||
formatterArgs: {
|
||||
hasDelete: false,
|
||||
loading: true,
|
||||
hasClone: false,
|
||||
hasUpdate: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'connect',
|
||||
icon: 'fa-terminal',
|
||||
type: 'primary',
|
||||
can: ({ row }) => row.is_active,
|
||||
callback: ({ row }) => {
|
||||
const oid = this.$store.getters.currentOrg ? this.$store.getters.currentOrg.id : ''
|
||||
const url = `/luna/?login_to=${row.id}${oid ? `&oid=${oid}` : ''}`
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'favor',
|
||||
type: 'info',
|
||||
icon: ({ row }) => {
|
||||
return this.checkFavorite(row.id) ? 'fa-star' : 'fa-star-o'
|
||||
},
|
||||
callback: ({ row }) => this.toggleFavorite(row.id)
|
||||
}
|
||||
},
|
||||
platform: {
|
||||
width: '120px'
|
||||
},
|
||||
comment: {
|
||||
width: '100px'
|
||||
},
|
||||
connectivity: connectivityMeta,
|
||||
actions: {
|
||||
width: '88px',
|
||||
align: 'center',
|
||||
formatterArgs: {
|
||||
hasDelete: false,
|
||||
loading: true,
|
||||
hasClone: false,
|
||||
hasUpdate: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'connect',
|
||||
icon: 'fa-terminal',
|
||||
type: 'primary',
|
||||
can: ({ row }) => row.is_active,
|
||||
callback: ({ row }) => {
|
||||
const oid = this.$store.getters.currentOrg ? this.$store.getters.currentOrg.id : ''
|
||||
const url = `/luna/?login_to=${row.id}${oid ? `&oid=${oid}` : ''}`
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'favor',
|
||||
type: 'info',
|
||||
icon: ({ row }) => {
|
||||
return this.checkFavorite(row.id) ? 'fa-star' : 'fa-star-o'
|
||||
},
|
||||
callback: ({ row }) => this.toggleFavorite(row.id)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
tableAttrs: {
|
||||
rowClassName({ row }) {
|
||||
return !row.is_active ? 'row_disabled' : ''
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasLeftActions: false,
|
||||
hasSearch: true
|
||||
}
|
||||
allFavorites: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -180,7 +56,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
refreshAllFavorites() {
|
||||
const formatterArgs = this.tableConfig.columnsMeta.actions.formatterArgs
|
||||
const formatterArgs = this.actions.formatterArgs
|
||||
formatterArgs.loading = true
|
||||
this.$axios.get('/api/v1/assets/favorite-assets/').then(resp => {
|
||||
this.allFavorites = resp
|
||||
@@ -192,14 +68,12 @@ export default {
|
||||
const url = '/api/v1/assets/favorite-assets/'
|
||||
this.$axios.post(url, data).then(() => {
|
||||
this.allFavorites.push({ asset: assetId })
|
||||
this.$message.success(this.$i18n.t('CollectionSucceed'))
|
||||
})
|
||||
},
|
||||
disfavor(assetId) {
|
||||
const url = `/api/v1/assets/favorite-assets/?asset=${assetId}`
|
||||
this.$axios.delete(url).then(() => {
|
||||
this.allFavorites = this.allFavorites.filter(item => item['asset'] !== assetId)
|
||||
this.$message.success(this.$i18n.t('CancelCollection'))
|
||||
})
|
||||
},
|
||||
toggleFavorite(assetId) {
|
||||
|
||||
Reference in New Issue
Block a user