Commit 20b454eb54516e51d95d8be5b3c3340d85f05f74

Authored by ww
1 parent 3e65c533

feat: 新增事件面板

  1 +<script lang="ts" setup>
  2 + import { ref, computed, Ref, unref } from 'vue';
  3 + import { Select, Switch, InputNumber } from 'ant-design-vue';
  4 + import { latestOptions, LatestOptionsEnum } from './config';
  5 + import { dateUtil } from '/@/utils/dateUtil';
  6 +
  7 + type DateUnit = 'days' | 'hours' | 'minutes' | 'seconds';
  8 +
  9 + type RangeObject = Record<DateUnit, number>;
  10 +
  11 + const emit = defineEmits(['update:value']);
  12 +
  13 + const props = withDefaults(
  14 + defineProps<{
  15 + value?: number;
  16 + }>(),
  17 + {
  18 + value: 1000 * 60 * 60 * 24 * 30,
  19 + }
  20 + );
  21 +
  22 + const advancedMode = ref(false);
  23 +
  24 + const getRangeObject = (milliseconds: number) => {
  25 + const methods: { method: string; key: moment.unitOfTime.Base }[] = [
  26 + { method: 'asDays', key: 'days' },
  27 + { method: 'asHours', key: 'hours' },
  28 + { method: 'asMinutes', key: 'minutes' },
  29 + { method: 'asSeconds', key: 'seconds' },
  30 + ];
  31 +
  32 + return methods.reduce((prev, next) => {
  33 + const { method, key } = next;
  34 + const value = dateUtil.duration(milliseconds, 'millisecond')[method]?.();
  35 +
  36 + if (value >= 1) {
  37 + milliseconds -= dateUtil.duration(parseInt(value), key).asMilliseconds();
  38 + }
  39 +
  40 + return { ...prev, [key]: value >= 1 ? parseInt(value) : 0 };
  41 + }, {} as Record<DateUnit, number>);
  42 + };
  43 +
  44 + const getMillisecondsByObject = (value: RangeObject | Ref<RangeObject>) => {
  45 + value = unref(value);
  46 + return Object.keys(unref(value)).reduce((prev, next) => {
  47 + return (
  48 + prev +
  49 + dateUtil
  50 + .duration(value[next], next as moment.unitOfTime.DurationConstructor)
  51 + .asMilliseconds()
  52 + );
  53 + }, 0);
  54 + };
  55 +
  56 + const getValue = computed(() => {
  57 + const { value } = props;
  58 + const dateRange = getRangeObject(value);
  59 + const { days, hours, minutes, seconds } = dateRange;
  60 +
  61 + const filterList = latestOptions.filter((item) => value >= item.value);
  62 + return {
  63 + days,
  64 + hours,
  65 + minutes,
  66 + seconds,
  67 + selectValue: filterList[filterList.length - 1].value,
  68 + };
  69 + });
  70 +
  71 + const handleSyncValue = (value: LatestOptionsEnum) => {
  72 + emit('update:value', value);
  73 + };
  74 +
  75 + const handleChange = (value: number, unit: DateUnit) => {
  76 + if (isNaN(value)) return;
  77 + let { days, hours, minutes, seconds } = unref(getValue);
  78 + const dateRangeObject: RangeObject = {
  79 + days,
  80 + hours,
  81 + minutes,
  82 + seconds,
  83 + [unit]: value,
  84 + };
  85 +
  86 + const milliseconds = getMillisecondsByObject(dateRangeObject);
  87 + emit('update:value', milliseconds);
  88 + };
  89 +</script>
  90 +
  91 +<template>
  92 + <section class="flex justify-center items-end gap-4 h-14 latest-date-picker">
  93 + <section v-if="advancedMode" class="flex flex-auto custom-date-range">
  94 + <div>
  95 + <div>天</div>
  96 + <InputNumber
  97 + :value="getValue.days"
  98 + @change="(event) => handleChange(event, 'days')"
  99 + :step="1"
  100 + :max="7300"
  101 + :min="0"
  102 + />
  103 + </div>
  104 + <div>
  105 + <div>小时</div>
  106 + <InputNumber
  107 + :value="getValue.hours"
  108 + @change="(event) => handleChange(event, 'hours')"
  109 + :step="1"
  110 + :max="23"
  111 + :min="0"
  112 + />
  113 + </div>
  114 + <div>
  115 + <div>分钟</div>
  116 + <InputNumber
  117 + :value="getValue.minutes"
  118 + @change="(event) => handleChange(event, 'minutes')"
  119 + :step="1"
  120 + :max="59"
  121 + :min="0"
  122 + />
  123 + </div>
  124 + <div>
  125 + <div>秒</div>
  126 + <InputNumber
  127 + :value="getValue.seconds"
  128 + @change="(event) => handleChange(event, 'seconds')"
  129 + :step="1"
  130 + :max="59"
  131 + :min="0"
  132 + />
  133 + </div>
  134 + </section>
  135 + <Select
  136 + v-if="!advancedMode"
  137 + class="!flex-auto"
  138 + v-model:value="getValue.selectValue"
  139 + :options="latestOptions"
  140 + placeholder="请选择最后时间段"
  141 + @change="handleSyncValue"
  142 + allow-clear
  143 + />
  144 + <div class="w-14 flex flex-col justify-between h-full text-center">
  145 + <div>高级</div>
  146 + <Switch class="w-14" v-model:checked="advancedMode" />
  147 + </div>
  148 + </section>
  149 +</template>
  150 +
  151 +<style scoped lang="less">
  152 + .latest-date-picker {
  153 + :deep(.ant-input-number) {
  154 + min-width: 0;
  155 + width: 100%;
  156 + }
  157 +
  158 + :deep(.ant-select-selection-item) {
  159 + pointer-events: none;
  160 + }
  161 + }
  162 +</style>
... ...
  1 +import { dateUtil } from '/@/utils/dateUtil';
  2 +
  3 +export enum ModeEnum {
  4 + LATEST = 'LATEST',
  5 + RANGE = 'RANGE',
  6 + INTERVAL = 'INTERVAL',
  7 +}
  8 +
  9 +export enum ModeNameEnum {
  10 + LATEST = '最后',
  11 + RANGE = '时间段',
  12 + INTERVAL = '时间间隔',
  13 +}
  14 +
  15 +export enum UnitEnum {
  16 + SECONDS = '秒',
  17 + MINUTES = '分',
  18 + HOURS = '时',
  19 + DAYS = '天',
  20 +}
  21 +
  22 +export enum LatestOptionsEnum {
  23 + '1_SECONDS' = '1_SECONDS',
  24 + '5_SECONDS' = '5_SECONDS',
  25 + '10_SECONDS' = '10_SECONDS',
  26 + '15_SECONDS' = '15_SECONDS',
  27 + '30_SECONDS' = '30_SECONDS',
  28 +
  29 + '1_MINUTES' = '1_MINUTES',
  30 + '2_MINUTES' = '2_MINUTES',
  31 + '5_MINUTES' = '5_MINUTES',
  32 + '10_MINUTES' = '10_MINUTES',
  33 + '15_MINUTES' = '15_MINUTES',
  34 + '30_MINUTES' = '30_MINUTES',
  35 +
  36 + '1_HOURS' = '1_HOURS',
  37 + '2_HOURS' = '2_HOURS',
  38 + '5_HOURS' = '5_HOURS',
  39 + '10_HOURS' = '10_HOURS',
  40 + '12_HOURS' = '12_HOURS',
  41 +
  42 + '1_DAYS' = '1_DAYS',
  43 + '7_DAYS' = '7_DAYS',
  44 + '30_DAYS' = '30_DAYS',
  45 +}
  46 +
  47 +const getLatesetOptionLabel = (string: string) => {
  48 + const [value, unit] = string.split('_');
  49 + return `${value} ${UnitEnum[unit]}`;
  50 +};
  51 +
  52 +const getLatesetOptionValue = (string: string) => {
  53 + const [value, unit] = string.split('_');
  54 + return dateUtil
  55 + .duration(Number(value), unit.toLowerCase() as moment.unitOfTime.DurationConstructor)
  56 + .asMilliseconds();
  57 +};
  58 +
  59 +export const latestOptions: { label: string; value: number }[] = Object.keys(LatestOptionsEnum).map(
  60 + (key) => ({ label: getLatesetOptionLabel(key), value: getLatesetOptionValue(key) })
  61 +);
  62 +
  63 +[
  64 + 'Yesterday',
  65 + 'Day before yesterday',
  66 + 'This day last week',
  67 + 'Previous week (Sun - Sat)',
  68 + 'Previous week (Mon - Sun)',
  69 + 'Previous month',
  70 + 'Previous year',
  71 + 'Current hour',
  72 + 'Current day',
  73 + 'Current day so far',
  74 + 'Current week (Sun - Sat)',
  75 + 'Current week (Mon - Sun)',
  76 + 'Current week so far (Sun - Sat)',
  77 + 'Current week so far (Mon - Sun)',
  78 + 'Current month',
  79 + 'Current month so far',
  80 + 'Current year',
  81 + 'Current year so far',
  82 +];
... ...
  1 +export { default as DateRangeButton } from './index.vue';
... ...
  1 +<script lang="ts" setup>
  2 + import { Popconfirm, Button, Radio, RadioGroup, RangePicker } from 'ant-design-vue';
  3 + import { ref } from 'vue';
  4 + import { Icon } from '/@/components/Icon';
  5 + import { ModeEnum, ModeNameEnum } from './config';
  6 + import LatestDate from './LatestDate.vue';
  7 +
  8 + const mode = ref(ModeEnum.LATEST);
  9 +
  10 + const visible = ref(true);
  11 +
  12 + const latestValue = ref<number>(1000 * 60 * 60 * 24 * 30);
  13 +</script>
  14 +
  15 +<template>
  16 + <Popconfirm :visible="visible">
  17 + <template #icon>
  18 + <span></span>
  19 + </template>
  20 + <template #title>
  21 + <RadioGroup v-model:value="mode" class="!flex flex-col gap-2 w-96">
  22 + <Radio :value="ModeEnum.LATEST">
  23 + <span>{{ ModeNameEnum.LATEST }}</span>
  24 + <LatestDate v-model:value="latestValue" v-if="mode === ModeEnum.LATEST" />
  25 + </Radio>
  26 + <Radio :value="ModeEnum.RANGE">
  27 + <span>{{ ModeNameEnum.RANGE }}</span>
  28 + <section v-if="mode === ModeEnum.RANGE" class="mt-2 flex flex-col justify-end">
  29 + <RangePicker :showTime="true" />
  30 + </section>
  31 + </Radio>
  32 + <Radio :value="ModeEnum.INTERVAL">
  33 + <span>{{ ModeNameEnum.INTERVAL }}</span>
  34 + <section class="mt-2 flex flex-col justify-end">
  35 + <!-- -->
  36 + </section>
  37 + </Radio>
  38 + </RadioGroup>
  39 + </template>
  40 + <Button type="primary" @click="visible = true">
  41 + <Icon icon="mdi:clock-outline" />
  42 + </Button>
  43 + </Popconfirm>
  44 +</template>
... ...
  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 +export { default as BasicEvents } from './index.vue';
... ...
  1 +<script lang="ts" setup>
  2 + import DebugEvent from './DebugEvent.vue';
  3 +</script>
  4 +
  5 +<template>
  6 + <section class="p-2 bg-gray-100 dark:bg-dark-200">
  7 + <DebugEvent />
  8 + </section>
  9 +</template>
... ...