Commit db7f19b312d3dd3a067376f189ac339149103955

Authored by ww
1 parent 0e29512e

feat: 完成event事件面板组件开发

Showing 31 changed files with 1417 additions and 189 deletions
... ... @@ -6,6 +6,7 @@ import { RuleChainType } from '/@/views/rule/designer/types/ruleNode';
6 6 enum Api {
7 7 SAVE = '/ruleChain/metadata',
8 8 GET_RULE_CHAINES = '/ruleChains',
  9 + GET_RULE_NODE_EVENTS = '/events/RULE_NODE',
9 10 }
10 11
11 12 export const getRuleChainData = (id: string) => {
... ... @@ -38,3 +39,18 @@ export const getRuleChains = (params: Recordable) => {
38 39 }
39 40 );
40 41 };
  42 +
  43 +export const getRuleNodeEventList = (
  44 + ruleNodeId: string,
  45 + params: Recordable,
  46 + data: Recordable & Record<'eventType', string>
  47 +) => {
  48 + return defHttp.post<TBPaginationResult>(
  49 + {
  50 + url: `${Api.GET_RULE_NODE_EVENTS}/${ruleNodeId}`,
  51 + params,
  52 + data,
  53 + },
  54 + { joinPrefix: false }
  55 + );
  56 +};
... ...
... ... @@ -10,6 +10,11 @@ interface OptionsType {
10 10 type: ElementsTypeEnum;
11 11 }
12 12
  13 +export interface ElementInfo {
  14 + id: string;
  15 + type: ElementsTypeEnum;
  16 +}
  17 +
13 18 export function useAwaitPopupWindowBindData(
14 19 options: OptionsType = { mode: DataActionModeEnum.CREATE, type: ElementsTypeEnum.NODE }
15 20 ) {
... ... @@ -29,6 +34,8 @@ export function useAwaitPopupWindowBindData(
29 34
30 35 const shadowComponent = shallowRef<Nullable<Component>>();
31 36
  37 + const elementInfo = ref<ElementInfo>();
  38 +
32 39 const getNodeSetValue = computed(() => {
33 40 return unref(mode) === DataActionModeEnum.CREATE
34 41 ? unref(nodeData)?.config?.configurationDescriptor.nodeDefinition.defaultConfiguration
... ... @@ -68,8 +75,10 @@ export function useAwaitPopupWindowBindData(
68 75
69 76 const open = async (
70 77 nodeData: NodeData,
71   - edgeData?: EdgeData
  78 + edgeData?: EdgeData,
  79 + _elementInfo?: ElementInfo
72 80 ): Promise<AwaitPopupWindowReturnDataType> => {
  81 + elementInfo.value = _elementInfo;
73 82 await handleFetchComponent(nodeData, edgeData);
74 83 return new Promise((_resolve) => {
75 84 visible.value = true;
... ... @@ -113,6 +122,7 @@ export function useAwaitPopupWindowBindData(
113 122 visible,
114 123 nodeData,
115 124 spinning,
  125 + elementInfo,
116 126 shadowComponent,
117 127 createComponentEl,
118 128 getComponentKey,
... ...
... ... @@ -10,7 +10,7 @@ import type { Ref } from 'vue';
10 10 import { markRaw, toRaw, unref } from 'vue';
11 11 import { isFunction } from 'lodash-es';
12 12 import type { CreateNodeModal } from '../src/components/CreateNodeModal';
13   -import { EdgeTypeEnum, NodeTypeEnum } from '../enum';
  13 +import { EdgeTypeEnum, ElementsTypeEnum, NodeTypeEnum } from '../enum';
14 14 import { BasicEdge, BasicNode } from '../src/components';
15 15 import type { EdgeData, NodeData } from '../types/node';
16 16 import type { CreateEdgeModal } from '../src/components/CreateEdgeModal';
... ... @@ -126,10 +126,11 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
126 126
127 127 onNodeDoubleClick(async ({ node }) => {
128 128 if ((node.data as NodeData).config?.disableAction) return;
129   -
130 129 const { flag, data } =
131 130 (await unref(updateNodeDrawerActionType)?.open(
132   - toRaw((node as NodeData)?.data as unknown as NodeData)
  131 + toRaw((node as NodeData)?.data as unknown as NodeData),
  132 + void 0,
  133 + { id: node.id, type: ElementsTypeEnum.NODE }
133 134 )) || {};
134 135
135 136 if (!flag) return;
... ... @@ -146,7 +147,8 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
146 147 const { flag, data } =
147 148 (await unref(updateEdgeDrawerActionType)?.open(
148 149 toRaw(unref(edge.sourceNode?.data as unknown as NodeData)),
149   - toRaw(unref(edge.data as EdgeData))
  150 + toRaw(unref(edge.data as EdgeData)),
  151 + { id: edge.id, type: ElementsTypeEnum.EDGE }
150 152 )) || {};
151 153
152 154 if (!flag) return;
... ...
1 1 <script setup lang="ts">
  2 + import { Spin } from 'ant-design-vue';
2 3 import { Background, BackgroundVariant } from '@vue-flow/background';
3 4 import { Controls } from '@vue-flow/controls';
4 5 import { Panel, PanelPosition, VueFlow } from '@vue-flow/core';
... ... @@ -34,6 +35,7 @@
34 35 const elements = ref([]);
35 36
36 37 const {
  38 + loading,
37 39 changeMarker,
38 40 getCurrentPageMetaData,
39 41 triggerChange,
... ... @@ -88,66 +90,68 @@
88 90 <main ref="rootElRef" class="w-full h-full flex relative" @drop="handleOnDrop">
89 91 <Sidebar />
90 92
91   - <VueFlow
92   - :id="getId"
93   - ref="flowElRef"
94   - v-model="elements"
95   - class="w-full h-full"
96   - @dragover="handleOnDragOver"
97   - >
98   - <template #connection-line="props">
99   - <BasicConnectionLine v-bind="props" />
100   - </template>
101   - <template #edge-custom="props">
102   - <BasicEdge v-bind="props" />
103   - </template>
104   -
105   - <BasicConnectionArrow />
106   -
107   - <Background :variant="BackgroundVariant.Lines" :gap="25" pattern-color="#cfcfcf" />
108   -
109   - <Controls :position="PanelPosition.BottomLeft" />
110   -
111   - <Panel position="bottom-right" class="controls">
112   - <section class="flex gap-4">
113   - <button
114   - :style="{ transform: `translateY(${getDeleteDisplayState ? 0 : '72px'})` }"
115   - class="button-box-shadow w-14 h-14 flex justify-center items-center bg-orange-600 rounded-full transition-transform transform"
116   - @click="handleDeleteSelectionElements"
117   - >
118   - <Icon class="!text-3xl !text-light-50" icon="mdi:delete" />
119   - </button>
120   - <button
121   - class="button-box-shadow w-14 h-14 flex justify-center items-center bg-gray-400 rounded-full opacity-50"
122   - >
123   - <Icon class="!text-3xl !text-light-50" icon="carbon:debug" />
124   - </button>
125   - <button
126   - :class="changeMarker ? '!bg-orange-600 !opacity-100' : 'opacity-50'"
127   - class="button-box-shadow w-14 h-14 flex justify-center items-center bg-gray-400 rounded-full"
128   - @click="handleApplyChange(flowActionType)"
129   - >
130   - <Icon class="!text-3xl !text-light-50" icon="mdi:tick" />
131   - </button>
  93 + <Spin :spinning="loading" wrapperClassName="w-full h-ful rule-chain-designer-loading-container">
  94 + <VueFlow
  95 + :id="getId"
  96 + ref="flowElRef"
  97 + v-model="elements"
  98 + class="w-full h-full"
  99 + @dragover="handleOnDragOver"
  100 + >
  101 + <template #connection-line="props">
  102 + <BasicConnectionLine v-bind="props" />
  103 + </template>
  104 + <template #edge-custom="props">
  105 + <BasicEdge v-bind="props" />
  106 + </template>
  107 +
  108 + <BasicConnectionArrow />
  109 +
  110 + <Background :variant="BackgroundVariant.Lines" :gap="25" pattern-color="#cfcfcf" />
  111 +
  112 + <Controls :position="PanelPosition.BottomLeft" />
  113 +
  114 + <Panel position="bottom-right" class="controls">
  115 + <section class="flex gap-4">
  116 + <button
  117 + :style="{ transform: `translateY(${getDeleteDisplayState ? 0 : '72px'})` }"
  118 + class="button-box-shadow w-14 h-14 flex justify-center items-center bg-orange-600 rounded-full transition-transform transform"
  119 + @click="handleDeleteSelectionElements"
  120 + >
  121 + <Icon class="!text-3xl !text-light-50" icon="mdi:delete" />
  122 + </button>
  123 + <button
  124 + class="button-box-shadow w-14 h-14 flex justify-center items-center bg-gray-400 rounded-full opacity-50"
  125 + >
  126 + <Icon class="!text-3xl !text-light-50" icon="carbon:debug" />
  127 + </button>
  128 + <button
  129 + :class="changeMarker ? '!bg-orange-600 !opacity-100' : 'opacity-50'"
  130 + class="button-box-shadow w-14 h-14 flex justify-center items-center bg-gray-400 rounded-full"
  131 + @click="handleApplyChange(flowActionType)"
  132 + >
  133 + <Icon class="!text-3xl !text-light-50" icon="mdi:tick" />
  134 + </button>
  135 + <button
  136 + :class="changeMarker ? '!bg-orange-600 !opacity-100' : 'opacity-50'"
  137 + class="button-box-shadow w-14 h-14 flex justify-center items-center bg-gray-400 rounded-full"
  138 + @click="handleRedoChange(flowActionType)"
  139 + >
  140 + <Icon class="!text-3xl !text-light-50" icon="ic:baseline-close" />
  141 + </button>
  142 + </section>
  143 + </Panel>
  144 +
  145 + <Panel position="top-right">
132 146 <button
133   - :class="changeMarker ? '!bg-orange-600 !opacity-100' : 'opacity-50'"
134   - class="button-box-shadow w-14 h-14 flex justify-center items-center bg-gray-400 rounded-full"
135   - @click="handleRedoChange(flowActionType)"
  147 + class="w-10 h-10 bg-gray-300 flex justify-center items-center rounded-full dark:bg-dark-50"
  148 + @click="handleFullScreen"
136 149 >
137   - <Icon class="!text-3xl !text-light-50" icon="ic:baseline-close" />
  150 + <Icon class="!text-2xl dark:text-light-50" :icon="getFullScreenIcon" />
138 151 </button>
139   - </section>
140   - </Panel>
141   -
142   - <Panel position="top-right">
143   - <button
144   - class="w-10 h-10 bg-gray-300 flex justify-center items-center rounded-full dark:bg-dark-50"
145   - @click="handleFullScreen"
146   - >
147   - <Icon class="!text-2xl dark:text-light-50" :icon="getFullScreenIcon" />
148   - </button>
149   - </Panel>
150   - </VueFlow>
  152 + </Panel>
  153 + </VueFlow>
  154 + </Spin>
151 155
152 156 <CreateNodeModal ref="createNodeModalActionType" :get-container="handleGetContainer" />
153 157 <CreateEdgeModal ref="createEdgeModalActionType" :get-container="handleGetContainer" />
... ... @@ -162,3 +166,11 @@
162 166 box-shadow: 0 3px 5px -1px #0003, 0 6px 10px 0 #00000024, 0 1px 18px 0 #0000001f;
163 167 }
164 168 </style>
  169 +
  170 +<style lang="less">
  171 + .rule-chain-designer-loading-container {
  172 + .ant-spin-container {
  173 + @apply w-full h-full;
  174 + }
  175 + }
  176 +</style>
... ...
... ... @@ -6,6 +6,7 @@
6 6 import { useConnectionFocus } from '../../../hook/useConnectionFocus';
7 7 import { useFlowContext } from '../../../hook/useFlowContext';
8 8 import { useTextWidth } from '../../../hook/useTextWidth';
  9 + import { ElementsTypeEnum } from '../../../enum';
9 10
10 11 const props = defineProps<EdgeProps>();
11 12
... ... @@ -31,7 +32,8 @@
31 32 const { flag, data } =
32 33 (await unref(updateEdgeDrawerActionType)?.open(
33 34 toRaw(unref(props.sourceNode?.data as unknown as NodeData)),
34   - toRaw(unref(props.data as EdgeData))
  35 + toRaw(unref(props.data as EdgeData)),
  36 + { id: props.id, type: ElementsTypeEnum.EDGE }
35 37 )) || {};
36 38
37 39 if (!flag) return;
... ...
... ... @@ -5,6 +5,7 @@
5 5 import { computed, toRaw, unref } from 'vue';
6 6 import { useFlowContext } from '../../../hook/useFlowContext';
7 7 import type { NodeData } from '../../../types/node';
  8 + import { ElementsTypeEnum } from '../../../enum';
8 9
9 10 const props = defineProps<NodeProps>();
10 11
... ... @@ -14,7 +15,10 @@
14 15
15 16 const handleOpenEdit = async () => {
16 17 const { flag, data } =
17   - (await unref(updateNodeDrawerActionType)?.open(toRaw(unref(getData)))) || {};
  18 + (await unref(updateNodeDrawerActionType)?.open(toRaw(unref(getData)), void 0, {
  19 + id: props.id,
  20 + type: ElementsTypeEnum.NODE,
  21 + })) || {};
18 22
19 23 if (!flag) return;
20 24
... ...
1   -<script lang="ts" setup>
2   - import { useTable, BasicTable } from '/@/components/Table';
3   - import { columns, formSchemas } from './debugEvent.config';
4   -
5   - const [register] = useTable({
6   - columns,
7   - showIndexColumn: false,
8   - useSearchForm: true,
9   - formConfig: {
10   - layout: 'inline',
11   - baseColProps: {
12   - span: 12,
13   - },
14   - labelWidth: 80,
15   - schemas: formSchemas,
16   - },
17   - });
18   -</script>
19   -
20   -<template>
21   - <BasicTable @register="register" />
22   -</template>
1   -import { BasicColumn, FormSchema } from '/@/components/Table';
2   -
3   -export const columns: BasicColumn[] = [
4   - {
5   - title: '事件时间',
6   - dataIndex: '',
7   - },
8   - {
9   - title: '服务器',
10   - dataIndex: '',
11   - },
12   - {
13   - title: '类型',
14   - dataIndex: '',
15   - },
16   - {
17   - title: '实体类型',
18   - dataIndex: '',
19   - },
20   - {
21   - title: '消息ID',
22   - dataIndex: '',
23   - },
24   - {
25   - title: '消息类型',
26   - dataIndex: '',
27   - },
28   - {
29   - title: '关联类型',
30   - dataIndex: '',
31   - },
32   - {
33   - title: '数据',
34   - dataIndex: '',
35   - },
36   - {
37   - title: '原数据',
38   - dataIndex: '',
39   - },
40   - {
41   - title: '错误',
42   - dataIndex: '',
43   - },
44   -];
45   -
46   -export const formSchemas: FormSchema[] = [
47   - {
48   - field: '',
49   - label: '服务器',
50   - component: 'Input',
51   - },
52   - {
53   - field: '',
54   - label: '类型',
55   - component: 'Select',
56   - },
57   - {
58   - field: '',
59   - label: '实体ID',
60   - component: 'Input',
61   - },
62   - {
63   - field: '',
64   - label: '实体类型',
65   - component: 'Select',
66   - },
67   - {
68   - field: '',
69   - label: '消息类型',
70   - component: 'Input',
71   - },
72   - {
73   - field: '',
74   - label: '关联类型',
75   - component: 'Input',
76   - },
77   - {
78   - field: '',
79   - label: '数据',
80   - component: 'Input',
81   - },
82   - {
83   - field: '',
84   - label: '元数据',
85   - component: 'Input',
86   - },
87   - {
88   - field: '',
89   - label: '有错误',
90   - component: 'Checkbox',
91   - },
92   -];
1   -<script lang="ts" setup>
2   - import DebugEvent from './DebugEvent.vue';
3   -</script>
4   -
5   -<template>
6   - <section class="bg-gray-100 dark:bg-dark-200">
7   - <DebugEvent />
8   - </section>
9   -</template>
  1 +<script setup lang="ts">
  2 + import AceEditor, { Ace } from 'ace-builds';
  3 + import githubTheme from 'ace-builds/src-noconflict/theme-github?url';
  4 + import 'ace-builds/src-noconflict/mode-java';
  5 + import { ref, unref } from 'vue';
  6 + import { BasicModal, useModalInner } from '/@/components/Modal';
  7 +
  8 + withDefaults(
  9 + defineProps<{
  10 + content?: string;
  11 + }>(),
  12 + {}
  13 + );
  14 +
  15 + defineEmits(['register']);
  16 +
  17 + const content = ref();
  18 +
  19 + const [register] = useModalInner((params: ModalParamsType<string>) => {
  20 + content.value = params.record;
  21 + initEditor();
  22 + unref(aceEditorRef)?.setValue(params.record, -1);
  23 + });
  24 +
  25 + const javaEditorElRef = ref();
  26 +
  27 + const aceEditorRef = ref<Ace.Editor>();
  28 +
  29 + const initEditor = () => {
  30 + AceEditor.config.setModuleUrl('ace/theme/github', githubTheme);
  31 + const editor = AceEditor.edit(unref(javaEditorElRef)!, {
  32 + mode: 'ace/mode/java',
  33 + });
  34 + editor.setTheme('ace/theme/github');
  35 + editor.setOptions({
  36 + fontSize: 14,
  37 + showLineNumbers: false,
  38 + showGutter: false,
  39 + });
  40 + editor.setReadOnly(true);
  41 + aceEditorRef.value = editor;
  42 + };
  43 +</script>
  44 +
  45 +<template>
  46 + <BasicModal @register="register" title="错误" :showOkBtn="false" cancelText="关闭" width="80%">
  47 + <div ref="javaEditorElRef" class="w-full h-full min-h-96"></div>
  48 + </BasicModal>
  49 +</template>
... ...
  1 +import { EventTypeEnum } from '../EventSelect/config';
  2 +import { columns as debugColumns } from './debug.config';
  3 +import { columns as errorColumns } from './error.config';
  4 +import { columns as liveColumns } from './live.config';
  5 +import { columns as statsColumns } from './stats.config';
  6 +
  7 +export function getColumns(type: EventTypeEnum) {
  8 + const mapping = {
  9 + [EventTypeEnum.ERROR]: errorColumns,
  10 + [EventTypeEnum.LC_EVENT]: liveColumns,
  11 + [EventTypeEnum.STATS]: statsColumns,
  12 + [EventTypeEnum.DEBUG_RULE_NODE]: debugColumns,
  13 + };
  14 + return mapping?.[type] || [];
  15 +}
... ...
  1 +import { BasicColumn } from '/@/components/Table';
  2 +
  3 +export const columns: BasicColumn[] = [
  4 + {
  5 + title: '事件时间',
  6 + dataIndex: 'createdTime',
  7 + },
  8 + {
  9 + title: '服务器',
  10 + dataIndex: 'body.server',
  11 + },
  12 + {
  13 + title: '类型',
  14 + dataIndex: 'type',
  15 + },
  16 + {
  17 + title: '实体类型',
  18 + dataIndex: 'entityId.entityType',
  19 + },
  20 + {
  21 + title: '消息ID',
  22 + dataIndex: 'id.id',
  23 + },
  24 + {
  25 + title: '消息类型',
  26 + dataIndex: 'msgtype',
  27 + },
  28 + {
  29 + title: '关联类型',
  30 + dataIndex: 'relatedType',
  31 + },
  32 + {
  33 + title: '数据',
  34 + dataIndex: 'data',
  35 + },
  36 + {
  37 + title: '元数据',
  38 + dataIndex: 'metadaeta',
  39 + },
  40 + {
  41 + title: '错误',
  42 + dataIndex: 'error',
  43 + },
  44 +];
... ...
  1 +import { BasicColumn } from '/@/components/Table';
  2 +import { dateUtil } from '/@/utils/dateUtil';
  3 +
  4 +export const columns: BasicColumn[] = [
  5 + {
  6 + title: '事件时间',
  7 + dataIndex: 'createdTime',
  8 + ellipsis: true,
  9 + width: 200,
  10 + format(text) {
  11 + return dateUtil(text).format('YYYY-MM-DD HH:mm:ss');
  12 + },
  13 + },
  14 + {
  15 + title: '服务器',
  16 + dataIndex: 'body.server',
  17 + ellipsis: true,
  18 + },
  19 + {
  20 + title: '方法',
  21 + dataIndex: 'body.method',
  22 + ellipsis: true,
  23 + },
  24 + {
  25 + title: '错误',
  26 + dataIndex: 'body.error',
  27 + ellipsis: true,
  28 + width: 100,
  29 + slots: {
  30 + customRender: 'error',
  31 + },
  32 + },
  33 +];
... ...
src/views/rule/designer/src/components/UpdateNodeDrawer/BasicEvents/index.ts renamed from src/views/rule/designer/src/components/BasicEvents/index.ts
  1 +<script lang="ts" setup>
  2 + import { useTable, BasicTable } from '/@/components/Table';
  3 + import { getColumns } from './config';
  4 + import { getRuleNodeEventList } from '/@/api/ruleDesigner';
  5 + import { BasicNodeFormData, NodeData } from '../../../../types/node';
  6 + import { computed, reactive, ref, unref, watch } from 'vue';
  7 + import { useUserStore } from '/@/store/modules/user';
  8 + import { EventSelect } from '../EventSelect';
  9 + import { Icon } from '/@/components/Icon';
  10 + import { Tooltip } from 'ant-design-vue';
  11 + import { EventTypeEnum } from '../EventSelect/config';
  12 + import { FilterForm } from '../FilterForm';
  13 + import { useModal } from '/@/components/Modal';
  14 + import { DataActionModeEnum } from '/@/enums/toolEnum';
  15 + import { ElementInfo } from '../../../../hook/useAwaitPopupWindowBindData';
  16 + import ShowDetailModal from './ShowDetailModal.vue';
  17 +
  18 + const props = defineProps<{
  19 + nodeData?: NodeData<BasicNodeFormData>;
  20 + elementInfo?: ElementInfo;
  21 + }>();
  22 +
  23 + const userStore = useUserStore();
  24 + const getNodeId = computed(() => props.elementInfo?.id);
  25 + const getTenantId = computed(() => unref(userStore.getUserInfo).tenantId);
  26 + const eventType = ref(EventTypeEnum.DEBUG_RULE_NODE);
  27 + const filterFormElRef = ref<Nullable<InstanceType<typeof FilterForm>>>(null);
  28 +
  29 + const searchParams = reactive({
  30 + params: {} as Recordable,
  31 + data: {} as Recordable,
  32 + });
  33 +
  34 + const [register, { reload, setColumns, setPagination }] = useTable({
  35 + columns: getColumns(EventTypeEnum.DEBUG_RULE_NODE),
  36 + showIndexColumn: false,
  37 + useSearchForm: false,
  38 + canResize: true,
  39 + resizeHeightOffset: 76,
  40 + fetchSetting: {
  41 + totalField: 'totalElements',
  42 + listField: 'data',
  43 + },
  44 + showTableSetting: true,
  45 + api: async (params: Record<'page' | 'pageSize', number>) => {
  46 + const result = await getRuleNodeEventList(
  47 + unref(getNodeId)!,
  48 + {
  49 + page: params.page - 1,
  50 + pageSize: params.pageSize,
  51 + tenantId: unref(getTenantId),
  52 + ...searchParams.params,
  53 + },
  54 + { ...searchParams.data, eventType: unref(eventType) }
  55 + );
  56 +
  57 + return result;
  58 + },
  59 + });
  60 +
  61 + const [registerModal, { openModal }] = useModal();
  62 +
  63 + const handleOpenFilterForm = () => {
  64 + openModal(true, {
  65 + mode: DataActionModeEnum.READ,
  66 + record: { type: unref(eventType) },
  67 + } as ModalParamsType<{ type: EventTypeEnum }>);
  68 + };
  69 +
  70 + const handleRemoveFilterQueue = () => {
  71 + unref(filterFormElRef)?.resetFields();
  72 + handleReset();
  73 + };
  74 +
  75 + const handleFilterChange = (searchValue: Record<'data' | 'params', Recordable>) => {
  76 + const { data, params } = searchValue;
  77 + handleReset(data, params);
  78 + };
  79 +
  80 + const handleReset = (data: Recordable = {}, params: Recordable = {}) => {
  81 + searchParams.data = data;
  82 + searchParams.params = params;
  83 + setPagination({ current: 1 });
  84 + reload();
  85 + };
  86 +
  87 + const handleEventTypeChange = () => {
  88 + setColumns(getColumns(unref(eventType)));
  89 + handleReset();
  90 + };
  91 +
  92 + const [registerDetailModal, { openModal: openDetailModal }] = useModal();
  93 +
  94 + const handleOpenDetailModal = (text: string) => {
  95 + openDetailModal(true, {
  96 + mode: DataActionModeEnum.READ,
  97 + record: text,
  98 + } as ModalParamsType<string>);
  99 + };
  100 +
  101 + watch(
  102 + () => props.elementInfo?.id,
  103 + () => {
  104 + reload();
  105 + }
  106 + );
  107 +</script>
  108 +
  109 +<template>
  110 + <section class="bg-gray-100 dark:bg-dark-200 p-4 h-full">
  111 + <BasicTable @register="register" class="debug-table h-full">
  112 + <template #tableTitle>
  113 + <EventSelect v-model:type="eventType" @change="handleEventTypeChange" />
  114 + </template>
  115 + <template #toolbar>
  116 + <Tooltip title="过滤器">
  117 + <Icon
  118 + icon="material-symbols:filter-list"
  119 + class="cursor-pointer svg:text-2xl"
  120 + @click="handleOpenFilterForm"
  121 + />
  122 + </Tooltip>
  123 + <Tooltip title="重置">
  124 + <Icon
  125 + icon="mdi:filter-variant-remove"
  126 + class="cursor-pointer svg:text-2xl"
  127 + @click="handleRemoveFilterQueue"
  128 + />
  129 + </Tooltip>
  130 + </template>
  131 + <template #error="{ text }">
  132 + <Icon
  133 + icon="material-symbols:more-horiz"
  134 + class="cursor-pointer svg:text-2xl"
  135 + @click="handleOpenDetailModal(text)"
  136 + />
  137 + </template>
  138 + </BasicTable>
  139 +
  140 + <FilterForm
  141 + ref="filterFormElRef"
  142 + @register="registerModal"
  143 + @filterChange="handleFilterChange"
  144 + />
  145 +
  146 + <ShowDetailModal @register="registerDetailModal" />
  147 + </section>
  148 +</template>
  149 +
  150 +<style lang="less" scoped></style>
... ...
  1 +import { BasicColumn } from '/@/components/Table';
  2 +import { dateUtil } from '/@/utils/dateUtil';
  3 +
  4 +export const columns: BasicColumn[] = [
  5 + {
  6 + title: '事件时间',
  7 + dataIndex: 'createdTime',
  8 + ellipsis: true,
  9 + width: 200,
  10 + format(text) {
  11 + return dateUtil(text).format('YYYY-MM-DD HH:mm:ss');
  12 + },
  13 + },
  14 + {
  15 + title: '服务器',
  16 + dataIndex: 'body.server',
  17 + ellipsis: true,
  18 + },
  19 + {
  20 + title: '事件',
  21 + dataIndex: 'body.event',
  22 + ellipsis: true,
  23 + },
  24 + {
  25 + title: '状态',
  26 + dataIndex: 'body.success',
  27 + ellipsis: true,
  28 + format(text) {
  29 + return text ? '成功' : '失败';
  30 + },
  31 + },
  32 + {
  33 + title: '错误',
  34 + dataIndex: 'body.error',
  35 + ellipsis: true,
  36 + width: 100,
  37 + slots: {
  38 + customRender: 'error',
  39 + },
  40 + },
  41 +];
... ...
  1 +import { BasicColumn } from '/@/components/Table';
  2 +import { dateUtil } from '/@/utils/dateUtil';
  3 +
  4 +export const columns: BasicColumn[] = [
  5 + {
  6 + title: '事件时间',
  7 + dataIndex: 'createdTime',
  8 + ellipsis: true,
  9 + width: 200,
  10 + format(text) {
  11 + return dateUtil(text).format('YYYY-MM-DD HH:mm:ss');
  12 + },
  13 + },
  14 + {
  15 + title: '服务器',
  16 + dataIndex: 'body.server',
  17 + ellipsis: true,
  18 + },
  19 + {
  20 + title: '消息处理',
  21 + dataIndex: 'body.event',
  22 + ellipsis: true,
  23 + },
  24 + {
  25 + title: '错误发生',
  26 + dataIndex: 'body.event',
  27 + ellipsis: true,
  28 + },
  29 +];
... ...
  1 +export enum EventTypeEnum {
  2 + ERROR = 'ERROR',
  3 + LC_EVENT = 'LC_EVENT',
  4 + STATS = 'STATS',
  5 + DEBUG_RULE_NODE = 'DEBUG_RULE_NODE',
  6 +}
  7 +
  8 +export enum EventTypeNameEnum {
  9 + ERROR = '错误',
  10 + LC_EVENT = '生命周期事件',
  11 + STATS = '类型统计',
  12 + DEBUG_RULE_NODE = '调试',
  13 +}
  14 +
  15 +export const eventTypeOptions = Object.keys(EventTypeEnum).map((value) => ({
  16 + label: EventTypeNameEnum[value],
  17 + value,
  18 +}));
... ...
  1 +export { default as EventSelect } from './index.vue';
... ...
  1 +<script setup lang="ts">
  2 + import { Select } from 'ant-design-vue';
  3 + import { eventTypeOptions, EventTypeEnum } from './config';
  4 + import { unref } from 'vue';
  5 +
  6 + withDefaults(
  7 + defineProps<{
  8 + type?: EventTypeEnum;
  9 + }>(),
  10 + {
  11 + type: EventTypeEnum.DEBUG_RULE_NODE,
  12 + }
  13 + );
  14 +
  15 + const emit = defineEmits<{
  16 + (eventName: 'update:type', value: EventTypeEnum): void;
  17 + (eventName: 'change', value: EventTypeEnum): void;
  18 + }>();
  19 +
  20 + const handleChange = (value: EventTypeEnum) => {
  21 + emit('update:type', unref(value));
  22 + emit('change', unref(value));
  23 + };
  24 +</script>
  25 +
  26 +<template>
  27 + <Select
  28 + :value="type"
  29 + :options="eventTypeOptions"
  30 + :bordered="false"
  31 + class="border-b w-40"
  32 + @change="handleChange"
  33 + />
  34 +</template>
... ...
  1 +import { EventTypeEnum } from '../EventSelect/config';
  2 +import {
  3 + formSchemas as debugFormSchemas,
  4 + generateSearchParams as transformDebugSearchParams,
  5 +} from './debug.config';
  6 +import {
  7 + formSchemas as errorFormSchemas,
  8 + generateSearchParams as transformErrorSearchParams,
  9 +} from './error.config';
  10 +import {
  11 + formSchemas as liveFormSchemas,
  12 + generateSearchParams as transformLiveSearchParams,
  13 +} from './live.config';
  14 +import {
  15 + formSchemas as statsFormSchemas,
  16 + generateSearchParams as transformStatsSearchParams,
  17 +} from './stats.config';
  18 +import { FormSchema } from '/@/components/Form';
  19 +
  20 +export function getFormSchemas(type: EventTypeEnum): FormSchema[] {
  21 + const mapping = {
  22 + [EventTypeEnum.ERROR]: errorFormSchemas,
  23 + [EventTypeEnum.LC_EVENT]: liveFormSchemas,
  24 + [EventTypeEnum.STATS]: statsFormSchemas,
  25 + [EventTypeEnum.DEBUG_RULE_NODE]: debugFormSchemas,
  26 + };
  27 +
  28 + return mapping?.[type] || [];
  29 +}
  30 +
  31 +export function transformFilterValue(
  32 + type: EventTypeEnum,
  33 + value: Recordable
  34 +): Record<'data' | 'params', Recordable> {
  35 + const mapping = {
  36 + [EventTypeEnum.ERROR]: transformErrorSearchParams,
  37 + [EventTypeEnum.LC_EVENT]: transformLiveSearchParams,
  38 + [EventTypeEnum.STATS]: transformStatsSearchParams,
  39 + [EventTypeEnum.DEBUG_RULE_NODE]: transformDebugSearchParams,
  40 + };
  41 + return mapping?.[type]?.(value as any) || { data: {}, params: {} };
  42 +}
... ...
  1 +import { h } from 'vue';
  2 +import { RangePickerExtra } from '../RangePickerExtra';
  3 +import { FormSchema } from '/@/components/Form';
  4 +
  5 +export enum FormFieldsEnum {
  6 + SERVER = 'server',
  7 + MSG_DIRECTION_TYPE = 'msgDirectionType',
  8 + ENTITY_ID = 'entityId',
  9 + ENTITY_NAME = 'entityName',
  10 + MSG_TYPE = 'msgType',
  11 + RELATION_TYPE = 'relationType',
  12 + DATA_SEARCH = 'dataSearch',
  13 + METADATA_SEARCH = 'metadataSearch',
  14 + IS_ERROR = 'isError',
  15 + ERROR_STR = 'errorStr',
  16 + EVENT_TYPE = 'eventType',
  17 + START_TIME = 'startTime',
  18 + END_TIME = 'endTime',
  19 +
  20 + TIME_INTERVAL = 'timeInterval',
  21 +}
  22 +
  23 +export enum MessageDirectionTypeEnum {
  24 + IN = 'IN',
  25 + OUT = 'OUT',
  26 +}
  27 +
  28 +export enum EntityNameEnum {
  29 + TENANT = 'TENANT',
  30 + TENANT_PROFILE = 'TENANT_PROFILE',
  31 + CUSTOMER = 'CUSTOMER',
  32 + USER = 'USER',
  33 + DASHBOARD = 'DASHBOARD',
  34 + ASSET = 'ASSET',
  35 + DEVICE = 'DEVICE',
  36 + DEVICE_PROFILE = 'DEVICE_PROFILE',
  37 + ALARM = 'ALARM',
  38 + RULE_CHAIN = 'RULE_CHAIN',
  39 + RULE_NODE = 'RULE_NODE',
  40 + EDGE = 'EDGE',
  41 + ENTITY_VIEW = 'ENTITY_VIEW',
  42 + WIDGETS_BUNDLE = 'WIDGETS_BUNDLE',
  43 + WIDGET_TYPE = 'WIDGET_TYPE',
  44 + API_USAGE_STATE = 'API_USAGE_STATE',
  45 + TB_RESOURCE = 'TB_RESOURCE',
  46 + OTA_PACKAGE = 'OTA_PACKAGE',
  47 + RPC = 'RPC',
  48 +}
  49 +
  50 +export const formSchemas: FormSchema[] = [
  51 + {
  52 + field: FormFieldsEnum.TIME_INTERVAL,
  53 + label: '时间段',
  54 + component: 'RangePicker',
  55 + colProps: { span: 24 },
  56 + componentProps: {
  57 + showTime: true,
  58 + },
  59 + renderComponentContent: ({ model }) => {
  60 + return {
  61 + renderExtraFooter: () =>
  62 + h(RangePickerExtra, {
  63 + onChange: (date: moment.Moment[]) => {
  64 + model[FormFieldsEnum.TIME_INTERVAL] = date;
  65 + },
  66 + }),
  67 + };
  68 + },
  69 + },
  70 + {
  71 + field: FormFieldsEnum.SERVER,
  72 + label: '服务器',
  73 + component: 'Input',
  74 + componentProps: {
  75 + placeholder: `请输入服务器`,
  76 + },
  77 + },
  78 + {
  79 + field: FormFieldsEnum.MSG_DIRECTION_TYPE,
  80 + label: '类型',
  81 + component: 'Select',
  82 + componentProps: {
  83 + options: Object.keys(MessageDirectionTypeEnum).map((value) => ({ label: value, value })),
  84 + allowClear: true,
  85 + placeholder: `请选择类型`,
  86 + getPopupContainer: () => document.body,
  87 + },
  88 + },
  89 + {
  90 + field: FormFieldsEnum.ENTITY_ID,
  91 + label: '实体ID',
  92 + component: 'Input',
  93 + componentProps: {
  94 + placeholder: `请输入实体ID`,
  95 + },
  96 + },
  97 + {
  98 + field: FormFieldsEnum.ENTITY_NAME,
  99 + label: '实体类型',
  100 + component: 'Select',
  101 + componentProps: {
  102 + options: Object.keys(EntityNameEnum).map((value) => ({ label: value, value })),
  103 + allowClear: true,
  104 + placeholder: `请选择实体类型`,
  105 + getPopupContainer: () => document.body,
  106 + },
  107 + },
  108 + {
  109 + field: FormFieldsEnum.MSG_TYPE,
  110 + label: '消息类型',
  111 + component: 'Input',
  112 + componentProps: {
  113 + placeholder: `请输入消息类型`,
  114 + },
  115 + },
  116 + {
  117 + field: FormFieldsEnum.RELATION_TYPE,
  118 + label: '关联类型',
  119 + component: 'Input',
  120 + componentProps: {
  121 + placeholder: `请输入关联类型`,
  122 + },
  123 + },
  124 + {
  125 + field: FormFieldsEnum.DATA_SEARCH,
  126 + label: '数据',
  127 + component: 'Input',
  128 + componentProps: {
  129 + placeholder: `请输入数据`,
  130 + },
  131 + },
  132 + {
  133 + field: FormFieldsEnum.METADATA_SEARCH,
  134 + label: '元数据',
  135 + component: 'Input',
  136 + componentProps: {
  137 + placeholder: `请输入元数据`,
  138 + },
  139 + },
  140 + {
  141 + field: FormFieldsEnum.IS_ERROR,
  142 + label: '有错误',
  143 + component: 'Checkbox',
  144 + },
  145 + {
  146 + field: FormFieldsEnum.ERROR_STR,
  147 + label: '错误',
  148 + component: 'Input',
  149 + show: ({ model }) => model[FormFieldsEnum.IS_ERROR],
  150 + componentProps: {
  151 + placeholder: `请输入错误`,
  152 + },
  153 + },
  154 +];
  155 +
  156 +export function generateSearchParams(
  157 + value: Record<FormFieldsEnum, any>
  158 +): Record<'data' | 'params', Recordable> {
  159 + const {
  160 + startTime,
  161 + endTime,
  162 + dataSearch,
  163 + entityId,
  164 + entityName,
  165 + errorStr,
  166 + eventType,
  167 + isError,
  168 + metadataSearch,
  169 + msgDirectionType,
  170 + msgType,
  171 + relationType,
  172 + server,
  173 + } = value;
  174 +
  175 + return {
  176 + params: { startTime, endTime },
  177 + data: {
  178 + dataSearch,
  179 + entityId,
  180 + entityName,
  181 + errorStr,
  182 + eventType,
  183 + isError,
  184 + metadataSearch,
  185 + msgDirectionType,
  186 + msgType,
  187 + relationType,
  188 + server,
  189 + },
  190 + };
  191 +}
... ...
  1 +import { h } from 'vue';
  2 +import { RangePickerExtra } from '../RangePickerExtra';
  3 +import { FormSchema } from '/@/components/Form';
  4 +
  5 +export enum FormFieldsEnum {
  6 + ERROR_STR = 'errorStr',
  7 + METHOD = 'method',
  8 + SERVER = 'server',
  9 +
  10 + START_TIME = 'startTime',
  11 + END_TIME = 'endTime',
  12 + TIME_INTERVAL = 'timeInterval',
  13 +}
  14 +
  15 +export const formSchemas: FormSchema[] = [
  16 + {
  17 + field: FormFieldsEnum.TIME_INTERVAL,
  18 + label: '时间段',
  19 + component: 'RangePicker',
  20 + colProps: { span: 24 },
  21 + componentProps: {
  22 + showTime: true,
  23 + },
  24 + renderComponentContent: ({ model }) => {
  25 + return {
  26 + renderExtraFooter: () =>
  27 + h(RangePickerExtra, {
  28 + onChange: (date: moment.Moment[]) => {
  29 + model[FormFieldsEnum.TIME_INTERVAL] = date;
  30 + },
  31 + }),
  32 + };
  33 + },
  34 + },
  35 + {
  36 + field: FormFieldsEnum.SERVER,
  37 + label: '服务器',
  38 + component: 'Input',
  39 + componentProps: {
  40 + placeholder: `请输入服务器`,
  41 + },
  42 + },
  43 + {
  44 + field: FormFieldsEnum.METHOD,
  45 + label: '方法',
  46 + component: 'Input',
  47 + componentProps: {
  48 + placeholder: `请输入方法`,
  49 + },
  50 + },
  51 + {
  52 + field: FormFieldsEnum.ERROR_STR,
  53 + label: '错误',
  54 + component: 'Input',
  55 + componentProps: {
  56 + placeholder: `请输入错误`,
  57 + },
  58 + },
  59 +];
  60 +
  61 +export function generateSearchParams(
  62 + value: Record<FormFieldsEnum, any>
  63 +): Record<'data' | 'params', Recordable> {
  64 + const { startTime, endTime, errorStr, method, server } = value;
  65 + return {
  66 + data: { errorStr, method, server },
  67 + params: { startTime, endTime },
  68 + };
  69 +}
... ...
  1 +export { default as FilterForm } from './index.vue';
... ...
  1 +<script setup lang="ts">
  2 + import { ref, unref } from 'vue';
  3 + import { EventTypeEnum } from '../EventSelect/config';
  4 + import { getFormSchemas, transformFilterValue } from './config';
  5 + import { FormFieldsEnum } from './debug.config';
  6 + import { BasicForm, useForm } from '/@/components/Form';
  7 + import { BasicModal, useModalInner } from '/@/components/Modal';
  8 +
  9 + interface FilterChangeValueType {
  10 + data: Recordable;
  11 + params: Recordable;
  12 + }
  13 +
  14 + const emit = defineEmits<{
  15 + (eventName: 'register', value: any): any;
  16 + (eventName: 'filterChange', value: FilterChangeValueType): void;
  17 + }>();
  18 +
  19 + const [registerForm, { resetSchema, getFieldsValue, resetFields }] = useForm({
  20 + layout: 'inline',
  21 + showActionButtonGroup: false,
  22 + labelWidth: 80,
  23 + fieldMapToTime: [
  24 + [FormFieldsEnum.TIME_INTERVAL, [FormFieldsEnum.START_TIME, FormFieldsEnum.END_TIME], 'x'],
  25 + ],
  26 + });
  27 +
  28 + const eventType = ref();
  29 +
  30 + const [register, { closeModal }] = useModalInner(
  31 + (params: ModalParamsType<{ type: EventTypeEnum }>) => {
  32 + const { record } = params;
  33 + const { type } = record;
  34 + if (unref(eventType) !== type) {
  35 + resetFields();
  36 + }
  37 + eventType.value = type;
  38 + resetSchema(getFormSchemas(unref(eventType)));
  39 + }
  40 + );
  41 +
  42 + const handleOk = () => {
  43 + emit('filterChange', transformFilterValue(unref(eventType), getFieldsValue()));
  44 + closeModal();
  45 + };
  46 +
  47 + defineExpose({
  48 + resetFields,
  49 + });
  50 +</script>
  51 +
  52 +<template>
  53 + <BasicModal
  54 + @register="register"
  55 + title="过滤器"
  56 + wrapClassName="rule-node-filter-form"
  57 + @cancel="closeModal()"
  58 + @ok="handleOk"
  59 + >
  60 + <BasicForm @register="registerForm" />
  61 + </BasicModal>
  62 +</template>
  63 +
  64 +<style lang="less">
  65 + .rule-node-filter-form {
  66 + .ant-form-item-label {
  67 + label {
  68 + @apply w-full truncate block leading-8;
  69 + }
  70 + }
  71 +
  72 + .ant-calendar-picker.ant-calendar-picker-default {
  73 + @apply !w-full;
  74 + }
  75 +
  76 + .ant-input-number {
  77 + @apply w-full;
  78 + }
  79 + }
  80 +</style>
... ...
  1 +import { h } from 'vue';
  2 +import { RangePickerExtra } from '../RangePickerExtra';
  3 +import { FormSchema } from '/@/components/Form';
  4 +
  5 +export enum FormFieldsEnum {
  6 + ERROR_STR = 'errorStr',
  7 + EVENT = 'event',
  8 + SERVER = 'server',
  9 + STATUS = 'status',
  10 +
  11 + START_TIME = 'startTime',
  12 + END_TIME = 'endTime',
  13 + TIME_INTERVAL = 'timeInterval',
  14 +}
  15 +
  16 +enum StatusEnum {
  17 + SUCCESS = 'SUCCESS',
  18 + FAILURE = 'FAILURE',
  19 +}
  20 +
  21 +enum StatusNameEnum {
  22 + SUCCESS = 'Success',
  23 + FAILURE = 'Failure',
  24 +}
  25 +
  26 +export const formSchemas: FormSchema[] = [
  27 + {
  28 + field: FormFieldsEnum.TIME_INTERVAL,
  29 + label: '时间段',
  30 + component: 'RangePicker',
  31 + colProps: { span: 24 },
  32 + componentProps: {
  33 + showTime: true,
  34 + },
  35 + renderComponentContent: ({ model }) => {
  36 + return {
  37 + renderExtraFooter: () =>
  38 + h(RangePickerExtra, {
  39 + onChange: (date: moment.Moment[]) => {
  40 + model[FormFieldsEnum.TIME_INTERVAL] = date;
  41 + },
  42 + }),
  43 + };
  44 + },
  45 + },
  46 + {
  47 + field: FormFieldsEnum.SERVER,
  48 + label: '服务器',
  49 + component: 'Input',
  50 + componentProps: {
  51 + placeholder: `请输入服务器`,
  52 + },
  53 + },
  54 + {
  55 + field: FormFieldsEnum.EVENT,
  56 + label: '事件',
  57 + component: 'Input',
  58 + componentProps: {
  59 + placeholder: `请输入事件`,
  60 + },
  61 + },
  62 + {
  63 + field: FormFieldsEnum.STATUS,
  64 + label: '状态',
  65 + component: 'Input',
  66 + componentProps: {
  67 + options: Object.keys(StatusEnum).map((value) => ({ label: StatusNameEnum[value], value })),
  68 + allowClear: true,
  69 + placeholder: `请选择状态`,
  70 + getPopupContainer: () => document.body,
  71 + },
  72 + },
  73 + {
  74 + field: FormFieldsEnum.ERROR_STR,
  75 + label: '错误',
  76 + component: 'Input',
  77 + componentProps: {
  78 + placeholder: `请输入错误`,
  79 + },
  80 + },
  81 +];
  82 +
  83 +export function generateSearchParams(
  84 + value: Record<FormFieldsEnum, any>
  85 +): Record<'data' | 'params', Recordable> {
  86 + const { startTime, endTime, errorStr, event, status, server } = value;
  87 + return {
  88 + data: { errorStr, event, status, server },
  89 + params: { startTime, endTime },
  90 + };
  91 +}
... ...
  1 +import { h } from 'vue';
  2 +import { RangePickerExtra } from '../RangePickerExtra';
  3 +import { FormSchema } from '/@/components/Form';
  4 +
  5 +export enum FormFieldsEnum {
  6 + SERVER = 'server',
  7 + MESSAGE_PROCESSED = 'messageProcessed',
  8 + ERRORS_OCCURRED = 'errorsOccured',
  9 +
  10 + START_TIME = 'startTime',
  11 + END_TIME = 'endTime',
  12 + TIME_INTERVAL = 'timeInterval',
  13 +}
  14 +
  15 +export const formSchemas: FormSchema[] = [
  16 + {
  17 + field: FormFieldsEnum.TIME_INTERVAL,
  18 + label: '时间段',
  19 + component: 'RangePicker',
  20 + colProps: { span: 24 },
  21 + componentProps: {
  22 + showTime: true,
  23 + },
  24 + renderComponentContent: ({ model }) => {
  25 + return {
  26 + renderExtraFooter: () =>
  27 + h(RangePickerExtra, {
  28 + onChange: (date: moment.Moment[]) => {
  29 + model[FormFieldsEnum.TIME_INTERVAL] = date;
  30 + },
  31 + }),
  32 + };
  33 + },
  34 + },
  35 + {
  36 + field: FormFieldsEnum.SERVER,
  37 + label: '服务器',
  38 + component: 'Input',
  39 + componentProps: {
  40 + placeholder: `请输入服务器`,
  41 + },
  42 + },
  43 + {
  44 + field: FormFieldsEnum.MESSAGE_PROCESSED,
  45 + label: 'Minimum messages processed',
  46 + component: 'InputNumber',
  47 + componentProps: {
  48 + min: 0,
  49 + placeholder: `请输入Minimum messages processed`,
  50 + },
  51 + },
  52 + {
  53 + field: FormFieldsEnum.ERRORS_OCCURRED,
  54 + label: 'Minimum errors occurred',
  55 + component: 'InputNumber',
  56 + componentProps: {
  57 + min: 0,
  58 + placeholder: `请输入Minimum errors occurred`,
  59 + },
  60 + },
  61 +];
  62 +
  63 +export function generateSearchParams(
  64 + value: Record<FormFieldsEnum, any>
  65 +): Record<'data' | 'params', Recordable> {
  66 + const { startTime, endTime, messageProcessed, errorsOccured, server } = value;
  67 + return {
  68 + data: { messageProcessed, errorsOccured, server },
  69 + params: { startTime, endTime },
  70 + };
  71 +}
... ...
  1 +import { dateUtil } from '/@/utils/dateUtil';
  2 +
  3 +export enum TimeUnitEnum {
  4 + day = 'day',
  5 + hour = 'hour',
  6 + minute = 'minute',
  7 + second = 'second',
  8 +}
  9 +export enum TimeUnitNameEnum {
  10 + day = '天',
  11 + hour = '小时',
  12 + minute = '分',
  13 + second = '秒',
  14 +}
  15 +
  16 +export enum DateShortcutOptionEnum {
  17 + YESTERDAY = 'YESTERDAY',
  18 + DAY_BEFORE_YESTERDAY = 'DAY_BEFORE_YESTERDAY',
  19 + THIS_DAY_LAST_WEEK = 'THIS_DAY_LAST_WEEK',
  20 + PREVIOUS_WEEK_SUN_SAT = 'PREVIOUS_WEEK_SUN_SAT',
  21 + PREVIOUS_WEEK_MON_SUN = 'PREVIOUS_WEEK_MON_SUN',
  22 + PREVIOUS_MONTH = 'PREVIOUS_MONTH',
  23 + PREVIOUS_YEAR = 'PREVIOUS_YEAR',
  24 + CURRENT_HOUR = 'CURRENT_HOUR',
  25 + CURRENT_DAY = 'CURRENT_DAY',
  26 + CURRENT_DAY_SO_FAR = 'CURRENT_DAY_SO_FAR',
  27 + CURRENT_WEEK_SUN_SAT = 'CURRENT_WEEK_SUN_SAT',
  28 + CURRENT_WEEK_MON_SUN = 'CURRENT_WEEK_MON_SUN',
  29 + CURRENT_WEEK_SO_FAR_SUN_SAT = 'CURRENT_WEEK_SO_FAR_SUN_SAT',
  30 + CURRENT_WEEK_SO_FAR_MON_SUN = 'CURRENT_WEEK_SO_FAR_MON_SUN',
  31 + CURRENT_MONTH = 'CURRENT_MONTH',
  32 + CURRENT_MONTH_SO_FAR = 'CURRENT_MONTH_SO_FAR',
  33 + CURRENT_YEAR = 'CURRENT_YEAR',
  34 + CURRENT_YEAR_SO_FAR = 'CURRENT_YEAR_SO_FAR',
  35 +}
  36 +export enum DateShortcutOptionNameEnum {
  37 + YESTERDAY = '昨天',
  38 + DAY_BEFORE_YESTERDAY = '前天',
  39 + THIS_DAY_LAST_WEEK = '前一周的这一天',
  40 + PREVIOUS_WEEK_SUN_SAT = '前一周 (周日至周六)',
  41 + PREVIOUS_WEEK_MON_SUN = '前一周 (周一至周日)',
  42 + PREVIOUS_MONTH = '前一个月',
  43 + PREVIOUS_YEAR = '前一年',
  44 + CURRENT_HOUR = '当前小时',
  45 + CURRENT_DAY = '当前天',
  46 + CURRENT_DAY_SO_FAR = '当天到目前为止',
  47 + CURRENT_WEEK_SUN_SAT = '本周 (周日至周六)',
  48 + CURRENT_WEEK_MON_SUN = '本周 (周一至周日)',
  49 + CURRENT_WEEK_SO_FAR_SUN_SAT = '本周到目前为止 (周日至周六)',
  50 + CURRENT_WEEK_SO_FAR_MON_SUN = '本周到目前为止 (周一至周日)',
  51 + CURRENT_MONTH = '本月',
  52 + CURRENT_MONTH_SO_FAR = '本月到目前为止',
  53 + CURRENT_YEAR = '本年',
  54 + CURRENT_YEAR_SO_FAR = '本年到目前为止',
  55 +}
  56 +
  57 +const options = [
  58 + { value: 1, unit: TimeUnitEnum.second },
  59 + { value: 5, unit: TimeUnitEnum.second },
  60 + { value: 10, unit: TimeUnitEnum.second },
  61 + { value: 15, unit: TimeUnitEnum.second },
  62 + { value: 30, unit: TimeUnitEnum.second },
  63 +
  64 + { value: 1, unit: TimeUnitEnum.minute },
  65 + { value: 5, unit: TimeUnitEnum.minute },
  66 + { value: 10, unit: TimeUnitEnum.minute },
  67 + { value: 15, unit: TimeUnitEnum.minute },
  68 + { value: 30, unit: TimeUnitEnum.minute },
  69 +
  70 + { value: 1, unit: TimeUnitEnum.hour },
  71 + { value: 2, unit: TimeUnitEnum.hour },
  72 + { value: 5, unit: TimeUnitEnum.hour },
  73 + { value: 10, unit: TimeUnitEnum.hour },
  74 + { value: 12, unit: TimeUnitEnum.hour },
  75 +
  76 + { value: 1, unit: TimeUnitEnum.day },
  77 + { value: 7, unit: TimeUnitEnum.day },
  78 + { value: 30, unit: TimeUnitEnum.day },
  79 +];
  80 +
  81 +export const latestOptions = options.map((item) => ({
  82 + ...item,
  83 + value: `${item.value}${item.unit}`,
  84 + label: `${item.value} ${TimeUnitNameEnum[item.unit]}`,
  85 + unitValue: item.value,
  86 +}));
  87 +
  88 +export const shortcutOptions = Object.keys(DateShortcutOptionEnum).map((value) => ({
  89 + label: DateShortcutOptionNameEnum[value],
  90 + value,
  91 +}));
  92 +
  93 +export const getShortcutOptionValue = (value: DateShortcutOptionEnum) => {
  94 + const mapping = {
  95 + YESTERDAY: () => {
  96 + const startDate = dateUtil().subtract(1, 'day').startOf('day');
  97 + const endDate = dateUtil().subtract(1, 'day').endOf('day');
  98 + return [startDate, endDate];
  99 + },
  100 + DAY_BEFORE_YESTERDAY: () => {
  101 + const startDate = dateUtil().subtract(2, 'day').startOf('day');
  102 + const endDate = dateUtil().subtract(2, 'day').endOf('day');
  103 + return [startDate, endDate];
  104 + },
  105 + THIS_DAY_LAST_WEEK: () => {
  106 + const startDate = dateUtil().subtract(7, 'day').startOf('day');
  107 + const endDate = dateUtil().subtract(7, 'day').endOf('day');
  108 + return [startDate, endDate];
  109 + },
  110 + PREVIOUS_WEEK_SUN_SAT: () => {
  111 + const startDate = dateUtil()
  112 + .subtract(1, 'week')
  113 + .startOf('week')
  114 + .subtract(1, 'day')
  115 + .startOf('day');
  116 + const endDate = dateUtil().subtract(1, 'week').endOf('week').subtract(1, 'day').endOf('day');
  117 + return [startDate, endDate];
  118 + },
  119 + PREVIOUS_WEEK_MON_SUN: () => {
  120 + const startDate = dateUtil().subtract(1, 'week').startOf('week');
  121 + const endDate = dateUtil().subtract(1, 'week').endOf('week');
  122 + return [startDate, endDate];
  123 + },
  124 + PREVIOUS_MONTH: () => {
  125 + const startDate = dateUtil().subtract(1, 'month').startOf('month');
  126 + const endDate = dateUtil().subtract(1, 'month').endOf('month');
  127 + return [startDate, endDate];
  128 + },
  129 + PREVIOUS_YEAR: () => {
  130 + const startDate = dateUtil().subtract(1, 'year').startOf('year');
  131 + const endDate = dateUtil().subtract(1, 'year').endOf('year');
  132 + return [startDate, endDate];
  133 + },
  134 + CURRENT_HOUR: () => {
  135 + const startDate = dateUtil().startOf('hour');
  136 + const endDate = dateUtil().endOf('hour');
  137 + return [startDate, endDate];
  138 + },
  139 + CURRENT_DAY: () => {
  140 + const startDate = dateUtil().startOf('day');
  141 + const endDate = dateUtil().endOf('day');
  142 + return [startDate, endDate];
  143 + },
  144 + CURRENT_DAY_SO_FAR: () => {
  145 + const startDate = dateUtil().startOf('day');
  146 + const endDate = dateUtil();
  147 + return [startDate, endDate];
  148 + },
  149 + CURRENT_WEEK_SUN_SAT: () => {
  150 + const startDate = dateUtil().startOf('week').subtract(1, 'day').startOf('day');
  151 + const endDate = dateUtil().endOf('week').subtract(1, 'day').endOf('day');
  152 + return [startDate, endDate];
  153 + },
  154 + CURRENT_WEEK_MON_SUN: () => {
  155 + const startDate = dateUtil().startOf('week');
  156 + const endDate = dateUtil().endOf('week');
  157 + return [startDate, endDate];
  158 + },
  159 + CURRENT_WEEK_SO_FAR_SUN_SAT: () => {
  160 + const startDate = dateUtil().startOf('week').subtract(1, 'day').startOf('day');
  161 + const endDate = dateUtil();
  162 + return [startDate, endDate];
  163 + },
  164 + CURRENT_WEEK_SO_FAR_MON_SUN: () => {
  165 + const startDate = dateUtil().startOf('week');
  166 + const endDate = dateUtil();
  167 + return [startDate, endDate];
  168 + },
  169 + CURRENT_MONTH: () => {
  170 + const startDate = dateUtil().startOf('month');
  171 + const endDate = dateUtil().endOf('month');
  172 + return [startDate, endDate];
  173 + },
  174 + CURRENT_MONTH_SO_FAR: () => {
  175 + const startDate = dateUtil().startOf('month');
  176 + const endDate = dateUtil();
  177 + return [startDate, endDate];
  178 + },
  179 + CURRENT_YEAR: () => {
  180 + const startDate = dateUtil().startOf('year');
  181 + const endDate = dateUtil().endOf('year');
  182 + return [startDate, endDate];
  183 + },
  184 + CURRENT_YEAR_SO_FAR: () => {
  185 + const startDate = dateUtil().startOf('year');
  186 + const endDate = dateUtil();
  187 + return [startDate, endDate];
  188 + },
  189 + };
  190 + return mapping?.[value]?.() || [];
  191 +};
... ...
  1 +export { default as RangePickerExtra } from './index.vue';
... ...
  1 +<script setup lang="ts">
  2 + import { Button, Popover, Select, Switch, InputNumber } from 'ant-design-vue';
  3 + import { reactive, ref, unref } from 'vue';
  4 + import {
  5 + DateShortcutOptionEnum,
  6 + getShortcutOptionValue,
  7 + latestOptions,
  8 + shortcutOptions,
  9 + } from './config';
  10 + import { getPopupContainer } from '/@/utils';
  11 + import { dateUtil } from '/@/utils/dateUtil';
  12 +
  13 + const emit = defineEmits<{
  14 + (eventName: 'change', date: moment.Moment[]): void;
  15 + }>();
  16 +
  17 + const mode = ref(false);
  18 +
  19 + const latestPopoverVisible = ref(false);
  20 +
  21 + const latestSelect = ref();
  22 +
  23 + const advancedLatest = reactive({
  24 + days: 0,
  25 + hours: 0,
  26 + minutes: 0,
  27 + seconds: 0,
  28 + });
  29 +
  30 + const getAdvancedLatest = () => {
  31 + const startDate = Object.keys(advancedLatest).reduce((prev, next) => {
  32 + return prev.subtract(advancedLatest[next], next);
  33 + }, dateUtil());
  34 + return [startDate, dateUtil()];
  35 + };
  36 +
  37 + const getLatestSelect = () => {
  38 + const selected = latestOptions.find((item) => item.value === unref(latestSelect))!;
  39 + if (!selected) return [];
  40 + const startDate = dateUtil().subtract(selected.unitValue, selected.unit);
  41 + return [startDate, dateUtil()];
  42 + };
  43 +
  44 + const handleUpdateDate = () => {
  45 + emit('change', unref(mode) ? getAdvancedLatest() : getLatestSelect());
  46 + latestPopoverVisible.value = false;
  47 + };
  48 +
  49 + const shortcutOptionPopoverVisible = ref(false);
  50 +
  51 + const shortcutOptionSelect = ref<DateShortcutOptionEnum>();
  52 +
  53 + const handleShortcutOptionUpdate = () => {
  54 + unref(shortcutOptionSelect) &&
  55 + emit('change', getShortcutOptionValue(unref(shortcutOptionSelect)!));
  56 + shortcutOptionPopoverVisible.value = false;
  57 + };
  58 +</script>
  59 +
  60 +<template>
  61 + <section class="h-full items-center flex gap-2 h-9">
  62 + <Popover
  63 + v-model:visible="latestPopoverVisible"
  64 + trigger="click"
  65 + :overlayStyle="{ zIndex: 9999 }"
  66 + overlayClassName="range-picker-extra-latest-popover"
  67 + >
  68 + <template #content>
  69 + <main class="w-90 p-4">
  70 + <div class="flex gap-3 items-end">
  71 + <div v-if="!mode" class="w-full">
  72 + <Select
  73 + v-model:value="latestSelect"
  74 + class="w-full border-b"
  75 + :getPopupContainer="getPopupContainer"
  76 + placeholder="请选择区间"
  77 + :bordered="false"
  78 + :options="latestOptions"
  79 + />
  80 + </div>
  81 +
  82 + <div v-if="mode" class="w-full flex gap-1">
  83 + <div class="w-1/4">
  84 + <div>天</div>
  85 + <InputNumber v-model:value="advancedLatest.days" :min="0" :precision="0" />
  86 + </div>
  87 + <div class="w-1/4">
  88 + <div>小时</div>
  89 + <InputNumber v-model:value="advancedLatest.hours" :min="0" :precision="0" />
  90 + </div>
  91 + <div class="w-1/4">
  92 + <div>分钟</div>
  93 + <InputNumber v-model:value="advancedLatest.minutes" :min="0" :precision="0" />
  94 + </div>
  95 + <div class="w-1/4">
  96 + <div>秒</div>
  97 + <InputNumber v-model:value="advancedLatest.seconds" :min="0" :precision="0" />
  98 + </div>
  99 + </div>
  100 +
  101 + <div>
  102 + <div class="h-6 text-center">高级</div>
  103 + <Switch v-model:checked="mode" />
  104 + </div>
  105 + </div>
  106 + <div class="w-full flex mt-4 gap-4 justify-end">
  107 + <Button size="small" @click="latestPopoverVisible = false"> 取消</Button>
  108 + <Button size="small" type="primary" @click="handleUpdateDate"> 更新</Button>
  109 + </div>
  110 + </main>
  111 + </template>
  112 + <Button type="primary" size="small" @click="latestPopoverVisible = true">最后</Button>
  113 + </Popover>
  114 +
  115 + <Popover
  116 + v-model:visible="shortcutOptionPopoverVisible"
  117 + trigger="click"
  118 + :overlayStyle="{ zIndex: 9999 }"
  119 + overlayClassName="range-picker-extra-shortcut-popover"
  120 + >
  121 + <template #content>
  122 + <main class="w-90 p-4">
  123 + <div>
  124 + <Select
  125 + v-model:value="shortcutOptionSelect"
  126 + class="w-full border-b"
  127 + :getPopupContainer="getPopupContainer"
  128 + placeholder="请选择区间"
  129 + :bordered="false"
  130 + :options="shortcutOptions"
  131 + />
  132 + </div>
  133 + <div class="w-full flex mt-4 gap-4 justify-end">
  134 + <Button size="small" @click="shortcutOptionPopoverVisible = false"> 取消</Button>
  135 + <Button size="small" type="primary" @click="handleShortcutOptionUpdate"> 更新</Button>
  136 + </div>
  137 + </main>
  138 + </template>
  139 + <Button type="primary" size="small" @click="shortcutOptionPopoverVisible = true">
  140 + 快捷选择
  141 + </Button>
  142 + </Popover>
  143 + </section>
  144 +</template>
  145 +
  146 +<style lang="less">
  147 + .range-picker-extra-latest-popover {
  148 + .ant-input-number {
  149 + min-width: fit-content;
  150 + width: 100%;
  151 + }
  152 + }
  153 +</style>
... ...
... ... @@ -9,7 +9,7 @@
9 9 import { useForm, BasicForm } from '/@/components/Form';
10 10 import { BottomFormSchemas, TopFormSchemas } from '../CreateNodeModal/config';
11 11 import { toRaw, unref } from 'vue';
12   - import { BasicEvents } from '../BasicEvents';
  12 + import { BasicEvents } from './BasicEvents';
13 13
14 14 const [topFormRegister, topFormActionType] = useForm({
15 15 schemas: TopFormSchemas,
... ... @@ -25,6 +25,7 @@
25 25 visible,
26 26 spinning,
27 27 nodeData,
  28 + elementInfo,
28 29 getComponentKey,
29 30 shadowComponent,
30 31 createComponentEl,
... ... @@ -113,7 +114,7 @@
113 114 </Spin>
114 115 </Tabs.TabPane>
115 116 <Tabs.TabPane :tab="TabsPanelNameEnum[TabsPanelEnum.EVENT]" :key="TabsPanelEnum.EVENT">
116   - <BasicEvents />
  117 + <BasicEvents :elementInfo="elementInfo" />
117 118 </Tabs.TabPane>
118 119 <Tabs.TabPane :tab="TabsPanelNameEnum[TabsPanelEnum.HELP]" :key="TabsPanelEnum.HELP">
119 120 <HelpMessage :nodeData="nodeData" />
... ...