Merge branch 'master' of github.com:jumpserver/lina

This commit is contained in:
ibuler
2020-05-09 19:44:52 +08:00
21 changed files with 471 additions and 155 deletions

View File

@@ -37,7 +37,8 @@ export default {
onRename: this.onRename.bind(this),
onSelected: this.onSelected.bind(this),
beforeDrop: this.beforeDrop.bind(this),
onDrop: this.onDrop.bind(this)
onDrop: this.onDrop.bind(this),
refresh: this.refresh.bind(this)
// 尚未定义的函数
// beforeClick
// beforeDrag
@@ -214,6 +215,12 @@ export default {
var node = this.zTree.getNodeByParam('id', newNode.id, parentNode)
this.zTree.editName(node)
})
},
refresh: function() {
this.$axios.post(
'/api/v1/assets/nodes/00000000-0000-0000-0000-000000000000/tasks/',
{ action: 'refresh_cache' }
)
}
}
}

View File

@@ -21,9 +21,7 @@ import '@/styles/ztree.css'
const defaultObject = {
type: Object,
default: () => {
}
default: () => {}
}
export default {
name: 'ZTree',
@@ -34,15 +32,13 @@ export default {
},
data() {
return {
defaultSetting: {
},
zTree: '',
rMenu: ''
}
},
computed: {
treeSetting() {
return _.merge(this.defaultSetting, this.setting)
return this.setting
}
},
mounted() {
@@ -64,15 +60,7 @@ export default {
if (this.treeSetting.showRefresh) {
this.rootNodeAddDom(
this.zTree,
() => {
this.$axios.post(
'/api/v1/assets/nodes/00000000-0000-0000-0000-000000000000/tasks/',
{ action: 'refresh_cache' }
).then(res => {
this.initTree()
}
)
}
this.treeSetting.callback.refresh
)
}
@@ -85,6 +73,7 @@ export default {
})
},
rootNodeAddDom: function(ztree, callback) {
const vm = this
var refreshIcon = "<a id='tree-refresh'><i class='fa fa-refresh'></i></a>"
var rootNode = ztree.getNodes()[0]
if (rootNode) {
@@ -98,28 +87,8 @@ export default {
refreshIconRef.bind('click', function() {
ztree.destroy()
callback()
vm.initTree()
})
},
setUrlParam: function(url, name, value) {
var urlArray = url.split('?')
if (urlArray.length === 1) {
url += '?' + name + '=' + value
} else {
var oriParam = urlArray[1].split('&')
var oriParamMap = {}
$.each(oriParam, function(index, value) {
var v = value.split('=')
oriParamMap[v[0]] = v[1]
})
oriParamMap[name] = value
url = urlArray[0] + '?'
var newParam = []
$.each(oriParamMap, function(index, value) {
newParam.push(index + '=' + value)
})
url += newParam.join('&')
}
return url
}
}

View File

@@ -50,7 +50,8 @@ export default {
onDrag: this.defaultCallback.bind(this, 'onDrag'),
beforeDrop: this.defaultCallback.bind(this, 'beforeDrop'),
onDrop: this.defaultCallback.bind(this, 'onDrop'),
beforeAsync: this.defaultCallback.bind(this, 'beforeAsync')
beforeAsync: this.defaultCallback.bind(this, 'beforeAsync'),
refresh: this.defaultCallback.bind(this, 'refresh')
}
}
}

View File

@@ -1,5 +1,5 @@
<template>
<ActionsGroup :size="'mini'" :actions="cleanedActions" :more-actions="cleanMoreActions" />
<ActionsGroup :size="'mini'" :actions="cleanedActions" :more-actions="cleanMoreActions" v-bind="$attrs" />
</template>
<script>

View File

@@ -21,7 +21,6 @@ export default {
}
}
</script>
<style scoped>
</style>

View File

@@ -11,6 +11,7 @@ import CustomActionsFormatter from './CustomActionsFormatter'
import DeleteActionFormatter from './DeleteActionFormatter'
import DateFormatter from './DateFormatter'
export default {
DetailFormatter,
DisplayFormatter,

View File

@@ -196,7 +196,7 @@ export default {
data = this.iAjax.processResults.bind(this)(data)
data.results.forEach((v) => {
if (!this.hasObjects.find((item) => item.value === v.value)) {
this.hasObjects.push(v)
this.HasObjects.push(v)
}
})
// 如果还有其它页,继续获取, 如果没有就停止

View File

@@ -1,5 +1,16 @@
import en from './langs/en'
import cn from './langs/cn'
import zhLocale from 'element-ui/lib/locale/lang/zh-CN'
import enLocale from 'element-ui/lib/locale/lang/en'
import customCN from './cn.json'
import customEN from './en.json'
const cn = {
...customCN,
...zhLocale
}
const en = {
...customEN,
...enLocale
}
export default {
en,
cn

View File

@@ -140,7 +140,9 @@
"ReplayStorageUpdate":"更新录像存储",
"CreateCommandStorage":"创建命令存储",
"CommandStorageUpdate":"更新命令存储",
"CommandFilterRulesCreate":" 创建命令过滤器规则"
"CommandFilterRulesCreate":" 创建命令过滤器规则",
"LabelCreate":"创建标签",
"LabelUpdate":"更新标签"
},
"auth":{"authExpireMsg":"账号已退出,请重新登录","Re-Login":"重新登录"},
"users":{
@@ -149,12 +151,14 @@
"userDetail":"用户详情",
"name":"姓名",
"username":"用户名",
"role":"角色",
"userGroups":"用户组",
"Username":"用户名",
"Role":"角色",
"User groups":"用户组",
"email":"邮箱",
"userGroup":"用户组",
"groupMembers":"组下成员",
"MFAAuth":"MFA 认证",
"role":"角色",
"Group members":"组下成员",
"MFAcertification":"MFA 认证",
"source":"来源",
"validity":"有效",
"action":"操作",
@@ -276,6 +280,7 @@
"gateway":"网关",
"date_created":"创建日期",
"port":"端口",
"version":"版本",
"TestConnection":"测试连接",
"AdminUserList":"管理用户列表",
"AdminUserDetail":"管理用户详情",
@@ -293,7 +298,7 @@
"assetlist":"资产列表",
"loginMode":"登录模式",
"quick_update":"快速更新",
"auto_push":"自动更新",
"auto_push":"自动推送",
"label_list":"标签列表",
"label_detail":"标签详情",
"create_label":"创建标签",
@@ -319,7 +324,10 @@
"platform_detail":"平台详情",
"BasePlatform":"基础平台",
"charset":"编码",
"meta":"元数据"
"meta":"元数据",
"TestAssetsConnective": "测试资产可连接性",
"PushSystemUserNow": "立刻推送系统",
"Replace node assets admin user with this":"替换资产节点的管理员"
},
"applications":{
"applications":"应用管理",

View File

@@ -1,13 +0,0 @@
<template>
<h1>管理用户详情组件</h1>
</template>
<script>
export default {
name: 'AdminUserDetail'
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,126 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</el-col>
<el-col :span="10">
<QuickActions type="primary" :actions="quickActions" />
</el-col>
</el-row>
</template>
<script>
import ListTable from '@/components/ListTable/index'
import { CustomActionsFormatter, DateFormatter } from '@/components/ListTable/formatters'
import QuickActions from '@/components/QuickActions/index'
export default {
name: 'Detail',
components: {
QuickActions,
ListTable
},
props: {
object: {
type: Object,
default: () => {}
}
},
data() {
return {
quickActions: [
{
title: this.$t('assets.TestAssetsConnective'),
attrs: {
type: 'primary',
label: this.$tc('Test')
},
callbacks: {
click: function() {
this.$axios.post(
`api/v1/assets/admin-users/${this.object.id}/tasks/`,
{ action: 'test' }
).then(res => {
window.open(`/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
}
)
}.bind(this)
}
}
],
tableConfig: {
url: `/api/v1/assets/asset-users/?prefer_id=${this.object.id}&prefer=admin_user&latest=1`,
columns: [
{
prop: 'hostname',
label: this.$t('assets.hostname')
},
{
prop: 'ip',
label: this.$t('assets.ip')
},
{
prop: 'username',
label: this.$t('assets.username')
},
{
prop: 'version',
label: this.$t('assets.version')
},
{
prop: 'date_created',
label: this.$t('assets.date_created'),
formatter: DateFormatter
},
{
prop: 'id',
label: this.$tc('Action'),
align: 'center',
formatter: CustomActionsFormatter,
actions: {
extraActions: [
{
name: this.$tc('delete'),
title: this.$tc('delete'),
callback: (val) => {
this.$axios.delete(`/api/v1/assets/asset-users/${val.cellValue}/`).then(
this.$refs.ListTable.reloadTable()
)
}
},
{
name: this.$tc('Test'),
title: this.$tc('Test'),
callback: (val) => {
console.log('Test')
}
}
]
}
}
]
},
headerActions: {
hasRightActions: false,
hasExport: false,
hasImport: false,
hasRefresh: false,
hasBulkDelete: false,
hasSearch: true,
hasCreate: false
}
}
},
computed: {
},
mounted() {
},
methods: {
}
}
</script>
<style lang='less' scoped>
</style>

View File

@@ -0,0 +1,91 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10">
<RelationCard ref="RelationCard" type="info" style="margin-top: 15px" v-bind="nodeReletionConfig" />
</el-col>
</el-row>
</template>
<script>
import DetailCard from '@/components/DetailCard'
import RelationCard from '@/components/RelationCard'
import { toSafeLocalDateStr } from '@/utils/common'
export default {
name: 'Detail',
components: {
DetailCard,
RelationCard
},
props: {
object: {
type: Object,
default: () => {}
}
},
data() {
return {
nodeReletionConfig: {
icon: 'fa-info',
title: this.$t('assets.Replace node assets admin user with this'),
objectsAjax: {
url: '/api/v1/assets/nodes/',
processResults(data) {
let results = data.results
results = results.map((item) => {
return { label: item.full_value, value: item.id }
})
const more = !!data.next
return { results: results, pagination: more, total: data.count }
}
},
performAdd: (items) => {
const data = []
const relationUrl = `/api/v1/assets/admin-users/${this.object.id}/nodes/`
items.map(v => {
data.push(v.value)
})
return this.$axios.patch(relationUrl, { nodes: data }).then(res => {
this.$message.success(this.$tc('Update success'))
}).catch(err => {
this.$message.error(this.$tc('Update failed' + ' ' + err))
})
},
onAddSuccess: () => {
console.log(this)
this.$refs.RelationCard.$refs.select2.clearSelected()
}
}
}
},
computed: {
detailCardItems() {
return [
{
key: this.$t('assets.name'),
value: this.object.name
},
{
key: this.$t('assets.username'),
value: this.object.username
},
{
key: this.$t('assets.date_created'),
value: toSafeLocalDateStr(this.object.date_created)
},
{
key: this.$t('assets.created_by'),
value: this.object.created_by
}
]
}
}
}
</script>
<style lang='less' scoped>
</style>

View File

@@ -0,0 +1,45 @@
<template>
<GenericDetailPage :object.sync="TaskDetail" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
<keep-alive>
<component :is="config.activeMenu" :object="TaskDetail" />
</keep-alive>
</GenericDetailPage>
</template>
<script>
import { GenericDetailPage, TabPage } from '@/layout/components'
import Detail from './Detail.vue'
import AssetList from './AssetsList.vue'
export default {
components: {
GenericDetailPage,
TabPage,
Detail,
AssetList
},
data() {
return {
TaskDetail: {},
config: {
title: this.$t('assets.AdminUserDetail'),
activeMenu: 'Detail',
submenu: [
{
title: this.$t('assets.detail'),
name: 'Detail'
},
{
title: this.$t('assets.assetlist'),
name: 'AssetList'
}
],
hasRightSide: true
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,28 +1,27 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<ListTable :table-config="tableConfig" :header-actions="headerActions" />
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</el-col>
<el-col :span="10">
<AutoPushCard v-bind="AutoPushConfig" />
<RelationCard v-bind="systemUserRelationConfig" />
<QuickActions type="primary" :actions="quickActions" />
<RelationCard type="info" style="margin-top: 15px" v-bind="nodeReletionConfig" />
</el-col>
</el-row>
</template>a
</template>
<script>
import RelationCard from '@/components/RelationCard'
import AutoPushCard from './AutoPushCard'
import ListTable from '@/components/ListTable/index'
import { ActionsFormatter } from '@/components/ListTable/formatters'
import { CustomActionsFormatter, DateFormatter } from '@/components/ListTable/formatters'
import QuickActions from '@/components/QuickActions/index'
import RelationCard from '@/components/RelationCard'
export default {
name: 'Detail',
components: {
QuickActions,
ListTable,
RelationCard,
AutoPushCard
RelationCard
},
props: {
object: {
@@ -70,6 +69,44 @@ export default {
return this.$axios.delete(relationUrl)
}
},
quickActions: [
{
title: this.$t('assets.TestAssetsConnective'),
attrs: {
type: 'primary',
label: this.$tc('Test')
},
callbacks: {
click: function() {
this.$axios.post(
`api/v1/assets/system-users/${this.object.id}/tasks/`,
{ action: 'test' }
).then(res => {
window.open(`/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
}
)
}.bind(this)
}
},
{
title: this.$t('assets.PushSystemUserNow'),
attrs: {
type: 'primary',
label: this.$tc('Push')
},
callbacks: {
click: function() {
this.$axios.post(
`api/v1/assets/system-users/${this.object.id}/tasks/`,
{ action: 'push' }
).then(res => {
window.open(`/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
}
)
}.bind(this)
}
}
],
tableConfig: {
url: `/api/v1/assets/asset-users/?prefer_id=${this.object.id}&prefer=system_user&latest=1`,
columns: [
@@ -90,22 +127,41 @@ export default {
label: this.$t('assets.version')
},
{
prop: 'action.date_created',
label: this.$t('assets.date_created')
prop: 'date_created',
label: this.$t('assets.date_created'),
formatter: DateFormatter
},
{
prop: 'id',
label: this.$ttc('action'),
align: 'center',
formatter: ActionsFormatter,
width: '200px',
formatter: CustomActionsFormatter,
actions: {
performDelete: ({ row, col }) => {
const id = row.id
// http://localhost/api/v1/assets/cmd-filters/04a70dd5-62b3-4d05-8ebf-220e00a8072a/rules/dba77c52-8613-4762-a9cd-55093211c738/
const url = `/api/v1/assets/cmd-filters/${this.object.id}/rules/${id}/`
return this.$axios.delete(url)
}
extraActions: [
{
name: this.$tc('delete'),
title: this.$tc('delete'),
callback: (val) => {
this.$axios.delete(`/api/v1/assets/asset-users/${val.cellValue}/`).then(
this.$refs.ListTable.reloadTable()
)
}
},
{
name: this.$tc('Test'),
title: this.$tc('Test'),
callback: (val) => {
console.log('Test')
}
},
{
name: this.$tc('Push'),
title: this.$tc('Push'),
callback: (val) => {
console.log('Push')
}
}
]
}
}
]
@@ -114,15 +170,68 @@ export default {
hasRightActions: false,
hasExport: false,
hasImport: false,
hasRefresh: true,
hasRefresh: false,
hasBulkDelete: false,
hasSearch: true,
hasCreate: false
},
nodeReletionConfig: {
icon: 'fa-info',
title: this.$t('perms.Add node to this permission'),
objectsAjax: {
url: '/api/v1/assets/nodes/',
processResults(data) {
let results = data.results
results = results.map((item) => {
return { label: item.full_value, value: item.id }
})
const more = !!data.next
return { results: results, pagination: more, total: data.count }
}
},
hasObjectsId: [],
hasObjects: [],
performAdd: (items) => {
const relationUrl = `/api/v1/assets/system-users-nodes-relations/`
const objectId = this.object.id
const data = items.map(v => {
return {
systemuser: objectId,
node: v.value
}
})
return this.$axios.post(relationUrl, data)
},
performDelete: (item) => {
const itemId = item.value
const objectId = this.object.id
const relationUrl = `/api/v1/assets/system-users-nodes-relations/?systemuser=${objectId}&node=${itemId}`
return this.$axios.delete(relationUrl)
}
}
}
},
computed: {
},
mounted() {
this.getNodeList()
},
methods: {
getNodeList() {
this.$axios.get(
`/api/v1/assets/system-users-nodes-relations/?systemuser=${this.object.id}&draw=1&limit=15&offset=0`
).then(data => {
for (const x in data.results) {
this.nodeReletionConfig.hasObjectsId.push(data.results[x].node)
this.nodeReletionConfig.hasObjects.push({
value: data.results[x].node,
label: data.results[x].node_display
})
}
}
)
}
}
}
</script>
@@ -130,4 +239,3 @@ export default {
<style lang='less' scoped>
</style>
d

View File

@@ -5,21 +5,18 @@
</el-col>
<el-col :span="10">
<AutoPushCard v-bind="AutoPushConfig" />
<RelationCard v-bind="systemUserRelationConfig" />
</el-col>
</el-row>
</template>a
<script>
import DetailCard from '@/components/DetailCard'
import RelationCard from '@/components/RelationCard'
import AutoPushCard from './AutoPushCard'
export default {
name: 'Detail',
components: {
DetailCard,
RelationCard,
AutoPushCard
},
props: {
@@ -40,33 +37,6 @@ export default {
auto_push: this.object.auto_push
}
]
},
systemUserRelationConfig: {
icon: 'fa-info',
title: this.$t('assets.command_filter_list'),
objectsAjax: {
url: '/api/v1/assets/system-users/'
},
hasObjectsId: this.object.system_users,
performAdd: (items) => {
// TODO: Orange API 待修复
const relationUrl = `/api/v1/assets/cmd-filters/`
const objectId = this.object.id
const data = items.map(v => {
return {
cmd_filter: objectId,
systemuser: v.value
}
})
return this.$axios.post(relationUrl, data)
},
performDelete: (item) => {
const itemId = item.value
const objectId = this.object.id
// TODO: Orange API 待修复
const relationUrl = `/api/v1/assets/cmd-filters/?cmd-filters=${objectId}&systemuser=${itemId}`
return this.$axios.delete(relationUrl)
}
}
}
},

View File

@@ -34,7 +34,7 @@ export default {
name: 'AssetList'
}
],
hasRightSide: false
hasRightSide: true
}
}
}

View File

@@ -103,7 +103,7 @@ export default [
},
{
path: 'admin-users/:id',
component: () => import('@/views/assets/AdminUser/AdminUserDetail.vue'), // Parent router-view
component: () => import('@/views/assets/AdminUser/AdminUserDetail/index.vue'), // Parent router-view
name: 'AdminUserDetail',
meta: { title: 'AdminUserDetail' },
hidden: true

View File

@@ -11,7 +11,7 @@
<script>
import { DetailCard, ActiveCard } from '@/components'
import { toSafeLocalDateStr } from '@/utils/common'
// import { toSafeLocalDateStr } from '@/utils/common'
export default {
name: 'AssetPermissionDetail',
@@ -52,43 +52,44 @@ export default {
},
{
key: this.$t('perms.UserCount'),
value: this.getDataLength(this.object.users)
// value: this.getDataLength(this.object.users)
value: JSON.stringify(this.object.users_amount)
},
{
key: this.$t('perms.UserGroupCount'),
value: this.getDataLength(this.object.user_groups)
value: JSON.stringify(this.object.assets_amount)
},
{
key: this.$t('perms.AssetCount'),
value: this.getDataLength(this.object.assets)
value: JSON.stringify(this.object.users_amount)
},
{
key: this.$t('perms.NodeCount'),
value: this.getDataLength(this.object.nodes)
value: JSON.stringify(this.object.nodes_amount)
},
{
key: this.$t('perms.SystemUserCount'),
value: this.getDataLength(this.object.system_users)
value: JSON.stringify(this.object.system_users_amount)
},
{
key: this.$t('perms.DateStart'),
value: toSafeLocalDateStr(this.object.date_start)
value: 'api没有这个字段'
},
{
key: this.$t('perms.DateExpired'),
value: toSafeLocalDateStr(this.object.date_expired)
value: 'api没有这个字段'
},
{
key: this.$t('perms.DateCreated'),
value: '没有这个字段'
value: this.object.date_created
},
{
key: this.$t('perms.CreatedBy'),
value: '没有这个字段'
value: this.object.created_by
},
{
key: this.$t('common.Comment'),
value: this.object.comment
value: 'api没有这个字段'
}
]
}

View File

@@ -21,7 +21,7 @@ export default {
data() {
return {
AssetPermission: {
name: '', users: '', user_groups: '', assets: '', nodes: '', system_users: '',
name: '', users_amount: 0, user_groups_amount: 0, assets_amount: 0, nodes_amount: 0, system_users_amount: 0,
date_start: '', date_expired: ''
},
config: {

View File

@@ -4,7 +4,7 @@
<script>
import GenericTreeListPage from '@/layout/components/GenericTreeListPage'
import { LengthFormatter, ExpandAssetPermissionFormatter } from '@/components/ListTable/formatters/index'
import { ExpandAssetPermissionFormatter } from '@/components/ListTable/formatters/index'
export default {
components: {
@@ -35,35 +35,31 @@ export default {
{ label: this.$t('perms.SystemUser'), key: 'system_user' },
{ label: '继承(先占位)', key: 'all=0' }
],
columns: ['expand', 'name', 'users', 'user_groups', 'assets', 'nodes', 'system_users', 'is_active', 'actions'],
columns: ['expand', 'name', 'users_amount', 'user_groups_amount', 'assets_amount', 'nodes_amount', 'system_users_amount', 'is_active', 'actions'],
columnsMeta: {
expand: {
type: 'expand',
formatter: ExpandAssetPermissionFormatter
},
users: {
formatter: LengthFormatter,
users_amount: {
label: this.$t('perms.User')
},
user_groups: {
formatter: LengthFormatter,
user_groups_amount: {
label: this.$t('perms.UserGroups')
},
assets: {
formatter: LengthFormatter,
assets_amount: {
label: this.$t('perms.Asset')
},
nodes: {
formatter: LengthFormatter,
nodes_amount: {
label: this.$t('perms.Node')
},
system_users: {
formatter: LengthFormatter,
system_users_amount: {
label: this.$t('perms.SystemUser')
}
},
actions: {
updateRoute: 'AssetPermissionUpdate'
updateRoute: 'AssetPermissionUpdate',
detailRoute: 'AssetPermissionDetail'
}
},
headerActions: {

View File

@@ -7,7 +7,7 @@
<script>
import AssetPermissionTree from './AssetPermissionTree'
import AssetPermissionList from './AssetPermissionList'
import { LengthFormatter, ExpandAssetPermissionFormatter } from '@/components/ListTable/formatters/index'
import { ExpandAssetPermissionFormatter } from '@/components/ListTable/formatters/index'
export default {
components: {
@@ -39,35 +39,31 @@ export default {
{ label: this.$t('perms.SystemUser'), key: 'system_user' },
{ label: '继承(先占位)', key: 'all=0' }
],
columns: ['expand', 'name', 'users', 'user_groups', 'assets', 'nodes', 'system_users', 'is_active', 'actions'],
columns: ['expand', 'name', 'users_amount', 'user_groups_amount', 'assets_amount', 'nodes_amount', 'system_users_amount', 'is_active', 'actions'],
columnsMeta: {
expand: {
type: 'expand',
formatter: ExpandAssetPermissionFormatter
},
users: {
formatter: LengthFormatter,
users_amount: {
label: this.$t('perms.User')
},
user_groups: {
formatter: LengthFormatter,
user_groups_amount: {
label: this.$t('perms.UserGroups')
},
assets: {
formatter: LengthFormatter,
assets_amount: {
label: this.$t('perms.Asset')
},
nodes: {
formatter: LengthFormatter,
nodes_amount: {
label: this.$t('perms.Node')
},
system_users: {
formatter: LengthFormatter,
system_users_amount: {
label: this.$t('perms.SystemUser')
}
},
actions: {
updateRoute: 'AssetPermissionUpdate'
updateRoute: 'AssetPermissionUpdate',
detailRoute: 'AssetPermissionDetail'
}
},
headerActions: {