Commit 94a40ca1b24bdebe78e74e456e7bf7e9e145721f

Authored by ww
1 parent db7f19b3

feat: 规则链设计器节点目录

1 import type { VueFlowStore, Getters, Elements } from '@vue-flow/core'; 1 import type { VueFlowStore, Getters, Elements } from '@vue-flow/core';
2 -import { ComputedRef, ref, unref } from 'vue'; 2 +import { ComputedRef, computed, ref, unref } from 'vue';
3 import { BasicNodeBindData, EdgeData, NodeData } from '../types/node'; 3 import { BasicNodeBindData, EdgeData, NodeData } from '../types/node';
4 import { EntryCategoryComponentEnum } from '../enum/category'; 4 import { EntryCategoryComponentEnum } from '../enum/category';
5 import { useBasicDataTransform } from './useBasicDataTransform'; 5 import { useBasicDataTransform } from './useBasicDataTransform';
@@ -7,6 +7,7 @@ import { getRuleChainData, saveRuleChainData } from '/@/api/ruleDesigner'; @@ -7,6 +7,7 @@ import { getRuleChainData, saveRuleChainData } from '/@/api/ruleDesigner';
7 import { ConnectionItemType, RuleChainType } from '../types/ruleNode'; 7 import { ConnectionItemType, RuleChainType } from '../types/ruleNode';
8 import { useInputNode } from './useInputNode'; 8 import { useInputNode } from './useInputNode';
9 import { buildUUID } from '/@/utils/uuid'; 9 import { buildUUID } from '/@/utils/uuid';
  10 +import { useRoute } from 'vue-router';
10 11
11 const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT]; 12 const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];
12 13
@@ -17,6 +18,10 @@ export function useSaveAndRedo() { @@ -17,6 +18,10 @@ export function useSaveAndRedo() {
17 18
18 const redoDataRef = ref<Elements>([]); 19 const redoDataRef = ref<Elements>([]);
19 20
  21 + const route = useRoute();
  22 +
  23 + const getRuleChainId = computed(() => (route.params as Record<'id', string>).id);
  24 +
20 const { mergeData, deconstructionData } = useBasicDataTransform(); 25 const { mergeData, deconstructionData } = useBasicDataTransform();
21 26
22 const triggerChange = () => { 27 const triggerChange = () => {
@@ -124,7 +129,7 @@ export function useSaveAndRedo() { @@ -124,7 +129,7 @@ export function useSaveAndRedo() {
124 firstNodeIndex, 129 firstNodeIndex,
125 ruleChainId: { 130 ruleChainId: {
126 entityType: 'RULE_CHAIN', 131 entityType: 'RULE_CHAIN',
127 - id: '992ddda0-3d97-11ee-8b46-0dfb0900ab17', 132 + id: unref(getRuleChainId),
128 }, 133 },
129 }); 134 });
130 135
@@ -140,9 +145,7 @@ export function useSaveAndRedo() { @@ -140,9 +145,7 @@ export function useSaveAndRedo() {
140 try { 145 try {
141 loading.value = true; 146 loading.value = true;
142 147
143 - const id = '992ddda0-3d97-11ee-8b46-0dfb0900ab17';  
144 -  
145 - const data = await getRuleChainData(id); 148 + const data = await getRuleChainData(unref(getRuleChainId));
146 149
147 const elements = parseRuleChain(data); 150 const elements = parseRuleChain(data);
148 151
@@ -5,7 +5,12 @@ @@ -5,7 +5,12 @@
5 import { Panel, PanelPosition, VueFlow } from '@vue-flow/core'; 5 import { Panel, PanelPosition, VueFlow } from '@vue-flow/core';
6 import { computed, onMounted, Ref, ref, unref } from 'vue'; 6 import { computed, onMounted, Ref, ref, unref } from 'vue';
7 import './style'; 7 import './style';
8 - import { BasicConnectionArrow, BasicConnectionLine, BasicEdge, Sidebar } from './src/components'; 8 + import {
  9 + BasicConnectionArrow,
  10 + BasicConnectionLine,
  11 + BasicEdge,
  12 + MenuSidebar,
  13 + } from './src/components';
9 import type { FlowElRef } from './types/flow'; 14 import type { FlowElRef } from './types/flow';
10 import { useDragCreate } from './hook/useDragCreate'; 15 import { useDragCreate } from './hook/useDragCreate';
11 import { createFlowContext } from './hook/useFlowContext'; 16 import { createFlowContext } from './hook/useFlowContext';
@@ -88,7 +93,8 @@ @@ -88,7 +93,8 @@
88 93
89 <template> 94 <template>
90 <main ref="rootElRef" class="w-full h-full flex relative" @drop="handleOnDrop"> 95 <main ref="rootElRef" class="w-full h-full flex relative" @drop="handleOnDrop">
91 - <Sidebar /> 96 + <!-- <Sidebar /> -->
  97 + <MenuSidebar />
92 98
93 <Spin :spinning="loading" wrapperClassName="w-full h-ful rule-chain-designer-loading-container"> 99 <Spin :spinning="loading" wrapperClassName="w-full h-ful rule-chain-designer-loading-container">
94 <VueFlow 100 <VueFlow
1 import { BasicConnectionModalEnum } from '../enum'; 1 import { BasicConnectionModalEnum } from '../enum';
2 import { FetchNodeComFlagTypeENum } from '../enum/node'; 2 import { FetchNodeComFlagTypeENum } from '../enum/node';
3 -import type { CategoryConfigType, NodeItemConfigType } from '../types/node'; 3 +import type { NodeItemConfigType } from '../types/node';
4 import { ActionCategoryConfig, ActionComponents } from './Action'; 4 import { ActionCategoryConfig, ActionComponents } from './Action';
5 import { EnrichmentCategoryConfig, EnrichmentComponents } from './Enrichment'; 5 import { EnrichmentCategoryConfig, EnrichmentComponents } from './Enrichment';
6 import { ExternalCategoryConfig, ExternalComponents } from './External'; 6 import { ExternalCategoryConfig, ExternalComponents } from './External';
7 import { FilterCategoryConfig, FilterComponents } from './Filter'; 7 import { FilterCategoryConfig, FilterComponents } from './Filter';
8 import { FlowCategoryConfig, FlowComponents } from './Flow'; 8 import { FlowCategoryConfig, FlowComponents } from './Flow';
9 import { TransformationCategoryConfig, TransformationComponents } from './Transformation'; 9 import { TransformationCategoryConfig, TransformationComponents } from './Transformation';
10 -import { RuleNodeTypeEnum } from './index.type'; 10 +import { CategoryItemType, RuleNodeTypeEnum } from './index.type';
11 11
12 const createModules = import.meta.glob('../packages/**/create.vue'); 12 const createModules = import.meta.glob('../packages/**/create.vue');
13 const connectionModules = import.meta.glob('../packages/**/connection.vue'); 13 const connectionModules = import.meta.glob('../packages/**/connection.vue');
@@ -63,7 +63,7 @@ export const fetchNodeExtraContent = async (config: NodeItemConfigType) => { @@ -63,7 +63,7 @@ export const fetchNodeExtraContent = async (config: NodeItemConfigType) => {
63 63
64 export const allComponents: Record< 64 export const allComponents: Record<
65 Exclude<RuleNodeTypeEnum, RuleNodeTypeEnum.ENTRY>, 65 Exclude<RuleNodeTypeEnum, RuleNodeTypeEnum.ENTRY>,
66 - { category: CategoryConfigType; components: NodeItemConfigType[] } 66 + CategoryItemType
67 > = { 67 > = {
68 [RuleNodeTypeEnum.FILTER]: { 68 [RuleNodeTypeEnum.FILTER]: {
69 category: FilterCategoryConfig, 69 category: FilterCategoryConfig,
  1 +import { CategoryConfigType, NodeItemConfigType } from '../types/node';
  2 +
1 export enum RuleNodeTypeEnum { 3 export enum RuleNodeTypeEnum {
2 ENTRY = 'ENTRY', 4 ENTRY = 'ENTRY',
3 FLOW = 'FLOW', 5 FLOW = 'FLOW',
@@ -7,3 +9,8 @@ export enum RuleNodeTypeEnum { @@ -7,3 +9,8 @@ export enum RuleNodeTypeEnum {
7 FILTER = 'FILTER', 9 FILTER = 'FILTER',
8 TRANSFORMATION = 'TRANSFORMATION', 10 TRANSFORMATION = 'TRANSFORMATION',
9 } 11 }
  12 +
  13 +export interface CategoryItemType {
  14 + category: CategoryConfigType;
  15 + components: NodeItemConfigType[];
  16 +}
  1 +<script lang="ts" setup>
  2 + import { CategoryItemType, RuleNodeTypeEnum } from '../../../packages/index.type';
  3 + import Icon from '/@/components/Icon';
  4 + import { Tooltip } from 'ant-design-vue';
  5 +
  6 + const emit = defineEmits<{
  7 + (eventName: 'update:activeKey', activeKey?: RuleNodeTypeEnum);
  8 + }>();
  9 +
  10 + defineProps<{
  11 + activeKey?: RuleNodeTypeEnum;
  12 + category?: CategoryItemType;
  13 + }>();
  14 +
  15 + const handleClick = (category?: RuleNodeTypeEnum) => {
  16 + emit('update:activeKey', category);
  17 + };
  18 +</script>
  19 +
  20 +<template>
  21 + <Tooltip color="#fff" placement="right">
  22 + <template #title>
  23 + <p class="text-dark-900 font-bold text-xs m-0 py-1">
  24 + {{ category?.category.title }}
  25 + </p>
  26 + <p class="text-gray-500 text-xs m-0 py-1">
  27 + {{ category?.category.description }}
  28 + </p>
  29 + </template>
  30 + <div
  31 + class="h-16 mt-2 flex justify-center items-center flex-col cursor-pointer rounded"
  32 + :class="activeKey === category?.category.category ? 'bg-blue-100 text-blue-400' : ''"
  33 + @click="handleClick(category?.category.category)"
  34 + >
  35 + <Icon :icon="category?.category.icon" class="svg:text-3xl" />
  36 + <div class="select-none">{{ category?.category.title }}</div>
  37 + </div>
  38 + </Tooltip>
  39 +</template>
  1 +<script lang="ts" setup>
  2 + import { cloneDeep } from 'lodash';
  3 + import { computed, ref, unref } from 'vue';
  4 + import { allComponents } from '../../../packages';
  5 + import { CategoryItemType, RuleNodeTypeEnum } from '../../../packages/index.type';
  6 + import CategoryItem from './CategoryItem.vue';
  7 + import NodeItem from './NodeItem.vue';
  8 +
  9 + const props = defineProps<{
  10 + searchText?: string;
  11 + }>();
  12 +
  13 + const activeKey = ref<RuleNodeTypeEnum | undefined>(RuleNodeTypeEnum.FILTER);
  14 +
  15 + const getCurrentCategoryNode = computed<CategoryItemType>(() => {
  16 + const category = cloneDeep(allComponents[unref(activeKey)!]) as CategoryItemType;
  17 + const { searchText } = props;
  18 + if (searchText) {
  19 + category.components = category.components.filter((item) =>
  20 + item.name.toUpperCase().includes(searchText.toUpperCase())
  21 + );
  22 + }
  23 + return category;
  24 + });
  25 +</script>
  26 +
  27 +<template>
  28 + <section class="absolute top-11 w-full flex flex-auto h-[calc(100%-2.75rem)] bg-neutral-100">
  29 + <nav class="w-20 min-w-20 p-2 border-t border-light-50">
  30 + <CategoryItem
  31 + v-for="category in allComponents"
  32 + :key="category.category.category"
  33 + v-model:activeKey="activeKey"
  34 + :category="category"
  35 + />
  36 + </nav>
  37 + <body class="p-4 w-full flex flex-col gap-2 items-center overflow-x-hidden overflow-y-auto">
  38 + <NodeItem
  39 + v-for="config in getCurrentCategoryNode.components"
  40 + :key="config.clazz"
  41 + :config="config"
  42 + :category="getCurrentCategoryNode.category"
  43 + />
  44 + </body>
  45 + </section>
  46 +</template>
  1 +<script setup lang="ts">
  2 + import { Input, Tooltip } from 'ant-design-vue';
  3 + import { Icon } from '/@/components/Icon';
  4 +
  5 + const emit = defineEmits(['fold', 'search']);
  6 +
  7 + const handleOnFold = () => emit('fold');
  8 +
  9 + const handleInputChange = (event: Event) => {
  10 + const value = (event.target as HTMLInputElement).value;
  11 + emit('search', value);
  12 + };
  13 +</script>
  14 +
  15 +<template>
  16 + <head class="h-11 w-full flex justify-between items-center bg-neutral-100">
  17 + <Input
  18 + placeholder="搜索节点"
  19 + class="!mx-2 flex-auto"
  20 + @change="handleInputChange"
  21 + :allowClear="true"
  22 + >
  23 + <template #suffix>
  24 + <Icon icon="material-symbols:search" />
  25 + </template>
  26 + </Input>
  27 + <div class="w-14 min-w-14 flex justify-center">
  28 + <Tooltip title="折叠">
  29 + <Icon
  30 + icon="ant-design:menu-fold-outlined"
  31 + class="cursor-pointer svg:text-xl"
  32 + @click="handleOnFold"
  33 + />
  34 + </Tooltip>
  35 + </div>
  36 + </head>
  37 +</template>
  1 +<script lang="ts" setup>
  2 + import { Tooltip } from 'ant-design-vue';
  3 + import { computed, ref, toRaw, unref } from 'vue';
  4 + import { EFFECT_SYMBOL, TRANSFER_DATA_KEY } from '../../../hook/useDragCreate';
  5 + import { CategoryConfigType, NodeItemConfigType } from '../../../types/node';
  6 +
  7 + const props = defineProps<{
  8 + category?: CategoryConfigType;
  9 + config?: NodeItemConfigType;
  10 + }>();
  11 +
  12 + const visible = ref(false);
  13 +
  14 + const getNodeDefinition = computed(() => {
  15 + const { config } = props;
  16 + const { configurationDescriptor } = config || {};
  17 + const { nodeDefinition } = configurationDescriptor || {};
  18 + return nodeDefinition || {};
  19 + });
  20 +
  21 + const getIcon = computed(() => {
  22 + const { icon } = unref(getNodeDefinition);
  23 + const { icon: categoryIcon } = props.category || {};
  24 + return icon || categoryIcon || 'tabler:circuit-ground';
  25 + });
  26 +
  27 + const getIconUrl = computed(() => {
  28 + const { iconUrl } = unref(getNodeDefinition);
  29 + return iconUrl;
  30 + });
  31 +
  32 + const getBackgroundColor = computed(() => {
  33 + const { config, category } = props;
  34 + const { backgroundColor } = config || {};
  35 + const { backgroundColor: categoryBackgroundColor } = category || {};
  36 + return backgroundColor || categoryBackgroundColor;
  37 + });
  38 +
  39 + const handleOnDragStart = (event: DragEvent, options: object) => {
  40 + if (event.dataTransfer) {
  41 + event.dataTransfer.setData(
  42 + TRANSFER_DATA_KEY,
  43 + JSON.stringify({ offsetX: event.offsetX, offsetY: event.offsetY, options })
  44 + );
  45 + event.dataTransfer.effectAllowed = EFFECT_SYMBOL;
  46 + }
  47 + };
  48 +
  49 + const handleDragStart = (event: DragEvent) => {
  50 + visible.value = false;
  51 + handleOnDragStart(event, toRaw(unref(props)));
  52 + };
  53 +</script>
  54 +
  55 +<template>
  56 + <Tooltip
  57 + v-model:visible="visible"
  58 + placement="right"
  59 + color="#fff"
  60 + trigger="hover"
  61 + :mouse-enter-delay="0.5"
  62 + overlay-class-name="!max-w-90"
  63 + >
  64 + <template #title>
  65 + <section class="text-dark-900">
  66 + <h1 class="font-bold dark:text-dark-50">
  67 + {{ config?.name }}
  68 + </h1>
  69 + <p class="italic text-gray-600">
  70 + {{ getNodeDefinition.description }}
  71 + </p>
  72 + <p v-html="getNodeDefinition.details"></p>
  73 + </section>
  74 + </template>
  75 + <main
  76 + class="node flex items-center cursor-pointer w-44 h-12 min-h-12 rounded border relative px-4 py-2 border-gray-700 z-10"
  77 + :style="{ backgroundColor: getBackgroundColor }"
  78 + :draggable="true"
  79 + @dragstart="handleDragStart"
  80 + >
  81 + <div>
  82 + <img v-if="getIconUrl" class="w-4 h-4" :src="getIconUrl" alt="" />
  83 + <Icon v-if="!getIconUrl" class="text-2xl dark:text-light-50" :icon="getIcon" />
  84 + </div>
  85 + <div class="flex flex-1 text-xs flex-col ml-2 text-left truncate">
  86 + <span class="text-gray-700 w-full truncate text-xs dark:text-light-50">
  87 + {{ config?.name }}
  88 + </span>
  89 + </div>
  90 + <div class="w-4 h-4 bg-dark-50 rounded-1 border absolute -left-2 border-light-50"></div>
  91 + <div class="w-4 h-4 bg-dark-50 rounded-1 border absolute -right-2 border-light-50"></div>
  92 + </main>
  93 + </Tooltip>
  94 +</template>
  95 +
  96 +<style lang="css" scoped>
  97 + .node::before {
  98 + content: '';
  99 + position: absolute;
  100 + width: 100%;
  101 + height: 100%;
  102 + left: 0;
  103 + display: none;
  104 + background-color: #00000027;
  105 + }
  106 +
  107 + .node:hover::before {
  108 + display: block;
  109 + }
  110 +</style>
  1 +export { default as MenuSidebar } from './index.vue';
  1 +<script setup lang="ts">
  2 + import NodeFilter from './NodeFilter.vue';
  3 + import CategoryPanel from './CategoryPanel.vue';
  4 + import { computed, CSSProperties, ref, unref } from 'vue';
  5 + import { Icon } from '/@/components/Icon';
  6 + import { Tooltip } from 'ant-design-vue';
  7 +
  8 + const expand = ref(true);
  9 +
  10 + const searchText = ref();
  11 +
  12 + const handleOnFold = () => {
  13 + expand.value = false;
  14 + };
  15 +
  16 + const getSidebarStyle = computed<CSSProperties>(() => {
  17 + return {
  18 + flex: `0 0 ${unref(expand) ? '320px' : 0}`,
  19 + width: unref(expand) ? '320px' : 0,
  20 + minWidth: unref(expand) ? '320px' : 0,
  21 + };
  22 + });
  23 +
  24 + const handleSearch = (value: string) => {
  25 + searchText.value = value;
  26 + };
  27 +</script>
  28 +
  29 +<template>
  30 + <section
  31 + :style="getSidebarStyle"
  32 + class="h-full shadow shadow-lg relative z-50 shadow-dark-600 overflow-hidden transition-all"
  33 + >
  34 + <NodeFilter @fold="handleOnFold" @search="handleSearch" />
  35 + <CategoryPanel :searchText="searchText" />
  36 + </section>
  37 + <Tooltip title="展开">
  38 + <Icon
  39 + icon="ant-design:menu-unfold-outlined"
  40 + class="cursor-pointer svg:text-2xl absolute top-5 left-5 z-40 !flex justify-center items-center w-10 h-10 rounded-1 bg-blue-200 svg:text-light-50 shadow-lg"
  41 + @click="expand = true"
  42 + />
  43 + </Tooltip>
  44 +</template>
  1 +import { h } from 'vue';
1 import { BasicColumn } from '/@/components/Table'; 2 import { BasicColumn } from '/@/components/Table';
  3 +import { dateUtil } from '/@/utils/dateUtil';
  4 +import Icon from '/@/components/Icon';
  5 +import { Modal } from 'ant-design-vue';
  6 +import { JsonPreview } from '/@/components/CodeEditor';
  7 +
  8 +const handleOpenJsonPreviewModal = (title: string, content: string) => {
  9 + Modal.info({
  10 + title,
  11 + width: 600,
  12 + content: h(JsonPreview, { data: JSON.parse(content) }),
  13 + });
  14 +};
2 15
3 export const columns: BasicColumn[] = [ 16 export const columns: BasicColumn[] = [
4 { 17 {
5 title: '事件时间', 18 title: '事件时间',
6 dataIndex: 'createdTime', 19 dataIndex: 'createdTime',
  20 + width: 200,
  21 + format(text) {
  22 + return dateUtil(text).format('YYYY-MM-DD HH:mm:ss');
  23 + },
7 }, 24 },
8 { 25 {
9 title: '服务器', 26 title: '服务器',
10 dataIndex: 'body.server', 27 dataIndex: 'body.server',
  28 + ellipsis: true,
11 }, 29 },
12 { 30 {
13 title: '类型', 31 title: '类型',
14 - dataIndex: 'type', 32 + dataIndex: 'body.type',
  33 + ellipsis: true,
15 }, 34 },
16 { 35 {
17 title: '实体类型', 36 title: '实体类型',
18 - dataIndex: 'entityId.entityType', 37 + dataIndex: 'body.entityName',
  38 + ellipsis: true,
19 }, 39 },
20 { 40 {
21 title: '消息ID', 41 title: '消息ID',
22 - dataIndex: 'id.id', 42 + dataIndex: 'body.entityId',
  43 + ellipsis: true,
23 }, 44 },
24 { 45 {
25 title: '消息类型', 46 title: '消息类型',
26 - dataIndex: 'msgtype', 47 + dataIndex: 'body.msgType',
  48 + ellipsis: true,
27 }, 49 },
28 { 50 {
29 title: '关联类型', 51 title: '关联类型',
30 - dataIndex: 'relatedType', 52 + dataIndex: 'body.relationType',
  53 + ellipsis: true,
31 }, 54 },
32 { 55 {
33 title: '数据', 56 title: '数据',
34 - dataIndex: 'data', 57 + dataIndex: 'body.data',
  58 + ellipsis: true,
  59 + width: 80,
  60 + customRender({ text }) {
  61 + return (
  62 + text &&
  63 + h(Icon, {
  64 + icon: 'material-symbols:more-horiz',
  65 + class: 'cursor-pointer svg:text-2xl',
  66 + onClick: () => handleOpenJsonPreviewModal('数据', text),
  67 + })
  68 + );
  69 + },
35 }, 70 },
36 { 71 {
37 title: '元数据', 72 title: '元数据',
38 - dataIndex: 'metadaeta', 73 + dataIndex: 'body.metadata',
  74 + ellipsis: true,
  75 + width: 80,
  76 + customRender({ text }) {
  77 + return (
  78 + text &&
  79 + h(Icon, {
  80 + icon: 'material-symbols:more-horiz',
  81 + class: 'cursor-pointer svg:text-2xl',
  82 + onClick: () => handleOpenJsonPreviewModal('元数据', text),
  83 + })
  84 + );
  85 + },
39 }, 86 },
40 { 87 {
41 title: '错误', 88 title: '错误',
42 dataIndex: 'error', 89 dataIndex: 'error',
  90 + width: 80,
  91 + slots: {
  92 + customRender: 'error',
  93 + },
43 }, 94 },
44 ]; 95 ];
@@ -46,6 +46,8 @@ @@ -46,6 +46,8 @@
46 const result = await getRuleNodeEventList( 46 const result = await getRuleNodeEventList(
47 unref(getNodeId)!, 47 unref(getNodeId)!,
48 { 48 {
  49 + sortProperty: 'createdTime',
  50 + sortOrder: 'DESC',
49 page: params.page - 1, 51 page: params.page - 1,
50 pageSize: params.pageSize, 52 pageSize: params.pageSize,
51 tenantId: unref(getTenantId), 53 tenantId: unref(getTenantId),
@@ -130,6 +132,7 @@ @@ -130,6 +132,7 @@
130 </template> 132 </template>
131 <template #error="{ text }"> 133 <template #error="{ text }">
132 <Icon 134 <Icon
  135 + v-if="text"
133 icon="material-symbols:more-horiz" 136 icon="material-symbols:more-horiz"
134 class="cursor-pointer svg:text-2xl" 137 class="cursor-pointer svg:text-2xl"
135 @click="handleOpenDetailModal(text)" 138 @click="handleOpenDetailModal(text)"
@@ -18,12 +18,14 @@ export const columns: BasicColumn[] = [ @@ -18,12 +18,14 @@ export const columns: BasicColumn[] = [
18 }, 18 },
19 { 19 {
20 title: '消息处理', 20 title: '消息处理',
21 - dataIndex: 'body.event', 21 + dataIndex: 'body.messagesProcessed',
22 ellipsis: true, 22 ellipsis: true,
  23 + width: 100,
23 }, 24 },
24 { 25 {
25 title: '错误发生', 26 title: '错误发生',
26 - dataIndex: 'body.event', 27 + dataIndex: 'body.errorsOccurred',
27 ellipsis: true, 28 ellipsis: true,
  29 + width: 100,
28 }, 30 },
29 ]; 31 ];
1 export * from './Basic'; 1 export * from './Basic';
2 export * from './Sidebar'; 2 export * from './Sidebar';
  3 +export { MenuSidebar } from './MenuSidebar';