|
@@ -0,0 +1,180 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import { computed, onMounted, ref } from 'vue';
|
|
|
+import dayjs from 'dayjs';
|
|
|
+
|
|
|
+import { t } from '@/i18n';
|
|
|
+
|
|
|
+import type { PickerMode } from 'ant-design-vue/es/vc-picker/interface';
|
|
|
+import type { Dayjs, OpUnitType } from 'dayjs';
|
|
|
+import type { OptionItem, RangeValue } from '@/types';
|
|
|
+
|
|
|
+type DateType = 'year' | 'month' | 'date' | 'custom';
|
|
|
+type LastType = '1d' | '7d' | '30d' | 'custom';
|
|
|
+
|
|
|
+interface Props {
|
|
|
+ isLast?: boolean;
|
|
|
+}
|
|
|
+
|
|
|
+const props = withDefaults(defineProps<Props>(), {
|
|
|
+ isLast: false,
|
|
|
+});
|
|
|
+
|
|
|
+const emit = defineEmits<{
|
|
|
+ change: [range: RangeValue];
|
|
|
+}>();
|
|
|
+
|
|
|
+const selectedType = ref<DateType | LastType>(props.isLast ? '1d' : 'month');
|
|
|
+const singleDate = ref<Dayjs>();
|
|
|
+const rangeDate = ref<RangeValue>();
|
|
|
+
|
|
|
+const isCustomType = computed(() => {
|
|
|
+ return selectedType.value === 'custom';
|
|
|
+});
|
|
|
+
|
|
|
+const typeOptions = computed<OptionItem<DateType | LastType>[]>(() => {
|
|
|
+ if (props.isLast) {
|
|
|
+ return [
|
|
|
+ { value: '1d', label: t('common.recent1Day') },
|
|
|
+ { value: '7d', label: t('common.recent7Day') },
|
|
|
+ { value: '30d', label: t('common.recent30Day') },
|
|
|
+ { value: 'custom', label: t('common.custom') },
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ { value: 'year', label: t('common.year') },
|
|
|
+ { value: 'month', label: t('common.month') },
|
|
|
+ { value: 'date', label: t('common.date') },
|
|
|
+ { value: 'custom', label: t('common.custom') },
|
|
|
+ ];
|
|
|
+});
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ handleTypeChange();
|
|
|
+});
|
|
|
+
|
|
|
+const disabledDate = (current: Dayjs) => {
|
|
|
+ return current > dayjs().endOf('day');
|
|
|
+};
|
|
|
+
|
|
|
+const handleTypeChange = () => {
|
|
|
+ if (props.isLast) {
|
|
|
+ handleLastTypeChange();
|
|
|
+ } else {
|
|
|
+ handleNormalTypeChange();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const handleNormalTypeChange = () => {
|
|
|
+ const now = dayjs();
|
|
|
+
|
|
|
+ if (isCustomType.value) {
|
|
|
+ if (!rangeDate.value) {
|
|
|
+ rangeDate.value = [now.startOf('month'), now.endOf('day')];
|
|
|
+ }
|
|
|
+
|
|
|
+ handleRangeChange();
|
|
|
+ } else {
|
|
|
+ if (!singleDate.value) {
|
|
|
+ singleDate.value = now;
|
|
|
+ }
|
|
|
+
|
|
|
+ handleDateChange();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const handleLastTypeChange = () => {
|
|
|
+ if (!isCustomType.value) {
|
|
|
+ const days = parseInt(selectedType.value);
|
|
|
+ const end = dayjs();
|
|
|
+ const start = end.subtract(days, 'day');
|
|
|
+ rangeDate.value = [start, end];
|
|
|
+ emitChange(start, end);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const handleDateChange = () => {
|
|
|
+ if (!singleDate.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const type = selectedType.value as OpUnitType;
|
|
|
+ emitChange(singleDate.value.startOf(type), singleDate.value.endOf(type));
|
|
|
+};
|
|
|
+
|
|
|
+const handleRangeChange = () => {
|
|
|
+ if (!rangeDate.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ selectedType.value = 'custom';
|
|
|
+ emitChange(rangeDate.value[0].startOf('day'), rangeDate.value[1].endOf('day'));
|
|
|
+};
|
|
|
+
|
|
|
+const emitChange = (start: Dayjs, end: Dayjs) => {
|
|
|
+ emit('change', [start, end]);
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div class="time-select-container">
|
|
|
+ <span class="time-select-label">{{ $t('common.selectTime') }}</span>
|
|
|
+ <ASelect class="time-select-type" v-model:value="selectedType" @change="handleTypeChange">
|
|
|
+ <ASelectOption v-for="item in typeOptions" :key="item.value" :value="item.value">
|
|
|
+ {{ item.label }}
|
|
|
+ </ASelectOption>
|
|
|
+ </ASelect>
|
|
|
+ <ADatePicker
|
|
|
+ v-show="!isLast && !isCustomType"
|
|
|
+ v-model:value="singleDate"
|
|
|
+ :picker="<PickerMode>selectedType"
|
|
|
+ :allow-clear="false"
|
|
|
+ :disabled-date="disabledDate"
|
|
|
+ @change="handleDateChange"
|
|
|
+ >
|
|
|
+ <template #suffixIcon>
|
|
|
+ <SvgIcon name="calendar" color="#333" />
|
|
|
+ </template>
|
|
|
+ </ADatePicker>
|
|
|
+ <ARangePicker
|
|
|
+ class="time-select-range-picker"
|
|
|
+ v-show="isLast || isCustomType"
|
|
|
+ v-model:value="rangeDate"
|
|
|
+ :allow-clear="false"
|
|
|
+ :separator="$t('common.to')"
|
|
|
+ :disabled-date="disabledDate"
|
|
|
+ @change="handleRangeChange"
|
|
|
+ >
|
|
|
+ <template #suffixIcon>
|
|
|
+ <SvgIcon name="calendar" color="#333" />
|
|
|
+ </template>
|
|
|
+ </ARangePicker>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.time-select-container {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.time-select-label {
|
|
|
+ margin-right: 16px;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 22px;
|
|
|
+ color: #000;
|
|
|
+}
|
|
|
+
|
|
|
+.time-select-type {
|
|
|
+ width: 110px;
|
|
|
+ margin-right: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.time-select-range-picker {
|
|
|
+ width: 256px;
|
|
|
+
|
|
|
+ :deep(.ant-picker-range-separator) {
|
|
|
+ padding-right: 16px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|