TimeRangeSelect.vue 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <script setup lang="ts">
  2. import { computed, onMounted, ref } from 'vue';
  3. import dayjs from 'dayjs';
  4. import { t } from '@/i18n';
  5. import type { PickerMode } from 'ant-design-vue/es/vc-picker/interface';
  6. import type { Dayjs, OpUnitType } from 'dayjs';
  7. import type { OptionItem, RangeValue } from '@/types';
  8. type DateType = 'year' | 'month' | 'date' | 'custom';
  9. type LastType = '1d' | '7d' | '30d' | 'custom';
  10. interface Props {
  11. isLast?: boolean;
  12. }
  13. const props = withDefaults(defineProps<Props>(), {
  14. isLast: false,
  15. });
  16. const emit = defineEmits<{
  17. change: [range: RangeValue];
  18. }>();
  19. const selectedType = ref<DateType | LastType>(props.isLast ? '1d' : 'month');
  20. const singleDate = ref<Dayjs>();
  21. const rangeDate = ref<RangeValue>();
  22. const isCustomType = computed(() => {
  23. return selectedType.value === 'custom';
  24. });
  25. const typeOptions = computed<OptionItem<DateType | LastType>[]>(() => {
  26. if (props.isLast) {
  27. return [
  28. { value: '1d', label: t('common.recent1Day') },
  29. { value: '7d', label: t('common.recent7Day') },
  30. { value: '30d', label: t('common.recent30Day') },
  31. { value: 'custom', label: t('common.custom') },
  32. ];
  33. }
  34. return [
  35. { value: 'year', label: t('common.year') },
  36. { value: 'month', label: t('common.month') },
  37. { value: 'date', label: t('common.date') },
  38. { value: 'custom', label: t('common.custom') },
  39. ];
  40. });
  41. onMounted(() => {
  42. handleTypeChange();
  43. });
  44. const disabledDate = (current: Dayjs) => {
  45. return current > dayjs().endOf('day');
  46. };
  47. const handleTypeChange = () => {
  48. if (props.isLast) {
  49. handleLastTypeChange();
  50. } else {
  51. handleNormalTypeChange();
  52. }
  53. };
  54. const handleNormalTypeChange = () => {
  55. const now = dayjs();
  56. if (isCustomType.value) {
  57. if (!rangeDate.value) {
  58. rangeDate.value = [now.startOf('month'), now.endOf('day')];
  59. }
  60. handleRangeChange();
  61. } else {
  62. if (!singleDate.value) {
  63. singleDate.value = now;
  64. }
  65. handleDateChange();
  66. }
  67. };
  68. const handleLastTypeChange = () => {
  69. if (!isCustomType.value) {
  70. const days = parseInt(selectedType.value);
  71. const end = dayjs();
  72. const start = end.subtract(days, 'day');
  73. rangeDate.value = [start, end];
  74. emitChange(start, end);
  75. }
  76. };
  77. const handleDateChange = () => {
  78. if (!singleDate.value) {
  79. return;
  80. }
  81. const type = selectedType.value as OpUnitType;
  82. emitChange(singleDate.value.startOf(type), singleDate.value.endOf(type));
  83. };
  84. const handleRangeChange = () => {
  85. if (!rangeDate.value) {
  86. return;
  87. }
  88. selectedType.value = 'custom';
  89. emitChange(rangeDate.value[0].startOf('day'), rangeDate.value[1].endOf('day'));
  90. };
  91. const emitChange = (start: Dayjs, end: Dayjs) => {
  92. emit('change', [start, end]);
  93. };
  94. </script>
  95. <template>
  96. <div class="time-select-container">
  97. <span class="time-select-label">{{ $t('common.selectTime') }}</span>
  98. <ASelect class="time-select-type" v-model:value="selectedType" @change="handleTypeChange">
  99. <ASelectOption v-for="item in typeOptions" :key="item.value" :value="item.value">
  100. {{ item.label }}
  101. </ASelectOption>
  102. </ASelect>
  103. <ADatePicker
  104. v-show="!isLast && !isCustomType"
  105. v-model:value="singleDate"
  106. :picker="<PickerMode>selectedType"
  107. :allow-clear="false"
  108. :disabled-date="disabledDate"
  109. @change="handleDateChange"
  110. >
  111. <template #suffixIcon>
  112. <SvgIcon name="calendar" color="#333" />
  113. </template>
  114. </ADatePicker>
  115. <ARangePicker
  116. class="time-select-range-picker"
  117. v-show="isLast || isCustomType"
  118. v-model:value="rangeDate"
  119. :allow-clear="false"
  120. :separator="$t('common.to')"
  121. :disabled-date="disabledDate"
  122. @change="handleRangeChange"
  123. >
  124. <template #suffixIcon>
  125. <SvgIcon name="calendar" color="#333" />
  126. </template>
  127. </ARangePicker>
  128. </div>
  129. </template>
  130. <style lang="scss" scoped>
  131. .time-select-container {
  132. display: inline-flex;
  133. align-items: center;
  134. }
  135. .time-select-label {
  136. margin-right: 16px;
  137. font-size: 14px;
  138. line-height: 22px;
  139. color: #000;
  140. }
  141. .time-select-type {
  142. width: 110px;
  143. margin-right: 12px;
  144. }
  145. .time-select-range-picker {
  146. width: 256px;
  147. :deep(.ant-picker-range-separator) {
  148. padding-right: 16px;
  149. }
  150. }
  151. </style>