Browse Source

update

main
wangshaoping 1 year ago
parent
commit
6e4673b14a
  1. 62
      io.sc.engine.mv.frontend/src/views/config/cutOffPoint.vue
  2. 59
      io.sc.engine.mv.frontend/src/views/config/dataExtractor.vue
  3. 57
      io.sc.engine.mv.frontend/src/views/config/distribution.vue
  4. 35
      io.sc.engine.mv.frontend/src/views/config/executor.vue
  5. 33
      io.sc.platform.developer.frontend/src/views/springboot/Autoconfigure.vue
  6. 14
      io.sc.platform.developer.frontend/src/views/springboot/Bean.vue
  7. 27
      io.sc.platform.developer.frontend/src/views/springboot/Environment.vue
  8. 217
      io.sc.platform.developer.frontend/src/views/springboot/Mapping.vue
  9. 1
      io.sc.platform.system.frontend/src/views/menu/Menu.vue

62
io.sc.engine.mv.frontend/src/views/config/cutOffPoint.vue

@ -0,0 +1,62 @@
<template>
<w-grid
:title="$t('io.sc.engine.mv.config.cutOffPoint.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:data-url="Environment.apiContextPath('/api/mv/config/cutOffPoint')"
:pageable="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'resetDefaultValues', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: '100%', name: 'name', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.name') },
{ width: 100, name: 'from', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.from') },
{ width: 100, name: 'to', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.to') },
{ width: 100, name: 'step', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.step') },
{ width: 100, name: 'scale', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.scale') },
{ width: 150, name: 'roundingMode', label: $t('roundingMode'), format: Formater.enum(RoundingModeEnum) },
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 150, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
height: '400px',
},
form: {
colsNum: 1,
fields: [
{ name: 'name', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.name'), type: 'text' },
{ name: 'from', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.from'), type: 'text' },
{ name: 'to', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.to'), type: 'text' },
{ name: 'step', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.step'), type: 'text' },
{ name: 'scale', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.scale'), type: 'text' },
{ name: 'roundingMode', label: $t('roundingMode'), type: 'select', options: Options.enum(RoundingModeEnum, false) },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'name', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.name') },
{ name: 'from', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.from') },
{ name: 'to', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.to') },
{ name: 'step', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.step') },
{ name: 'scale', label: $t('io.sc.engine.mv.config.cutOffPoint.grid.entity.scale') },
{ name: 'roundingMode', label: $t('roundingMode'), format: Formater.none() },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate') },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, EnumTools, Formater, Options } from 'platform-core';
const RoundingModeEnum = await EnumTools.fetch('io.sc.platform.core.enums.RoundingMode');
</script>

59
io.sc.engine.mv.frontend/src/views/config/dataExtractor.vue

@ -0,0 +1,59 @@
<template>
<w-grid
:title="$t('io.sc.engine.mv.config.dataExtractor.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:data-url="Environment.apiContextPath('/api/mv/config/dataExtractor')"
:pageable="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: 100, name: 'name', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.name') },
{ width: 100, name: 'enable', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.enable'), format: Formater.enableTag() },
{ width: 120, name: 'order', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.order'), align: 'right' },
{ width: 120, name: 'executeTimeWeight', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.executeTimeWeight'), align: 'right' },
{ width: 120, name: 'datasourceName', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.datasourceName') },
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 150, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '800px',
height: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'name', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.name'), type: 'text' },
{ name: 'enable', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.enable'), type: 'text' },
{ name: 'order', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.order'), type: 'text' },
{ name: 'executeTimeWeight', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.executeTimeWeight'), type: 'text' },
{ name: 'datasourceName', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.datasourceName'), type: 'text' },
{ name: 'groovyScript', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.groovyScript'), type: 'textarea', rows: 12 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'name', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.name') },
{ name: 'enable', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.enable'), format: Formater.none() },
{ name: 'order', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.order') },
{ name: 'executeTimeWeight', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.executeTimeWeight') },
{ name: 'datasourceName', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.datasourceName') },
{ name: 'groovyScript', label: $t('io.sc.engine.mv.config.dataExtractor.grid.entity.groovyScript'), format: Formater.replaceAll() },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from 'platform-core';
</script>

57
io.sc.engine.mv.frontend/src/views/config/distribution.vue

@ -0,0 +1,57 @@
<template>
<w-grid
:title="$t('io.sc.engine.mv.config.distribution.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:data-url="Environment.apiContextPath('/api/mv/config/distribution')"
:pageable="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: 150, name: 'modelId', label: $t('io.sc.engine.mv.config.distribution.grid.entity.modelId') },
{ width: '100%', name: 'modelName', label: $t('io.sc.engine.mv.config.distribution.grid.entity.modelName') },
{ width: 160, name: 'scoreSegStart', label: $t('io.sc.engine.mv.config.distribution.grid.entity.scoreSegStart'), align: 'right' },
{ width: 160, name: 'scoreSegEnd', label: $t('io.sc.engine.mv.config.distribution.grid.entity.scoreSegEnd'), align: 'right' },
{ width: 150, name: 'count', label: $t('io.sc.engine.mv.config.distribution.grid.entity.count'), align: 'right' },
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 150, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
height: '400px',
},
form: {
colsNum: 1,
fields: [
{ name: 'modelId', label: $t('io.sc.engine.mv.config.distribution.grid.entity.modelId'), type: 'text' },
{ name: 'modelName', label: $t('io.sc.engine.mv.config.distribution.grid.entity.modelName'), type: 'text' },
{ name: 'scoreSegStart', label: $t('io.sc.engine.mv.config.distribution.grid.entity.scoreSegStart'), type: 'text' },
{ name: 'scoreSegEnd', label: $t('io.sc.engine.mv.config.distribution.grid.entity.scoreSegEnd'), type: 'text' },
{ name: 'count', label: $t('io.sc.engine.mv.config.distribution.grid.entity.count'), type: 'text' },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'modelId', label: $t('io.sc.engine.mv.config.distribution.grid.entity.modelId') },
{ name: 'modelName', label: $t('io.sc.engine.mv.config.distribution.grid.entity.modelName') },
{ name: 'scoreSegStart', label: $t('io.sc.engine.mv.config.distribution.grid.entity.scoreSegStart') },
{ name: 'scoreSegEnd', label: $t('io.sc.engine.mv.config.distribution.grid.entity.scoreSegEnd') },
{ name: 'count', label: $t('io.sc.engine.mv.config.distribution.grid.entity.count') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from 'platform-core';
</script>

35
io.sc.engine.mv.frontend/src/views/config/executor.vue

@ -0,0 +1,35 @@
<template>
<w-grid
:title="$t('io.sc.engine.mv.config.executor.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:data-url="Environment.apiContextPath('/api/mv/config/executor')"
:pageable="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: 80, name: 'order', label: $t('order'), align: 'right' },
{ width: 280, name: 'nameI18nKey', label: $t('name'), sortable: false, format: Formater.i18n() },
{ width: '100%', name: 'descriptionI18nKey', label: $t('description'), sortable: false, format: Formater.i18n() },
{ width: 250, name: 'className', label: $t('className'), format: Formater.simpleClassName() },
{ width: 100, name: 'enable', label: $t('enable'), format: Formater.enableTag() },
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'nameI18nKey', label: $t('name') },
{ name: 'descriptionI18nKey', label: $t('description') },
{ name: 'enable', label: $t('enable'), format: Formater.none() },
{ name: 'order', label: $t('order') },
{ name: 'className', label: $t('className'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from 'platform-core';
</script>

33
io.sc.platform.developer.frontend/src/views/springboot/Autoconfigure.vue

@ -1,4 +1,33 @@
<template>
<div>Autoconfigure</div>
<w-grid
:title="$t('menu.developer.springboot.autoconfigure')"
:checkbox-selection="false"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/autoConfiguration')"
:pageable="true"
:toolbar-actions="['refresh', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: 100, name: 'context', label: $t('category') },
{ width: 400, name: 'name', label: $t('name') },
{ width: 100, name: 'positiveAndNegativeType', label: $t('P/N') + ' ' + $t('type') },
{ width: 100, name: 'matchedAndNotMatchedType', label: $t('M/N') + ' ' + $t('type') },
{ width: 250, name: 'condition', label: $t('condition') },
{ width: '100%', name: 'message', label: $t('description') },
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'context', label: $t('category') },
{ name: 'name', label: $t('name') },
{ name: 'positiveAndNegativeType', label: $t('P/N') + ' ' + $t('type') },
{ name: 'matchedAndNotMatchedType', label: $t('M/N') + ' ' + $t('type') },
{ name: 'condition', label: $t('condition') },
{ name: 'message', label: $t('description') },
],
},
}"
></w-grid>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { Environment, Formater } from 'platform-core';
</script>

14
io.sc.platform.developer.frontend/src/views/springboot/Bean.vue

@ -1,13 +1,13 @@
<template>
<w-grid
:title="$t('menu.developer.springboot.bean')"
:toolbar-actions="['refresh', 'separator', 'view', 'export']"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/beans')"
:checkbox-selection="false"
:pageable="false"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/beans')"
:pageable="true"
:toolbar-actions="['refresh', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: 600, name: 'name', label: $t('name'), sortable: false },
{ width: 500, name: 'type', label: $t('className'), sortable: false },
{ width: 500, name: 'name', label: $t('name') },
{ width: 400, name: 'type', label: $t('className'), format: Formater.simpleClassName() },
{ width: 100, name: 'scope', label: $t('scope'), sortable: false },
]"
:viewer="{
@ -17,7 +17,7 @@
{ width: 100, name: 'name', label: $t('name') },
{ width: 100, name: 'context', label: $t('context') },
{ width: 100, name: 'scope', label: $t('scope') },
{ width: 100, name: 'type', label: $t('className') },
{ width: 100, name: 'type', label: $t('className'), format: Formater.none() },
{ width: 100, name: 'resource', label: $t('resource') },
{
width: 100,
@ -51,5 +51,5 @@
></w-grid>
</template>
<script setup lang="ts">
import { Environment } from 'platform-core';
import { Environment, Formater } from 'platform-core';
</script>

27
io.sc.platform.developer.frontend/src/views/springboot/Environment.vue

@ -1,4 +1,27 @@
<template>
<div>Environment</div>
<w-grid
:title="$t('menu.developer.springboot.environment')"
:checkbox-selection="false"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/environment')"
:pageable="false"
:toolbar-actions="['refresh', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: 200, name: 'propertySourceName', label: $t('category') },
{ width: 400, name: 'propertyName', label: $t('name') },
{ width: '100%', name: 'value', label: $t('value') },
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'propertySourceName', label: $t('category') },
{ name: 'propertyName', label: $t('name') },
{ name: 'value', label: $t('value') },
],
},
}"
></w-grid>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { Environment, Formater } from 'platform-core';
</script>

217
io.sc.platform.developer.frontend/src/views/springboot/Mapping.vue

@ -1,124 +1,119 @@
<template>
<q-tabs v-model="selectedTabRef" inline-label align="left" :breakpoint="0" no-caps>
<q-tab name="dispatcherServlets" icon="bi-people" :label="$t('Dispatcher Servlets')" />
<q-tab name="servletFilters" icon="bi-diagram-3" :label="$t('Servlet Filters')" />
<q-tab name="servlets" icon="bi-diagram-3" :label="$t('Servlets')" />
</q-tabs>
<q-tab-panels v-model="selectedTabRef" animated swipeable keep-alive>
<q-tab-panel name="dispatcherServlets" class="px-0">
<w-grid
:title="$t('menu.developer.springboot.bean')"
:toolbar-actions="['refresh', 'separator', 'view', 'export']"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/mappings/dispatcherServletMappingDescriptions')"
:checkbox-selection="false"
:pageable="false"
:columns="[
{
width: 100,
name: 'httpMethod',
label: $t('Http Method'),
format: (value, row) => {
return row.details?.requestMappingConditions?.methods || 'GET';
<div>
<q-tabs v-model="selectedTabRef" inline-label align="left" :breakpoint="0" no-caps>
<q-tab name="dispatcherServlets" icon="bi-people" :label="$t('Dispatcher Servlets')" />
<q-tab name="servletFilters" icon="bi-diagram-3" :label="$t('Servlet Filters')" />
<q-tab name="servlets" icon="bi-diagram-3" :label="$t('Servlets')" />
</q-tabs>
<q-tab-panels v-model="selectedTabRef" animated swipeable keep-alive>
<q-tab-panel name="dispatcherServlets" class="px-0">
<w-grid
:title="$t('menu.developer.springboot.mapping')"
:checkbox-selection="false"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/mappings/dispatcherServletMappingDescriptions')"
:pageable="true"
:toolbar-actions="['refresh', 'separator', 'view', 'export']"
:columns="[
{
width: 100,
name: 'httpMethods',
label: $t('httpMethod'),
},
sortable: false,
},
{
width: 200,
name: 'patterns',
label: $t('patterns'),
format: (value, row) => {
return row.details?.requestMappingConditions?.patterns || row.predicate;
{
width: 400,
name: 'patterns',
label: $t('pattern'),
format: (value) => {
return value;
},
},
sortable: false,
},
{
width: 400,
name: 'className',
label: $t('className'),
format: (value, row) => {
return row.details?.handlerMethod?.className || row.handler;
{
width: 300,
name: 'className',
label: $t('className'),
format: Formater.simpleClassName(),
},
{
width: 200,
name: 'methodName',
label: $t('methodName'),
format: (value) => {
return value;
},
},
sortable: false,
},
{
width: 150,
name: 'methodName',
label: $t('methodName'),
format: (value, row) => {
return row.details?.handlerMethod?.name;
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'httpMethods', label: $t('httpMethod') },
{ name: 'patterns', label: $t('pattern') },
{ name: 'className', label: $t('className'), format: Formater.none() },
{ name: 'methodName', label: $t('methodName') },
{ name: 'methodDescriptor', label: $t('methodDescriptor') },
{ name: 'consumes', label: $t('consume'), format: Formater.split() },
{ name: 'produces', label: $t('produce'), format: Formater.split() },
{ name: 'headers', label: $t('header'), format: Formater.split() },
{ name: 'params', label: $t('parameter'), format: Formater.split() },
],
},
sortable: false,
},
{
width: 150,
name: 'consumes',
label: $t('consumes'),
format: (value, row) => {
let result = '';
const consumes = row.details?.requestMappingConditions?.consumes;
if (consumes) {
for (const consume of consumes) {
result += consume.mediaType + '<br/>';
}
}
return result;
}"
></w-grid>
</q-tab-panel>
<q-tab-panel name="servletFilters" class="px-0">
<w-grid
:title="$t('menu.developer.springboot.bean')"
:toolbar-actions="['refresh', 'separator', 'view', 'export']"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/mappings/filterRegistrationMappingDescriptions')"
:checkbox-selection="false"
:pageable="true"
:columns="[
{ width: 200, name: 'registeredName', label: $t('name') },
{ width: 200, name: 'patterns', label: $t('pattern'), format: Formater.split() },
{ width: '100%', name: 'className', label: $t('className') },
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'registeredName', label: $t('name') },
{ name: 'patterns', label: $t('pattern') },
{ name: 'className', label: $t('className') },
],
},
sortable: false,
},
{
width: 150,
name: 'produces',
label: $t('produces'),
format: (value, row) => {
let result = '';
const produces = row.details?.requestMappingConditions?.produces;
if (produces) {
for (const produce of produces) {
result += produce.mediaType + '<br/>';
}
}
return result;
}"
></w-grid>
</q-tab-panel>
<q-tab-panel name="servlets" class="px-0">
<w-grid
:title="$t('menu.developer.springboot.bean')"
:checkbox-selection="false"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/mappings/servletRegistrationMappingDescriptions')"
:pageable="true"
:toolbar-actions="['refresh', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: 300, name: 'name', label: $t('name') },
{ width: '100%', name: 'patterns', label: $t('pattern') },
{ width: 400, name: 'className', label: $t('className') },
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'name', label: $t('name') },
{ name: 'patterns', label: $t('pattern') },
{ name: 'className', label: $t('className') },
],
},
sortable: false,
},
]"
></w-grid>
</q-tab-panel>
<q-tab-panel name="servletFilters" class="px-0">
<w-grid
:title="$t('menu.developer.springboot.bean')"
:toolbar-actions="['refresh', 'separator', 'view', 'export']"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/mappings/filterRegistrationMappingDescriptions')"
:checkbox-selection="false"
:pageable="false"
:columns="[
{ width: 200, name: 'servletNameMappings', label: $t('servletNameMappings'), sortable: false },
{ width: 200, name: 'urlPatternMappings', label: $t('urlPatternMappings'), sortable: false },
{ width: 400, name: 'className', label: $t('className'), sortable: false },
{ width: 150, name: 'name', label: $t('name'), sortable: false },
]"
></w-grid>
</q-tab-panel>
<q-tab-panel name="servlets" class="px-0">
<w-grid
:title="$t('menu.developer.springboot.bean')"
:toolbar-actions="['refresh', 'separator', 'view', 'export']"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/mappings/servletRegistrationMappingDescriptions')"
:checkbox-selection="false"
:pageable="false"
:columns="[
{ width: 300, name: 'mappings', label: $t('mappings'), sortable: false },
{ width: 300, name: 'className', label: $t('className'), sortable: false },
{ width: 200, name: 'name', label: $t('name'), sortable: false },
]"
></w-grid>
</q-tab-panel>
</q-tab-panels>
}"
></w-grid>
</q-tab-panel>
</q-tab-panels>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Environment } from 'platform-core';
import { Environment, Formater } from 'platform-core';
const selectedTabRef = ref('dispatcherServlets');
</script>

1
io.sc.platform.system.frontend/src/views/menu/Menu.vue

@ -36,6 +36,7 @@
label: $t('system.menu.grid.toolbar.addChild'),
},
],
'expand',
'edit',
'remove',
'separator',

Loading…
Cancel
Save