Skip to content

DateTime Picker

Overview

DateTime Picker is a combination of Date Picker and Time Picker, allowing users to select both date and time simultaneously. It provides flexible configuration options, supports various date-time formats and selection modes, making it an ideal choice for handling complete time information.

Learning Objectives

  • Master the basic concepts and use cases of DateTime Picker
  • Learn how to implement basic date-time selection functionality
  • Understand date-time range selection
  • Master date-time formatting and display
  • Learn how to disable dates/times and limit selections
  • Understand custom date-time pickers
  • Master the application of date-time pickers in real projects
  • Master the complete usage of the API

Basic Usage

Basic Date-Time Selection

The simplest date-time picker:

vue
<template>
  <div>
    <h4>Basic Date-Time Selection</h4>
    <el-date-picker
      v-model="datetime1"
      type="datetime"
      placeholder="Select date and time"
    />
    <p>Selected date and time: {{ datetime1 }}</p>
  </div>
</template>

<script setup>
import { ref, computed, watch } from 'vue'
import dayjs from 'dayjs'

const selectedQuick = ref('today')
const customRange = ref('')
const currentRange = ref([new Date(), new Date()])
const chartType = ref('hourly')
const chartMetric = ref('views')

const quickOptions = [
  {
    key: 'today',
    label: 'Today',
    getValue: () => {
      const start = dayjs().startOf('day').toDate()
      const end = dayjs().endOf('day').toDate()
      return [start, end]
    }
  },
  {
    key: 'yesterday',
    label: 'Yesterday',
    getValue: () => {
      const start = dayjs().subtract(1, 'day').startOf('day').toDate()
      const end = dayjs().subtract(1, 'day').endOf('day').toDate()
      return [start, end]
    }
  },
  {
    key: 'last7days',
    label: 'Last 7 Days',
    getValue: () => {
      const start = dayjs().subtract(6, 'day').startOf('day').toDate()
      const end = dayjs().endOf('day').toDate()
      return [start, end]
    }
  },
  {
    key: 'last30days',
    label: 'Last 30 Days',
    getValue: () => {
      const start = dayjs().subtract(29, 'day').startOf('day').toDate()
      const end = dayjs().endOf('day').toDate()
      return [start, end]
    }
  },
  {
    key: 'thisMonth',
    label: 'This Month',
    getValue: () => {
      const start = dayjs().startOf('month').toDate()
      const end = dayjs().endOf('month').toDate()
      return [start, end]
    }
  },
  {
    key: 'lastMonth',
    label: 'Last Month',
    getValue: () => {
      const start = dayjs().subtract(1, 'month').startOf('month').toDate()
      const end = dayjs().subtract(1, 'month').endOf('month').toDate()
      return [start, end]
    }
  }
]

const rangeShortcuts = [
  {
    text: 'Last 1 hour',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000)
      return [start, end]
    }
  },
  {
    text: 'Last 24 hours',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24)
      return [start, end]
    }
  },
  {
    text: 'Last 7 days',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
      return [start, end]
    }
  }
]

const statistics = computed(() => {
  // Simulated statistics data, in a real application this would be fetched from backend based on currentRange
  return {
    totalViews: 125680,
    viewsChange: 12.5,
    uniqueVisitors: 8432,
    visitorsChange: -3.2,
    avgDuration: 245, // seconds
    durationChange: 8.7,
    conversionRate: 3.45,
    conversionChange: 1.2
  }
})

// Initialize current time range
const initCurrentRange = () => {
  const todayOption = quickOptions.find(opt => opt.key === 'today')
  currentRange.value = todayOption.getValue()
}

const selectQuickOption = (option) => {
  selectedQuick.value = option.key
  currentRange.value = option.getValue()
  customRange.value = ''
}

const handleCustomRangeChange = (value) => {
  if (value && Array.isArray(value)) {
    selectedQuick.value = ''
    currentRange.value = value
  }
}

const formatTimeRange = (range) => {
  if (!range || !Array.isArray(range)) return ''
  
  const [start, end] = range
  const startStr = dayjs(start).format('YYYY-MM-DD HH:mm')
  const endStr = dayjs(end).format('YYYY-MM-DD HH:mm')
  
  return `${startStr} to ${endStr}`
}

const getDataDuration = (range) => {
  if (!range || !Array.isArray(range)) return ''
  
  const [start, end] = range
  const duration = dayjs(end).diff(dayjs(start), 'hour')
  
  if (duration < 24) {
    return `${duration} hours`
  } else {
    const days = Math.floor(duration / 24)
    const hours = duration % 24
    return hours > 0 ? `${days} days ${hours} hours` : `${days} days`
  }
}

const getDataPoints = (range) => {
  if (!range || !Array.isArray(range)) return 0
  
  const [start, end] = range
  const duration = dayjs(end).diff(dayjs(start), 'hour')
  
  // Simulated data point calculation
  return Math.min(duration * 60, 10000) // Assume one data point per minute, max 10000
}

const formatDuration = (seconds) => {
  const minutes = Math.floor(seconds / 60)
  const remainingSeconds = seconds % 60
  
  if (minutes === 0) {
    return `${remainingSeconds} seconds`
  } else {
    return `${minutes}m ${remainingSeconds}s`
  }
}

const updateChart = () => {
  // Chart update logic
  console.log('Updating chart:', chartType.value, chartMetric.value)
}

const getChartTypeLabel = (type) => {
  const labels = {
    hourly: 'Hourly',
    daily: 'Daily',
    weekly: 'Weekly',
    monthly: 'Monthly'
  }
  return labels[type] || type
}

const getMetricLabel = (metric) => {
  const labels = {
    views: 'Views',
    visitors: 'Unique Visitors',
    duration: 'Duration',
    conversion: 'Conversion Rate'
  }
  return labels[metric] || metric
}

// Watch for time range changes to update chart
watch(currentRange, () => {
  updateChart()
}, { deep: true })

// Initialize
initCurrentRange()
</script>

<style scoped>
.data-analytics {
  max-width: 1200px;
  padding: 20px;
}

.time-filter {
  background: #f5f7fa;
  padding: 20px;
  border-radius: 8px;
  margin-bottom: 24px;
}

.time-filter h4 {
  margin: 0 0 16px 0;
  color: #303133;
}

.filter-options {
  margin-bottom: 20px;
}

.quick-filters {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.custom-range {
  display: flex;
  align-items: center;
  gap: 12px;
}

.custom-range label {
  font-weight: 500;
  color: #303133;
}

.time-info {
  display: flex;
  gap: 24px;
  padding: 16px;
  background: white;
  border-radius: 6px;
  border: 1px solid #e4e7ed;
}

.info-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.info-item .label {
  color: #606266;
  font-size: 12px;
}

.info-item .value {
  color: #303133;
  font-weight: 500;
}

.statistics-panel {
  background: white;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
  margin-bottom: 24px;
}

.statistics-panel h4 {
  margin: 0 0 16px 0;
  color: #303133;
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}

.stat-card {
  padding: 20px;
  background: #f8f9fa;
  border-radius: 6px;
  border: 1px solid #e9ecef;
}

.stat-title {
  color: #606266;
  font-size: 14px;
  margin-bottom: 8px;
}

.stat-value {
  color: #303133;
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 4px;
}

.stat-change {
  font-size: 12px;
}

.stat-change.positive {
  color: #67c23a;
}

.stat-change.negative {
  color: #f56c6c;
}

.chart-panel {
  background: white;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
}

.chart-panel h4 {
  margin: 0 0 16px 0;
  color: #303133;
}

.chart-controls {
  display: flex;
  gap: 16px;
  margin-bottom: 20px;
  align-items: center;
}

.chart-container {
  height: 300px;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.chart-placeholder {
  text-align: center;
  color: #909399;
}

.chart-placeholder p {
  margin: 4px 0;
}
</style>

<script setup>
import { ref } from 'vue'

const datetime1 = ref('')
</script>

Date-Time Range Selection

Select a date-time range:

vue
<template>
  <div>
    <h4>Date-Time Range Selection</h4>
    <el-date-picker
      v-model="datetimeRange"
      type="datetimerange"
      range-separator="to"
      start-placeholder="Start date and time"
      end-placeholder="End date and time"
    />
    <p>Selected date-time range: {{ datetimeRange }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetimeRange = ref('')
</script>

Default Time Setting

Set default time for the date picker:

vue
<template>
  <div>
    <h4>Default Time Setting</h4>
    <el-date-picker
      v-model="datetime2"
      type="datetime"
      placeholder="Select date and time"
      :default-time="defaultTime"
    />
    <p>Default time is 12:00:00</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime2 = ref('')
const defaultTime = new Date(2000, 1, 1, 12, 0, 0)
</script>

Date-Time Formatting

Customize date-time display format:

vue
<template>
  <div>
    <h4>Date-Time Formatting</h4>
    <div class="format-demo">
      <div class="format-item">
        <label>Standard format:</label>
        <el-date-picker
          v-model="datetime3"
          type="datetime"
          placeholder="YYYY-MM-DD HH:mm:ss"
          format="YYYY-MM-DD HH:mm:ss"
          value-format="YYYY-MM-DD HH:mm:ss"
        />
      </div>
      
      <div class="format-item">
        <label>Custom format:</label>
        <el-date-picker
          v-model="datetime4"
          type="datetime"
          placeholder="YYYY/MM/DD HH:mm"
          format="YYYY/MM/DD HH:mm"
          value-format="YYYY-MM-DD HH:mm:ss"
        />
      </div>
      
      <div class="format-item">
        <label>12-hour format:</label>
        <el-date-picker
          v-model="datetime5"
          type="datetime"
          placeholder="YYYY/MM/DD hh:mm:ss A"
          format="YYYY/MM/DD hh:mm:ss A"
          value-format="YYYY-MM-DD HH:mm:ss"
        />
      </div>
    </div>
    
    <div class="chart-panel">
      <h4>Trend Chart</h4>
      
      <div class="chart-controls">
        <el-radio-group v-model="chartType" @change="updateChart">
          <el-radio-button label="hourly">Hourly</el-radio-button>
          <el-radio-button label="daily">Daily</el-radio-button>
          <el-radio-button label="weekly">Weekly</el-radio-button>
          <el-radio-button label="monthly">Monthly</el-radio-button>
        </el-radio-group>
        
        <el-select v-model="chartMetric" @change="updateChart">
          <el-option label="Views" value="views" />
          <el-option label="Visitors" value="visitors" />
          <el-option label="Duration" value="duration" />
          <el-option label="Conversion" value="conversion" />
        </el-select>
      </div>
      
      <div class="chart-container">
        <div class="chart-placeholder">
          <p>Chart Area</p>
          <p>Time Range: {{ formatTimeRange(currentRange) }}</p>
          <p>Chart Type: {{ getChartTypeLabel(chartType) }}</p>
          <p>Metric: {{ getMetricLabel(chartMetric) }}</p>
        </div>
      </div>
    </div>
  </div>
</template>
    
    <div class="format-result">
      <p>Standard format: {{ datetime3 }}</p>
      <p>Custom format: {{ datetime4 }}</p>
      <p>12-hour format: {{ datetime5 }}</p>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime3 = ref('')
const datetime4 = ref('')
const datetime5 = ref('')
</script>

<style scoped>
.format-demo {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-bottom: 20px;
}

.format-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.format-item label {
  width: 100px;
  font-weight: 500;
  color: #303133;
}

.format-result {
  padding: 16px;
  background: #f5f7fa;
  border-radius: 6px;
}

.format-result p {
  margin: 4px 0;
  color: #606266;
}
</style>

Advanced Features

Disabling Date-Time

Disable specific dates or times:

vue
<template>
  <div>
    <h4>Disabling Date-Time</h4>
    <div class="disabled-demo">
      <div class="disabled-item">
        <label>Disable past dates:</label>
        <el-date-picker
          v-model="datetime6"
          type="datetime"
          placeholder="Only future times allowed"
          :disabled-date="disabledPastDates"
        />
      </div>
      
      <div class="disabled-item">
        <label>Disable non-working hours:</label>
        <el-date-picker
          v-model="datetime7"
          type="datetime"
          placeholder="Only working hours allowed"
          :disabled-date="disabledWeekends"
          :disabled-hours="disabledHours"
          :disabled-minutes="disabledMinutes"
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime6 = ref('')
const datetime7 = ref('')

const disabledPastDates = (time) => {
  return time.getTime() < Date.now() - 24 * 60 * 60 * 1000
}

const disabledWeekends = (time) => {
  const day = time.getDay()
  return day === 0 || day === 6 // Disable weekends
}

const disabledHours = () => {
  const hours = []
  for (let i = 0; i < 9; i++) {
    hours.push(i)
  }
  for (let i = 18; i < 24; i++) {
    hours.push(i)
  }
  return hours
}

const disabledMinutes = (hour) => {
  // No restrictions on minutes during working hours
  return []
}
</script>

<style scoped>
.disabled-demo {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.disabled-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.disabled-item label {
  width: 150px;
  font-weight: 500;
  color: #303133;
}
</style>

Shortcuts

Provide commonly used date-time shortcuts:

vue
<template>
  <div>
    <h4>Shortcuts</h4>
    <div class="shortcuts-demo">
      <div class="shortcuts-item">
        <label>Single date-time:</label>
        <el-date-picker
          v-model="datetime8"
          type="datetime"
          placeholder="Select date and time"
          :shortcuts="datetimeShortcuts"
        />
      </div>
      
      <div class="shortcuts-item">
        <label>Date-time range:</label>
        <el-date-picker
          v-model="datetimeRange2"
          type="datetimerange"
          range-separator="to"
          start-placeholder="Start time"
          end-placeholder="End time"
          :shortcuts="rangeShortcuts"
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime8 = ref('')
const datetimeRange2 = ref('')

const datetimeShortcuts = [
  {
    text: 'Now',
    value: new Date()
  },
  {
    text: 'Today at 9 AM',
    value: () => {
      const date = new Date()
      date.setHours(9, 0, 0, 0)
      return date
    }
  },
  {
    text: 'Today at 6 PM',
    value: () => {
      const date = new Date()
      date.setHours(18, 0, 0, 0)
      return date
    }
  },
  {
    text: 'Tomorrow at 9 AM',
    value: () => {
      const date = new Date()
      date.setDate(date.getDate() + 1)
      date.setHours(9, 0, 0, 0)
      return date
    }
  }
]

const rangeShortcuts = [
  {
    text: 'Last 1 hour',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000)
      return [start, end]
    }
  },
  {
    text: 'Last 6 hours',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 6)
      return [start, end]
    }
  },
  {
    text: 'Last 24 hours',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24)
      return [start, end]
    }
  },
  {
    text: 'Last 7 days',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
      return [start, end]
    }
  },
  {
    text: 'Last 30 days',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
      return [start, end]
    }
  }
]
</script>

<style scoped>
.shortcuts-demo {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.shortcuts-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.shortcuts-item label {
  width: 150px;
  font-weight: 500;
  color: #303133;
}
</style>

Different Sizes

Provide date-time pickers in different sizes:

vue
<template>
  <div>
    <h4>Different Sizes</h4>
    <div class="size-demo">
      <el-date-picker
        v-model="datetime9"
        type="datetime"
        size="large"
        placeholder="Large size"
      />
      <el-date-picker
        v-model="datetime10"
        type="datetime"
        placeholder="Default size"
      />
      <el-date-picker
        v-model="datetime11"
        type="datetime"
        size="small"
        placeholder="Small size"
      />
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime9 = ref('')
const datetime10 = ref('')
const datetime11 = ref('')
</script>

<style scoped>
.size-demo {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: flex-start;
}
</style>

Practical Application Examples

Event Management System

Create a date-time selection component for event management:

vue
<template>
  <div class="event-management">
    <h3>Event Management System</h3>
    
    <div class="event-form">
      <h4>Create Event</h4>
      
      <div class="form-section">
        <div class="form-item">
          <label>Event Name:</label>
          <el-input v-model="eventForm.name" placeholder="Enter event name" />
        </div>
        
        <div class="form-item">
          <label>Event Type:</label>
          <el-select v-model="eventForm.type" placeholder="Select event type">
            <el-option label="Meeting" value="meeting" />
            <el-option label="Training" value="training" />
            <el-option label="Activity" value="activity" />
            <el-option label="Other" value="other" />
          </el-select>
        </div>
        
        <div class="form-item">
          <label>Event Time:</label>
          <el-date-picker
            v-model="eventForm.timeRange"
            type="datetimerange"
            range-separator="to"
            start-placeholder="Start time"
            end-placeholder="End time"
            :shortcuts="eventShortcuts"
            :disabled-date="disabledEventDates"
            @change="handleTimeChange"
          />
        </div>
        
        <div v-if="eventDuration" class="duration-display">
          <span class="duration-label">Event Duration:</span>
          <span class="duration-value">{{ eventDuration }}</span>
        </div>
        
        <div class="form-item">
          <label>Reminder:</label>
          <el-select v-model="eventForm.reminder" placeholder="Select reminder time">
            <el-option label="No reminder" value="none" />
            <el-option label="15 minutes before" value="15min" />
            <el-option label="30 minutes before" value="30min" />
            <el-option label="1 hour before" value="1hour" />
            <el-option label="1 day before" value="1day" />
          </el-select>
        </div>
        
        <div class="form-item">
          <label>Description:</label>
          <el-input
            v-model="eventForm.description"
            type="textarea"
            :rows="3"
            placeholder="Enter event description"
          />
        </div>
        
        <div class="form-actions">
          <el-button type="primary" @click="createEvent">Create Event</el-button>
          <el-button @click="resetForm">Reset</el-button>
        </div>
      </div>
    </div>
    
    <div class="event-list">
      <h4>Event List</h4>
      
      <div class="filter-bar">
        <el-date-picker
          v-model="filterDate"
          type="daterange"
          range-separator="to"
          start-placeholder="Start date"
          end-placeholder="End date"
          @change="filterEvents"
        />
        <el-select v-model="filterType" placeholder="Event type" @change="filterEvents">
          <el-option label="All" value="" />
          <el-option label="Meeting" value="meeting" />
          <el-option label="Training" value="training" />
          <el-option label="Activity" value="activity" />
          <el-option label="Other" value="other" />
        </el-select>
      </div>
      
      <div class="events">
        <div
          v-for="event in filteredEvents"
          :key="event.id"
          class="event-item"
          :class="getEventStatus(event)"
        >
          <div class="event-header">
            <div class="event-title">{{ event.name }}</div>
            <div class="event-type">
              <el-tag :type="getTypeColor(event.type)" size="small">
                {{ getTypeName(event.type) }}
              </el-tag>
            </div>
          </div>
          
          <div class="event-time">
            <el-icon><Clock /></el-icon>
            <span>{{ formatEventTime(event.timeRange) }}</span>
          </div>
          
          <div v-if="event.description" class="event-description">
            {{ event.description }}
          </div>
          
          <div class="event-actions">
            <el-button size="small" @click="editEvent(event)">Edit</el-button>
            <el-button size="small" type="danger" @click="deleteEvent(event.id)">Delete</el-button>
          </div>
        </div>
        
        <div v-if="filteredEvents.length === 0" class="no-events">
          No events found
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { Clock } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import dayjs from 'dayjs'

const eventForm = ref({
  name: '',
  type: '',
  timeRange: '',
  reminder: 'none',
  description: ''
})

const events = ref([
  {
    id: 1,
    name: 'Project Kickoff Meeting',
    type: 'meeting',
    timeRange: [new Date('2024-01-15 09:00:00'), new Date('2024-01-15 11:00:00')],
    reminder: '30min',
    description: 'Discuss project plan and assignments'
  },
  {
    id: 2,
    name: 'Vue 3 Training',
    type: 'training',
    timeRange: [new Date('2024-01-16 14:00:00'), new Date('2024-01-16 17:00:00')],
    reminder: '1hour',
    description: 'Learning Vue 3 new features'
  }
])

const filterDate = ref('')
const filterType = ref('')

const eventDuration = computed(() => {
  if (!eventForm.value.timeRange || !Array.isArray(eventForm.value.timeRange)) {
    return ''
  }
  
  const [start, end] = eventForm.value.timeRange
  if (!start || !end) return ''
  
  const duration = dayjs(end).diff(dayjs(start), 'minute')
  const hours = Math.floor(duration / 60)
  const minutes = duration % 60
  
  if (hours === 0) {
    return `${minutes} minutes`
  } else if (minutes === 0) {
    return `${hours} hours`
  } else {
    return `${hours} hours ${minutes} minutes`
  }
})

const filteredEvents = computed(() => {
  let result = events.value
  
  if (filterDate.value && Array.isArray(filterDate.value)) {
    const [start, end] = filterDate.value
    result = result.filter(event => {
      const eventStart = dayjs(event.timeRange[0])
      return eventStart.isAfter(dayjs(start)) && eventStart.isBefore(dayjs(end).add(1, 'day'))
    })
  }
  
  if (filterType.value) {
    result = result.filter(event => event.type === filterType.value)
  }
  
  return result.sort((a, b) => new Date(a.timeRange[0]) - new Date(b.timeRange[0]))
})

const eventShortcuts = [
  {
    text: 'Today morning',
    value: () => {
      const start = new Date()
      start.setHours(9, 0, 0, 0)
      const end = new Date()
      end.setHours(12, 0, 0, 0)
      return [start, end]
    }
  },
  {
    text: 'Today afternoon',
    value: () => {
      const start = new Date()
      start.setHours(14, 0, 0, 0)
      const end = new Date()
      end.setHours(18, 0, 0, 0)
      return [start, end]
    }
  },
  {
    text: 'Tomorrow morning',
    value: () => {
      const start = new Date()
      start.setDate(start.getDate() + 1)
      start.setHours(9, 0, 0, 0)
      const end = new Date()
      end.setDate(end.getDate() + 1)
      end.setHours(12, 0, 0, 0)
      return [start, end]
    }
  },
  {
    text: 'Next Monday morning',
    value: () => {
      const today = new Date()
      const nextMonday = new Date(today)
      nextMonday.setDate(today.getDate() + (1 + 7 - today.getDay()) % 7)
      nextMonday.setHours(9, 0, 0, 0)
      
      const end = new Date(nextMonday)
      end.setHours(12, 0, 0, 0)
      
      return [nextMonday, end]
    }
  }
]

const disabledEventDates = (time) => {
  // Disable past dates
  return time.getTime() < Date.now() - 24 * 60 * 60 * 1000
}

const handleTimeChange = (value) => {
  if (value && Array.isArray(value)) {
    const [start, end] = value
    if (dayjs(end).diff(dayjs(start), 'minute') < 30) {
      ElMessage.warning('Event duration cannot be less than 30 minutes')
      eventForm.value.timeRange = ''
    }
  }
}

const createEvent = () => {
  if (!eventForm.value.name) {
    ElMessage.warning('Please enter event name')
    return
  }
  
  if (!eventForm.value.type) {
    ElMessage.warning('Please select event type')
    return
  }
  
  if (!eventForm.value.timeRange) {
    ElMessage.warning('Please select event time')
    return
  }
  
  const newEvent = {
    id: Date.now(),
    ...eventForm.value
  }
  
  events.value.push(newEvent)
  ElMessage.success('Event created successfully')
  resetForm()
}

const resetForm = () => {
  eventForm.value = {
    name: '',
    type: '',
    timeRange: '',
    reminder: 'none',
    description: ''
  }
}

const editEvent = (event) => {
  eventForm.value = { ...event }
}

const deleteEvent = (eventId) => {
  const index = events.value.findIndex(e => e.id === eventId)
  if (index > -1) {
    events.value.splice(index, 1)
    ElMessage.success('Event deleted successfully')
  }
}

const filterEvents = () => {
  // Filtering logic is handled in computed property
}

const getEventStatus = (event) => {
  const now = new Date()
  const start = new Date(event.timeRange[0])
  const end = new Date(event.timeRange[1])
  
  if (now < start) {
    return 'upcoming'
  } else if (now >= start && now <= end) {
    return 'ongoing'
  } else {
    return 'completed'
  }
}

const getTypeColor = (type) => {
  const colors = {
    meeting: 'primary',
    training: 'success',
    activity: 'warning',
    other: 'info'
  }
  return colors[type] || 'info'
}

const getTypeName = (type) => {
  const names = {
    meeting: 'Meeting',
    training: 'Training',
    activity: 'Activity',
    other: 'Other'
  }
  return names[type] || 'Other'
}

const formatEventTime = (timeRange) => {
  if (!timeRange || !Array.isArray(timeRange)) return ''
  
  const [start, end] = timeRange
  const startStr = dayjs(start).format('YYYY-MM-DD HH:mm')
  const endStr = dayjs(end).format('YYYY-MM-DD HH:mm')
  
  if (dayjs(start).format('YYYY-MM-DD') === dayjs(end).format('YYYY-MM-DD')) {
    return `${dayjs(start).format('YYYY-MM-DD')} ${dayjs(start).format('HH:mm')} - ${dayjs(end).format('HH:mm')}`
  } else {
    return `${startStr} - ${endStr}`
  }
}
</script>

<style scoped>
.event-management {
  max-width: 1000px;
  padding: 20px;
}

.event-form {
  background: #f5f7fa;
  padding: 20px;
  border-radius: 8px;
  margin-bottom: 24px;
}

.event-form h4 {
  margin: 0 0 20px 0;
  color: #303133;
}

.form-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.form-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.form-item label {
  width: 100px;
  font-weight: 500;
  color: #303133;
  flex-shrink: 0;
}

.duration-display {
  padding: 12px;
  background: #e1f3d8;
  border-radius: 6px;
  border-left: 4px solid #67c23a;
}

.duration-label {
  color: #529b2e;
  font-weight: 500;
}

.duration-value {
  color: #529b2e;
  font-weight: bold;
}

.form-actions {
  display: flex;
  gap: 12px;
  margin-top: 8px;
}

.event-list {
  background: white;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
}

.event-list h4 {
  margin: 0 0 16px 0;
  color: #303133;
}

.filter-bar {
  display: flex;
  gap: 12px;
  margin-bottom: 20px;
  align-items: center;
}

.events {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.event-item {
  padding: 16px;
  border: 1px solid #e4e7ed;
  border-radius: 6px;
  background: white;
}

.event-item.upcoming {
  border-left: 4px solid #409EFF;
}

.event-item.ongoing {
  border-left: 4px solid #67c23a;
  background: #f0f9ff;
}

.event-item.completed {
  border-left: 4px solid #909399;
  background: #f5f7fa;
}

.event-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.event-title {
  font-weight: 500;
  color: #303133;
  font-size: 16px;
}

.event-time {
  display: flex;
  align-items: center;
  gap: 8px;
  color: #606266;
  margin-bottom: 8px;
}

.event-description {
  color: #909399;
  font-size: 14px;
  margin-bottom: 12px;
}

.event-actions {
  display: flex;
  gap: 8px;
}

.no-events {
  text-align: center;
  color: #909399;
  padding: 40px;
  background: #f5f7fa;
  border-radius: 6px;
}
</style>

Data Analytics Time Filter

Create a time filter component for data analytics:

vue
<template>
  <div class="data-analytics">
    <h3>Data Analytics</h3>
    
    <div class="time-filter">
      <h4>Time Filter</h4>
      
      <div class="filter-options">
        <div class="quick-filters">
          <el-button
            v-for="option in quickOptions"
            :key="option.key"
            :type="selectedQuick === option.key ? 'primary' : 'default'"
            size="small"
            @click="selectQuickOption(option)"
          >
            {{ option.label }}
          </el-button>
        </div>
        
        <div class="custom-range">
          <label>Custom time range:</label>
          <el-date-picker
            v-model="customRange"
            type="datetimerange"
            range-separator="to"
            start-placeholder="Start time"
            end-placeholder="End time"
            :shortcuts="rangeShortcuts"
            @change="handleCustomRangeChange"
          />
        </div>
      </div>
      
      <div class="time-info">
        <div class="info-item">
          <span class="label">Current time range:</span>
          <span class="value">{{ formatTimeRange(currentRange) }}</span>
        </div>
        <div class="info-item">
          <span class="label">Data duration:</span>
          <span class="value">{{ getDataDuration(currentRange) }}</span>
        </div>
        <div class="info-item">
          <span class="label">Data points:</span>
          <span class="value">{{ getDataPoints(currentRange) }}</span>
        </div>
      </div>
    </div>
    
    <div class="statistics-panel">
      <h4>Statistics</h4>
      
      <div class="stats-grid">
        <div class="stat-card">
          <div class="stat-title">Total Views</div>
          <div class="stat-value">{{ statistics.totalViews.toLocaleString() }}</div>
          <div class="stat-change" :class="statistics.viewsChange >= 0 ? 'positive' : 'negative'">
            {{ statistics.viewsChange >= 0 ? '+' : '' }}{{ statistics.viewsChange }}%
          </div>
        </div>
        
        <div class="stat-card">
          <div class="stat-title">Unique Visitors</div>
          <div class="stat-value">{{ statistics.uniqueVisitors.toLocaleString() }}</div>
          <div class="stat-change" :class="statistics.visitorsChange >= 0 ? 'positive' : 'negative'">
            {{ statistics.visitorsChange >= 0 ? '+' : '' }}{{ statistics.visitorsChange }}%
          </div>
        </div>
        
        <div class="stat-card">
          <div class="stat-title">Average Duration</div>
          <div class="stat-value">{{ formatDuration(statistics.avgDuration) }}</div>
          <div class="stat-change" :class="statistics.durationChange >= 0 ? 'positive' : 'negative'">
            {{ statistics.durationChange >= 0 ? '+' : '' }}{{ statistics.durationChange }}%
          </div>
        </div>
        
        <div class="stat-card">
          <div class="stat-title">Conversion Rate</div>
          <div class="stat-value">{{ statistics.conversionRate }}%</div>
          <div class="stat-change" :class="statistics.conversionChange >= 0 ? 'positive' : 'negative'">
            {{ statistics.conversionChange >= 0 ? '+' : '' }}{{ statistics.conversionChange }}%
          </div>
        </div>
      </div>
    </div>
    
    <div class="chart-panel">
      <h4>Trend Chart</h4>
      
      <div class="chart-controls">
        <el-radio-group v-model="chartType" @change="updateChart">
          <el-radio-button label="hourly">Hourly</el-radio-button>
          <el-radio-button label="daily">Daily</el-radio-button>
          <el-radio-button label="weekly">Weekly</el-radio-button>
          <el-radio-button label="monthly">Monthly</el-radio-button>
        </el-radio-group>
        
        <el-select v-model="chartMetric" @change="updateChart">
          <el-option label="Views" value="views" />
          <el-option label="Visitors" value="visitors" />
          <el-option label="Duration" value="duration" />
          <el-option label="Conversion" value="conversion" />
        </el-select>
      </div>
      
      <div class="chart-container">
        <div class="chart-placeholder">
          <p>Chart Area</p>
          <p>Time Range: {{ formatTimeRange(currentRange) }}</p>
          <p>Chart Type: {{ getChartTypeLabel(chartType) }}</p>
          <p>Metric: {{ getMetricLabel(chartMetric) }}</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch } from 'vue'
import dayjs from 'dayjs'

const selectedQuick = ref('today')
const customRange = ref('')
const currentRange = ref([new Date(), new Date()])
const chartType = ref('hourly')
const chartMetric = ref('views')

const quickOptions = [
  {
    key: 'today',
    label: 'Today',
    getValue: () => {
      const start = dayjs().startOf('day').toDate()
      const end = dayjs().endOf('day').toDate()
      return [start, end]
    }
  },
  {
    key: 'yesterday',
    label: 'Yesterday',
    getValue: () => {
      const start = dayjs().subtract(1, 'day').startOf('day').toDate()
      const end = dayjs().subtract(1, 'day').endOf('day').toDate()
      return [start, end]
    }
  },
  {
    key: 'last7days',
    label: 'Last 7 Days',
    getValue: () => {
      const start = dayjs().subtract(6, 'day').startOf('day').toDate()
      const end = dayjs().endOf('day').toDate()
      return [start, end]
    }
  },
  {
    key: 'last30days',
    label: 'Last 30 Days',
    getValue: () => {
      const start = dayjs().subtract(29, 'day').startOf('day').toDate()
      const end = dayjs().endOf('day').toDate()
      return [start, end]
    }
  },
  {
    key: 'thisMonth',
    label: 'This Month',
    getValue: () => {
      const start = dayjs().startOf('month').toDate()
      const end = dayjs().endOf('month').toDate()
      return [start, end]
    }
  },
  {
    key: 'lastMonth',
    label: 'Last Month',
    getValue: () => {
      const start = dayjs().subtract(1, 'month').startOf('month').toDate()
      const end = dayjs().subtract(1, 'month').endOf('month').toDate()
      return [start, end]
    }
  }
]

const rangeShortcuts = [
  {
    text: 'Last 1 hour',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000)
      return [start, end]
    }
  },
  {
    text: 'Last 24 hours',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24)
      return [start, end]
    }
  },
  {
    text: 'Last 7 days',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
      return [start, end]
    }
  }
]

const statistics = computed(() => {
  // Simulated statistics data, in a real application this would be fetched from backend based on currentRange
  return {
    totalViews: 125680,
    viewsChange: 12.5,
    uniqueVisitors: 8432,
    visitorsChange: -3.2,
    avgDuration: 245, // seconds
    durationChange: 8.7,
    conversionRate: 3.45,
    conversionChange: 1.2
  }
})

// Initialize current time range
const initCurrentRange = () => {
  const todayOption = quickOptions.find(opt => opt.key === 'today')
  currentRange.value = todayOption.getValue()
}

const selectQuickOption = (option) => {
  selectedQuick.value = option.key
  currentRange.value = option.getValue()
  customRange.value = ''
}

const handleCustomRangeChange = (value) => {
  if (value && Array.isArray(value)) {
    selectedQuick.value = ''
    currentRange.value = value
  }
}

const formatTimeRange = (range) => {
  if (!range || !Array.isArray(range)) return ''
  
  const [start, end] = range
  const startStr = dayjs(start).format('YYYY-MM-DD HH:mm')
  const endStr = dayjs(end).format('YYYY-MM-DD HH:mm')
  
  return `${startStr} to ${endStr}`
}

const getDataDuration = (range) => {
  if (!range || !Array.isArray(range)) return ''
  
  const [start, end] = range
  const duration = dayjs(end).diff(dayjs(start), 'hour')
  
  if (duration < 24) {
    return `${duration} hours`
  } else {
    const days = Math.floor(duration / 24)
    const hours = duration % 24
    return hours > 0 ? `${days} days ${hours} hours` : `${days} days`
  }
}

const getDataPoints = (range) => {
  if (!range || !Array.isArray(range)) return 0
  
  const [start, end] = range
  const duration = dayjs(end).diff(dayjs(start), 'hour')
  
  // Simulated data point calculation
  return Math.min(duration * 60, 10000) // Assume one data point per minute, max 10000
}

const formatDuration = (seconds) => {
  const minutes = Math.floor(seconds / 60)
  const remainingSeconds = seconds % 60
  
  if (minutes === 0) {
    return `${remainingSeconds} seconds`
  } else {
    return `${minutes}m ${remainingSeconds}s`
  }
}

const updateChart = () => {
  // Chart update logic
  console.log('Updating chart:', chartType.value, chartMetric.value)
}

const getChartTypeLabel = (type) => {
  const labels = {
    hourly: 'Hourly',
    daily: 'Daily',
    weekly: 'Weekly',
    monthly: 'Monthly'
  }
  return labels[type] || type
}

const getMetricLabel = (metric) => {
  const labels = {
    views: 'Views',
    visitors: 'Unique Visitors',
    duration: 'Duration',
    conversion: 'Conversion Rate'
  }
  return labels[metric] || metric
}

// Watch for time range changes to update chart
watch(currentRange, () => {
  updateChart()
}, { deep: true })

// Initialize
initCurrentRange()
</script>

<style scoped>
.data-analytics {
  max-width: 1200px;
  padding: 20px;
}

.time-filter {
  background: #f5f7fa;
  padding: 20px;
  border-radius: 8px;
  margin-bottom: 24px;
}

.time-filter h4 {
  margin: 0 0 16px 0;
  color: #303133;
}

.filter-options {
  margin-bottom: 20px;
}

.quick-filters {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.custom-range {
  display: flex;
  align-items: center;
  gap: 12px;
}

.custom-range label {
  font-weight: 500;
  color: #303133;
}

.time-info {
  display: flex;
  gap: 24px;
  padding: 16px;
  background: white;
  border-radius: 6px;
  border: 1px solid #e4e7ed;
}

.info-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.info-item .label {
  color: #606266;
  font-size: 12px;
}

.info-item .value {
  color: #303133;
  font-weight: 500;
}

.statistics-panel {
  background: white;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
  margin-bottom: 24px;
}

.statistics-panel h4 {
  margin: 0 0 16px 0;
  color: #303133;
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}

.stat-card {
  padding: 20px;
  background: #f8f9fa;
  border-radius: 6px;
  border: 1px solid #e9ecef;
}

.stat-title {
  color: #606266;
  font-size: 14px;
  margin-bottom: 8px;
}

.stat-value {
  color: #303133;
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 4px;
}

.stat-change {
  font-size: 12px;
}

.stat-change.positive {
  color: #67c23a;
}

.stat-change.negative {
  color: #f56c6c;
}

.chart-panel {
  background: white;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
}

.chart-panel h4 {
  margin: 0 0 16px 0;
  color: #303133;
}

.chart-controls {
  display: flex;
  gap: 16px;
  margin-bottom: 20px;
  align-items: center;
}

.chart-container {
  height: 300px;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.chart-placeholder {
  text-align: center;
  color: #909399;
}

.chart-placeholder p {
  margin: 4px 0;
}
</style>

API Documentation

DateTimePicker Attributes

NameDescriptionTypeDefault
model-value / v-modelBinding valueDate / string / Array
readonlyWhether DateTimePicker is read onlybooleanfalse
disabledWhether DateTimePicker is disabledbooleanfalse
editableWhether the input is editablebooleantrue
clearableWhether to show clear buttonbooleantrue
sizeSize of the inputenumdefault
placeholderPlaceholder in non-range modestring
start-placeholderPlaceholder for the start date in range modestring
end-placeholderPlaceholder for the end date in range modestring
typeType of the pickerenumdate
formatFormat of the displayed value in the input boxstringYYYY-MM-DD HH:mm:ss
value-formatFormat of binding valuestring
default-valueOptional, default date of the calendarDate / string
default-timeDefault time value after picking a dateDate / Array
range-separatorRange separatorstring'-'
picker-optionsOptions for the pickerobject{}
shortcutsAn object array to set shortcut optionsArray
disabled-dateA function determining if a date is disabledFunction
disabled-hoursA function determining if hours are disabledFunction
disabled-minutesA function determining if minutes are disabledFunction
disabled-secondsA function determining if seconds are disabledFunction
cell-class-nameSet custom class name for date cellsFunction
teleportedWhether to teleport the picker dropdown to the bodybooleantrue

DateTimePicker Events

NameDescriptionType
changeTriggers when user confirms the valueFunction
blurTriggers when input blursFunction
focusTriggers when input focusesFunction
calendar-changeTriggers when the calendar selected date is changedFunction
panel-changeTriggers when the calendar panel changesFunction
visible-changeTriggers when the dropdown appears/disappearsFunction

DateTimePicker Methods

NameDescriptionType
focusFocus the input elementFunction
blurBlur the input elementFunction

Practice Exercises

Exercise 1: Appointment System

Create a complete appointment system:

vue
<template>
  <div class="appointment-system">
    <h3>Online Appointment System</h3>
    
    <div class="appointment-form">
      <h4>Appointment Information</h4>
      
      <div class="form-grid">
        <div class="form-item">
          <label>Appointment Type:</label>
          <el-select v-model="appointment.type" placeholder="Select appointment type">
            <el-option label="Medical Consultation" value="medical" />
            <el-option label="Legal Consultation" value="legal" />
            <el-option label="Technical Support" value="technical" />
            <el-option label="Business Meeting" value="business" />
          </el-select>
        </div>
        
        <div class="form-item">
          <label>Appointment Time:</label>
          <el-date-picker
            v-model="appointment.datetime"
            type="datetime"
            placeholder="Select appointment time"
            :shortcuts="appointmentShortcuts"
            :disabled-date="disabledAppointmentDates"
            :disabled-hours="disabledAppointmentHours"
            :disabled-minutes="disabledAppointmentMinutes"
          />
        </div>
        
        <div class="form-item">
          <label>Expected Duration:</label>
          <el-select v-model="appointment.duration" placeholder="Select duration">
            <el-option label="30 minutes" :value="30" />
            <el-option label="1 hour" :value="60" />
            <el-option label="1.5 hours" :value="90" />
            <el-option label="2 hours" :value="120" />
          </el-select>
        </div>
        
        <div class="form-item full-width">
          <label>Description:</label>
          <el-input
            v-model="appointment.description"
            type="textarea"
            :rows="3"
            placeholder="Please briefly describe your appointment"
          />
        </div>
      </div>
      
      <div class="form-actions">
        <el-button type="primary" @click="submitAppointment">Submit</el-button>
        <el-button @click="resetAppointment">Reset</el-button>
      </div>
    </div>
    
    <div class="appointment-calendar">
      <h4>Appointment Calendar</h4>
      <div class="calendar-placeholder">
        <p>Calendar View</p>
        <p>Shows already booked time slots</p>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import dayjs from 'dayjs'

const appointment = ref({
  type: '',
  datetime: '',
  duration: 60,
  description: ''
})

const appointmentShortcuts = [
  {
    text: 'Tomorrow at 9 AM',
    value: () => {
      const tomorrow = dayjs().add(1, 'day')
      return tomorrow.hour(9).minute(0).second(0).toDate()
    }
  },
  {
    text: 'Tomorrow at 2 PM',
    value: () => {
      const tomorrow = dayjs().add(1, 'day')
      return tomorrow.hour(14).minute(0).second(0).toDate()
    }
  },
  {
    text: 'Day after tomorrow at 10 AM',
    value: () => {
      const dayAfterTomorrow = dayjs().add(2, 'day')
      return dayAfterTomorrow.hour(10).minute(0).second(0).toDate()
    }
  }
]

const disabledAppointmentDates = (time) => {
  // Disable past dates and weekends
  const day = time.getDay()
  const isPast = time.getTime() < Date.now() - 24 * 60 * 60 * 1000
  const isWeekend = day === 0 || day === 6
  
  return isPast || isWeekend
}

const disabledAppointmentHours = () => {
  // Only allow working hours 9:00-17:00
  const hours = []
  for (let i = 0; i < 9; i++) {
    hours.push(i)
  }
  for (let i = 17; i < 24; i++) {
    hours.push(i)
  }
  return hours
}

const disabledAppointmentMinutes = (hour) => {
  // Only allow on the hour and half hour
  const minutes = []
  for (let i = 1; i < 60; i++) {
    if (i !== 30) {
      minutes.push(i)
    }
  }
  return minutes
}

const submitAppointment = () => {
  if (!appointment.value.type) {
    ElMessage.warning('Please select appointment type')
    return
  }
  
  if (!appointment.value.datetime) {
    ElMessage.warning('Please select appointment time')
    return
  }
  
  ElMessage.success('Appointment submitted successfully, we will contact you shortly to confirm')
  resetAppointment()
}

const resetAppointment = () => {
  appointment.value = {
    type: '',
    datetime: '',
    duration: 60,
    description: ''
  }
}
</script>

<style scoped>
.appointment-system {
  max-width: 800px;
  padding: 20px;
}

.appointment-form {
  background: #f5f7fa;
  padding: 20px;
  border-radius: 8px;
  margin-bottom: 24px;
}

.appointment-form h4 {
  margin: 0 0 20px 0;
  color: #303133;
}

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin-bottom: 20px;
}

.form-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.form-item.full-width {
  grid-column: 1 / -1;
}

.form-item label {
  font-weight: 500;
  color: #303133;
}

.form-actions {
  display: flex;
  gap: 12px;
}

.appointment-calendar {
  background: white;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
}

.appointment-calendar h4 {
  margin: 0 0 16px 0;
  color: #303133;
}

.calendar-placeholder {
  height: 200px;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #909399;
}
</style>

Exercise 2: Log Query System

Create a time filter component for log queries:

vue
<template>
  <div class="log-query-system">
    <h3>System Log Query</h3>
    
    <div class="query-form">
      <h4>Query Criteria</h4>
      
      <div class="query-grid">
        <div class="query-item">
          <label>Log Level:</label>
          <el-select v-model="queryForm.level" placeholder="Select log level" multiple>
            <el-option label="DEBUG" value="debug" />
            <el-option label="INFO" value="info" />
            <el-option label="WARN" value="warn" />
            <el-option label="ERROR" value="error" />
            <el-option label="FATAL" value="fatal" />
          </el-select>
        </div>
        
        <div class="query-item">
          <label>Time Range:</label>
          <el-date-picker
            v-model="queryForm.timeRange"
            type="datetimerange"
            range-separator="to"
            start-placeholder="Start time"
            end-placeholder="End time"
            :shortcuts="logShortcuts"
            @change="handleTimeRangeChange"
          />
        </div>
        
        <div class="query-item">
          <label>Keyword:</label>
          <el-input
            v-model="queryForm.keyword"
            placeholder="Enter keyword to search"
            clearable
          />
        </div>
        
        <div class="query-item">
          <label>Service Module:</label>
          <el-select v-model="queryForm.module" placeholder="Select service module">
            <el-option label="All" value="" />
            <el-option label="User Service" value="user" />
            <el-option label="Order Service" value="order" />
            <el-option label="Payment Service" value="payment" />
            <el-option label="Notification Service" value="notification" />
          </el-select>
        </div>
      </div>
      
      <div class="query-actions">
        <el-button type="primary" @click="searchLogs">Query Logs</el-button>
        <el-button @click="resetQuery">Reset</el-button>
        <el-button @click="exportLogs">Export Logs</el-button>
      </div>
    </div>
    
    <div class="log-results">
      <h4>Query Results</h4>
      
      <div class="result-info">
        <span>Found {{ totalLogs }} logs</span>
        <span v-if="queryForm.timeRange">Time Range: {{ formatQueryTimeRange() }}</span>
      </div>
      
      <div class="log-list">
        <div
          v-for="log in logs"
          :key="log.id"
          class="log-item"
          :class="`level-${log.level}`"
        >
          <div class="log-header">
            <div class="log-time">{{ formatLogTime(log.timestamp) }}</div>
            <div class="log-level">
              <el-tag :type="getLevelColor(log.level)" size="small">
                {{ log.level.toUpperCase() }}
              </el-tag>
            </div>
            <div class="log-module">{{ log.module }}</div>
          </div>
          
          <div class="log-message">{{ log.message }}</div>
          
          <div v-if="log.details" class="log-details">
            <el-collapse>
              <el-collapse-item title="Details">
                <pre>{{ log.details }}</pre>
              </el-collapse-item>
            </el-collapse>
          </div>
        </div>
        
        <div v-if="logs.length === 0" class="no-logs">
          No logs found
        </div>
      </div>
      
      <div class="pagination">
        <el-pagination
          v-model:current-page="currentPage"
          v-model:page-size="pageSize"
          :page-sizes="[10, 20, 50, 100]"
          :total="totalLogs"
          layout="total, sizes, prev, pager, next, jumper"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'
import dayjs from 'dayjs'

const queryForm = ref({
  level: [],
  timeRange: '',
  keyword: '',
  module: ''
})

const currentPage = ref(1)
const pageSize = ref(20)
const totalLogs = ref(156)

const logs = ref([
  {
    id: 1,
    timestamp: new Date('2024-01-15 10:30:25'),
    level: 'info',
    module: 'user',
    message: 'User login successful',
    details: 'User ID: 12345\nIP: 192.168.1.100\nUser-Agent: Mozilla/5.0...'
  },
  {
    id: 2,
    timestamp: new Date('2024-01-15 10:32:15'),
    level: 'warn',
    module: 'order',
    message: 'Order inventory low warning',
    details: 'Product ID: 67890\nRequested: 10\nAvailable: 5'
  },
  {
    id: 3,
    timestamp: new Date('2024-01-15 10:35:42'),
    level: 'error',
    module: 'payment',
    message: 'Payment API call failed',
    details: 'Error Code: 500\nError Message: Internal Server Error\nRequest ID: req_123456'
  }
])

const logShortcuts = [
  {
    text: 'Last 1 hour',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000)
      return [start, end]
    }
  },
  {
    text: 'Last 6 hours',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 6)
      return [start, end]
    }
  },
  {
    text: 'Today',
    value: () => {
      const start = dayjs().startOf('day').toDate()
      const end = dayjs().endOf('day').toDate()
      return [start, end]
    }
  },
  {
    text: 'Yesterday',
    value: () => {
      const start = dayjs().subtract(1, 'day').startOf('day').toDate()
      const end = dayjs().subtract(1, 'day').endOf('day').toDate()
      return [start, end]
    }
  },
  {
    text: 'Last 7 days',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
      return [start, end]
    }
  }
]

const handleTimeRangeChange = (value) => {
  if (value && Array.isArray(value)) {
    const [start, end] = value
    const duration = dayjs(end).diff(dayjs(start), 'day')
    
    if (duration > 30) {
      ElMessage.warning('Query time range cannot exceed 30 days')
      queryForm.value.timeRange = ''
    }
  }
}

const searchLogs = () => {
  if (!queryForm.value.timeRange) {
    ElMessage.warning('Please select a time range')
    return
  }
  
  ElMessage.success('Query completed')
  currentPage.value = 1
}

const resetQuery = () => {
  queryForm.value = {
    level: [],
    timeRange: '',
    keyword: '',
    module: ''
  }
  currentPage.value = 1
}

const exportLogs = () => {
  if (!queryForm.value.timeRange) {
    ElMessage.warning('Please set query criteria first')
    return
  }
  
  ElMessage.success('Log export task submitted, please download later')
}

const formatQueryTimeRange = () => {
  if (!queryForm.value.timeRange || !Array.isArray(queryForm.value.timeRange)) return ''
  
  const [start, end] = queryForm.value.timeRange
  const startStr = dayjs(start).format('YYYY-MM-DD HH:mm')
  const endStr = dayjs(end).format('YYYY-MM-DD HH:mm')
  
  return `${startStr} to ${endStr}`
}

const formatLogTime = (timestamp) => {
  return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
}

const getLevelColor = (level) => {
  const colors = {
    debug: 'info',
    info
# DateTime Picker

## Overview

DateTime Picker is a combination of Date Picker and Time Picker, allowing users to select both date and time simultaneously. It provides flexible configuration options, supports various date-time formats and selection modes, making it an ideal choice for handling complete time information.

## Learning Objectives

- Master the basic concepts and use cases of DateTime Picker
- Learn how to implement basic date-time selection functionality
- Understand date-time range selection
- Master date-time formatting and display
- Learn how to disable dates/times and limit selections
- Understand custom date-time pickers
- Master the application of date-time pickers in real projects
- Master the complete usage of the API

## Basic Usage

### Basic Date-Time Selection

The simplest date-time picker:

```vue
<template>
  <div>
    <h4>Basic Date-Time Selection</h4>
    <el-date-picker
      v-model="datetime1"
      type="datetime"
      placeholder="Select date and time"
    />
    <p>Selected date and time: {{ datetime1 }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime1 = ref('')
</script>

Date-Time Range Selection

Select a date-time range:

vue
<template>
  <div>
    <h4>Date-Time Range Selection</h4>
    <el-date-picker
      v-model="datetimeRange"
      type="datetimerange"
      range-separator="to"
      start-placeholder="Start date and time"
      end-placeholder="End date and time"
    />
    <p>Selected date-time range: {{ datetimeRange }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetimeRange = ref('')
</script>

Default Time Setting

Set default time for the date picker:

vue
<template>
  <div>
    <h4>Default Time Setting</h4>
    <el-date-picker
      v-model="datetime2"
      type="datetime"
      placeholder="Select date and time"
      :default-time="defaultTime"
    />
    <p>Default time is 12:00:00</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime2 = ref('')
const defaultTime = new Date(2000, 1, 1, 12, 0, 0)
</script>

Date-Time Formatting

Customize date-time display format:

vue
<template>
  <div>
    <h4>Date-Time Formatting</h4>
    <div class="format-demo">
      <div class="format-item">
        <label>Standard format:</label>
        <el-date-picker
          v-model="datetime3"
          type="datetime"
          placeholder="YYYY-MM-DD HH:mm:ss"
          format="YYYY-MM-DD HH:mm:ss"
          value-format="YYYY-MM-DD HH:mm:ss"
        />
      </div>
      
      <div class="format-item">
        <label>Custom format:</label>
        <el-date-picker
          v-model="datetime4"
          type="datetime"
          placeholder="YYYY/MM/DD HH:mm"
          format="YYYY/MM/DD HH:mm"
          value-format="YYYY-MM-DD HH:mm:ss"
        />
      </div>
      
      <div class="format-item">
        <label>12-hour format:</label>
        <el-date-picker
          v-model="datetime5"
          type="datetime"
          placeholder="YYYY/MM/DD hh:mm:ss A"
          format="YYYY/MM/DD hh:mm:ss A"
          value-format="YYYY-MM-DD HH:mm:ss"
        />
      </div>
    </div>
    
    <div class="format-result">
      <p>Standard format: {{ datetime3 }}</p>
      <p>Custom format: {{ datetime4 }}</p>
      <p>12-hour format: {{ datetime5 }}</p>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime3 = ref('')
const datetime4 = ref('')
const datetime5 = ref('')
</script>

<style scoped>
.format-demo {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-bottom: 20px;
}

.format-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.format-item label {
  width: 100px;
  font-weight: 500;
  color: #303133;
}

.format-result {
  padding: 16px;
  background: #f5f7fa;
  border-radius: 6px;
}

.format-result p {
  margin: 4px 0;
  color: #606266;
}
</style>

Advanced Features

Disabling Date-Time

Disable specific dates or times:

vue
<template>
  <div>
    <h4>Disabling Date-Time</h4>
    <div class="disabled-demo">
      <div class="disabled-item">
        <label>Disable past dates:</label>
        <el-date-picker
          v-model="datetime6"
          type="datetime"
          placeholder="Only future times allowed"
          :disabled-date="disabledPastDates"
        />
      </div>
      
      <div class="disabled-item">
        <label>Disable non-working hours:</label>
        <el-date-picker
          v-model="datetime7"
          type="datetime"
          placeholder="Only working hours allowed"
          :disabled-date="disabledWeekends"
          :disabled-hours="disabledHours"
          :disabled-minutes="disabledMinutes"
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime6 = ref('')
const datetime7 = ref('')

const disabledPastDates = (time) => {
  return time.getTime() < Date.now() - 24 * 60 * 60 * 1000
}

const disabledWeekends = (time) => {
  const day = time.getDay()
  return day === 0 || day === 6 // Disable weekends
}

const disabledHours = () => {
  const hours = []
  for (let i = 0; i < 9; i++) {
    hours.push(i)
  }
  for (let i = 18; i < 24; i++) {
    hours.push(i)
  }
  return hours
}

const disabledMinutes = (hour) => {
  // No restrictions on minutes during working hours
  return []
}
</script>

<style scoped>
.disabled-demo {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.disabled-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.disabled-item label {
  width: 150px;
  font-weight: 500;
  color: #303133;
}
</style>

Shortcuts

Provide commonly used date-time shortcuts:

vue
<template>
  <div>
    <h4>Shortcuts</h4>
    <div class="shortcuts-demo">
      <div class="shortcuts-item">
        <label>Single date-time:</label>
        <el-date-picker
          v-model="datetime8"
          type="datetime"
          placeholder="Select date and time"
          :shortcuts="datetimeShortcuts"
        />
      </div>
      
      <div class="shortcuts-item">
        <label>Date-time range:</label>
        <el-date-picker
          v-model="datetimeRange2"
          type="datetimerange"
          range-separator="to"
          start-placeholder="Start time"
          end-placeholder="End time"
          :shortcuts="rangeShortcuts"
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime8 = ref('')
const datetimeRange2 = ref('')

const datetimeShortcuts = [
  {
    text: 'Now',
    value: new Date()
  },
  {
    text: 'Today at 9 AM',
    value: () => {
      const date = new Date()
      date.setHours(9, 0, 0, 0)
      return date
    }
  },
  {
    text: 'Today at 6 PM',
    value: () => {
      const date = new Date()
      date.setHours(18, 0, 0, 0)
      return date
    }
  },
  {
    text: 'Tomorrow at 9 AM',
    value: () => {
      const date = new Date()
      date.setDate(date.getDate() + 1)
      date.setHours(9, 0, 0, 0)
      return date
    }
  }
]

const rangeShortcuts = [
  {
    text: 'Last 1 hour',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000)
      return [start, end]
    }
  },
  {
    text: 'Last 6 hours',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 6)
      return [start, end]
    }
  },
  {
    text: 'Last 24 hours',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24)
      return [start, end]
    }
  },
  {
    text: 'Last 7 days',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
      return [start, end]
    }
  },
  {
    text: 'Last 30 days',
    value: () => {
      const end = new Date()
      const start = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
      return [start, end]
    }
  }
]
</script>

<style scoped>
.shortcuts-demo {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.shortcuts-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.shortcuts-item label {
  width: 150px;
  font-weight: 500;
  color: #303133;
}
</style>

Different Sizes

Provide date-time pickers in different sizes:

vue
<template>
  <div>
    <h4>Different Sizes</h4>
    <div class="size-demo">
      <el-date-picker
        v-model="datetime9"
        type="datetime"
        size="large"
        placeholder="Large size"
      />
      <el-date-picker
        v-model="datetime10"
        type="datetime"
        placeholder="Default size"
      />
      <el-date-picker
        v-model="datetime11"
        type="datetime"
        size="small"
        placeholder="Small size"
      />
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const datetime9 = ref('')
const datetime10 = ref('')
const datetime11 = ref('')
</script>

<style scoped>
.size-demo {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: flex-start;
}
</style>

Practical Application Examples

Event Management System

Create a date-time selection component for event management:

vue
<template>
  <div class="event-management">
    <h3>Event Management System</h3>
    
    <div class="event-form">
      <h4>Create Event</h4>
      
      <div class="form-section">
        <div class="form-item">
          <label>Event Name:</label>
          <el-input v-model="eventForm.name" placeholder="Enter event name" />
        </div>
        
        <div class="form-item">
          <label>Event Type:</label>
          <el-select v-model="eventForm.type" placeholder="Select event type">
            <el-option label="Meeting" value="meeting" />
            <el-option label="Training" value="training" />
            <el-option label="Activity" value="activity" />
            <el-option label="Other" value="other" />
          </el-select>
        </div>
        
        <div class="form-item">
          <label>Event Time:</label>
          <el-date-picker
            v-model="eventForm.timeRange"
            type="datetimerange"
            range-separator="to"
            start-placeholder="Start time"
            end-placeholder="End time"
            :shortcuts="eventShortcuts"
            :disabled-date="disabledEventDates"
            @change="handleTimeChange"
          />
        </div>
        
        <div v-if="eventDuration" class="duration-display">
          <span class="duration-label">Event Duration:</span>
          <span class="duration-value">{{ eventDuration }}</span>
        </div>
        
        <div class="form-item">
          <label>Reminder:</label>
          <el-select v-model="eventForm.reminder" placeholder="Select reminder time">
            <el-option label="No reminder" value="none" />
            <el-option label="15 minutes before" value="15min" />
            <el-option label="30 minutes before" value="30min" />
            <el-option label="1 hour before" value="1hour" />
            <el-option label="1 day before" value="1day" />
          </el-select>
        </div>
        
        <div class="form-item">
          <label>Description:</label>
          <el-input
            v-model="eventForm.description"
            type="textarea"
            :rows="3"
            placeholder="Enter event description"
          />
        </div>
        
        <div class="form-actions">
          <el-button type="primary" @click="createEvent">Create Event</el-button>
          <el-button @click="resetForm">Reset</el-button>
        </div>
      </div>
    </div>
    
    <div class="event-list">
      <h4>Event List</h4>
      
      <div class="filter-bar">
        <el-date-picker
          v-model="filterDate"
          type="daterange"
          range-separator="to"
          start-placeholder="Start date"
          end-placeholder="End date"
          @change="filterEvents"
        />
        <el-select v-model="filterType" placeholder="Event type" @change="filterEvents">
          <el-option label="All" value="" />
          <el-option label="Meeting" value="meeting" />
          <el-option label="Training" value="training" />
          <el-option label="Activity" value="activity" />
          <el-option label="Other" value="other" />
        </el-select>
      </div>
      
      <div class="events">
        <div
          v-for="event in filteredEvents"
          :key="event.id"
          class="event-item"
          :class="getEventStatus(event)"
        >
          <div class="event-header">
            <div class="event-title">{{ event.name }}</div>
            <div class="event-type">
              <el-tag :type="getTypeColor(event.type)" size="small">
                {{ getTypeName(event.type) }}
              </el-tag>
            </div>
          </div>
          
          <div class="event-time">
            <el-icon><Clock /></el-icon>
            <span>{{ formatEventTime(event.timeRange) }}</span>
          </div>
          
          <div v-if="event.description" class="event-description">
            {{ event.description }}
          </div>
          
          <div class="event-actions">
            <el-button size="small" @click="editEvent(event)">Edit</el-button>
            <el-button size="small" type="danger" @click="deleteEvent(event.id)">Delete</el-button>
          </div>
        </div>
        
        <div v-if="filteredEvents.length === 0" class="no-events">
          No events found
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { Clock } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import dayjs from 'dayjs'

const eventForm = ref({
  name: '',
  type: '',
  timeRange: '',
  reminder: 'none',
  description: ''
})

const events = ref([
  {
    id: 1,
    name: 'Project Kickoff Meeting',
    type: 'meeting',
    timeRange: [new Date('2024-01-15 09:00:00'), new Date('2024-01-15 11:00:00')],
    reminder: '30min',
    description: 'Discuss project plan and assignments'
  },
  {
    id: 2,
    name: 'Vue 3 Training',
    type: 'training',
    timeRange: [new Date('2024-01-16 14:00:00'), new Date('2024-01-16 17:00:00')],
    reminder: '1hour',
    description: 'Learning Vue 3 new features'
  }
])

const filterDate = ref('')
const filterType = ref('')

const eventDuration = computed(() => {
  if (!eventForm.value.timeRange || !Array.isArray(eventForm.value.timeRange)) {
    return ''
  }
  
  const [start, end] = eventForm.value.timeRange
  if (!start || !end) return ''
  
  const duration = dayjs(end).diff(dayjs(start), 'minute')
  const hours = Math.floor(duration / 60)
  const minutes = duration % 60
  
  if (hours === 0) {
    return `${minutes} minutes`
  } else if (minutes === 0) {
    return `${hours} hours`
  } else {
    return `${hours} hours ${minutes} minutes`
  }
})

const filteredEvents = computed(() => {
  let result = events.value
  
  if (filterDate.value && Array.isArray(filterDate.value)) {
    const [start, end] = filterDate.value
    result = result.filter(event => {
      const eventStart = dayjs(event.timeRange[0])
      return eventStart.isAfter(dayjs(start)) && eventStart.isBefore(dayjs(end).add(1, 'day'))
    })
  }
  
  if (filterType.value) {
    result = result.filter(event => event.type === filterType.value)
  }
  
  return result.sort((a, b) => new Date(a.timeRange[0]) - new Date(b.timeRange[0]))
})

const eventShortcuts = [
  {
    text: 'Today morning',
    value: () => {
      const start = new Date()
      start.setHours(9, 0, 0, 0)
      const end = new Date()
      end.setHours(12, 0, 0, 0)
      return [start, end]
    }
  },
  {
    text: 'Today afternoon',
    value: () => {
      const start = new Date()
      start.setHours(14, 0, 0, 0)
      const end = new Date()
      end.setHours(18, 0, 0, 0)
      return [start, end]
    }
  },
  {
    text: 'Tomorrow morning',
    value: () => {
      const start = new Date()
      start.setDate(start.getDate() + 1)
      start.setHours(9, 0, 0, 0)
      const end = new Date()
      end.setDate(end.getDate() + 1)
      end.setHours(12, 0, 0, 0)
      return [start, end]
    }
  },
  {
    text: 'Next Monday morning',
    value: () => {
      const today = new Date()
      const nextMonday = new Date(today)
      nextMonday.setDate(today.getDate() + (1 + 7 - today.getDay()) % 7)
      nextMonday.setHours(9, 0, 0, 0)
      
      const end = new Date(nextMonday)
      end.setHours(12, 0, 0, 0)
      
      return [nextMonday, end]
    }
  }
]

const disabledEventDates = (time) => {
  // Disable past dates
  return time.getTime() < Date.now() - 24 * 60 * 60 * 1000
}

const handleTimeChange = (value) => {
  if (value && Array.isArray(value)) {
    const [start, end] = value
    if (dayjs(end).diff(dayjs(start), 'minute') < 30) {
      ElMessage.warning('Event duration cannot be less than 30 minutes')
      eventForm.value.timeRange = ''
    }
  }
}

const createEvent = () => {
  if (!eventForm.value.name) {
    ElMessage.warning('Please enter event name')
    return
  }
  
  if (!eventForm.value.type) {
    ElMessage.warning('Please select event type')
    return
  }
  
  if (!eventForm.value.timeRange) {
    ElMessage.warning('Please select event time')
    return
  }
  
  const newEvent = {
    id: Date.now(),
    ...eventForm.value
  }
  
  events.value.push(newEvent)
  ElMessage.success('Event created successfully')
  resetForm()
}

const resetForm = () => {
  eventForm.value = {
    name: '',
    type: '',
    timeRange: '',
    reminder: 'none',
    description: ''
  }
}

const editEvent = (event) => {
  eventForm.value = { ...event }
}

const deleteEvent = (eventId) => {
  const index = events.value.findIndex(e => e.id === eventId)
  if (index > -1) {
    events.value.splice(index, 1)
    ElMessage.success('Event deleted successfully')
  }
}

const filterEvents = () => {
  // Filtering logic is handled in computed property
}

const getEventStatus = (event) => {
  const now = new Date()
  const start = new Date(event.timeRange[0])
  const end = new Date(event.timeRange[1])
  
  if (now < start) {
    return 'upcoming'
  } else if (now >= start && now <= end) {
    return 'ongoing'
  } else {
    return 'completed'
  }
}

const getTypeColor = (type) => {
  const colors = {
    meeting: 'primary',
    training: 'success',
    activity: 'warning',
    other: 'info'
  }
  return colors[type] || 'info'
}

const getTypeName = (type) => {
  const names = {
    meeting: 'Meeting',
    training: 'Training',
    activity: 'Activity',
    other: 'Other'
  }
  return names[type] || 'Other'
}

const formatEventTime = (timeRange) => {
  if (!timeRange || !Array.isArray(timeRange)) return ''
  
  const [start, end] = timeRange
  const startStr = dayjs(start).format('YYYY-MM-DD HH:mm')
  const endStr = dayjs(end).format('YYYY-MM-DD HH:mm')
  
  if (dayjs(start).format('YYYY-MM-DD') === dayjs(end).format('YYYY-MM-DD')) {
    return `${dayjs(start).format('YYYY-MM-DD')} ${dayjs(start).format('HH:mm')} - ${dayjs(end).format('HH:mm')}`
  } else {
    return `${startStr} - ${endStr}`
  }
}
</script>

<style scoped>
.event-management {
  max-width: 1000px;
  padding: 20px;
}

.event-form {
  background: #f5f7fa;
  padding: 20px;
  border-radius: 8px;
  margin-bottom: 24px;
}

.event-form h4 {
  margin: 0 0 20px 0;
  color: #303133;
}

.form-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.form-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.form-item label {
  width: 100px;
  font-weight: 500;
  color: #303133;
  flex-shrink: 0;
}

.duration-display {
  padding: 12px;
  background: #e1f3d8;
  border-radius: 6px;
  border-left: 4px solid #67c23a;
}

.duration-label {
  color: #529b2e;
  font-weight: 500;
}

.duration-value {
  color: #529b2e;
  font-weight: bold;
}

.form-actions {
  display: flex;
  gap: 12px;
  margin-top: 8px;
}

.event-list {
  background: white;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
}

.event-list h4 {
  margin: 0 0 16px 0;
  color: #303133;
}

.filter-bar {
  display: flex;
  gap: 12px;
  margin-bottom: 20px;
  align-items: center;
}

.events {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.event-item {
  padding: 16px;
  border: 1px solid #e4e7ed;
  border-radius: 6px;
  background: white;
}

.event-item.upcoming {
  border-left: 4px solid #409EFF;
}

.event-item.ongoing {
  border-left: 4px solid #67c23a;
  background: #f0f9ff;
}

.event-item.completed {
  border-left: 4px solid #909399;
  background: #f5f7fa;
}

.event-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.event-title {
  font-weight: 500;
  color: #303133;
  font-size: 16px;
}

.event-time {
  display: flex;
  align-items: center;
  gap: 8px;
  color: #606266;
  margin-bottom: 8px;
}

.event-description {
  color: #909399;
  font-size: 14px;
  margin-bottom: 12px;
}

.event-actions {
  display: flex;
  gap: 8px;
}

.no-events {
  text-align: center;
  color: #909399;
  padding: 40px;
  background: #f5f7fa;
  border-radius: 6px;
}
</style>

Data Analytics Time Filter

Create a time filter component for data analytics:

vue
<template>
  <div class="data-analytics">
    <h3>Data Analytics</h3>
    
    <div class="time-filter">
      <h4>Time Filter</h4>
      
      <div class="filter-options">
        <div class="quick-filters">
          <el-button
            v-for="option in quickOptions"
            :key="option.key"
            :type="selectedQuick === option.key ? 'primary' : 'default'"
            size="small"
            @click="selectQuickOption(option)"
          >
            {{ option.label }}
          </el-button>
        </div>
        
        <div class="custom-range">
          <label>Custom time range:</label>
          <el-date-picker
            v-model="customRange"
            type="datetimerange"
            range-separator="to"
            start-placeholder="Start time"
            end-placeholder="End time"
            :shortcuts="rangeShortcuts"
            @change="handleCustomRangeChange"
          />
        </div>
      </div>
      
      <div class="time-info">
        <div class="info-item">
          <span class="label">Current time range:</span>
          <span class="value">{{ formatTimeRange(currentRange) }}</span>
        </div>
        <div class="info-item">
          <span class="label">Data duration:</span>
          <span class="value">{{ getDataDuration(currentRange) }}</span>
        </div>
        <div class="info-item">
          <span class="label">Data points:</span>
          <span class="value">{{ getDataPoints(currentRange) }}</span>
        </div>
      </div>
    </div>
    
    <div class="statistics-panel">
      <h4>Statistics</h4>
      
      <div class="stats-grid">
        <div class="stat-card">
          <div class="stat-title">Total Views</div>
          <div class="stat-value">{{ statistics.totalViews.toLocaleString() }}</div>
          <div class="stat-change" :class="statistics.viewsChange >= 0 ? 'positive' : 'negative'">
            {{ statistics.viewsChange >= 0 ? '+' : '' }}{{ statistics.viewsChange }}%
          </div>
        </div>
        
        <div class="stat-card">
          <div class="stat-title">Unique Visitors</div>
          <div class="stat-value">{{ statistics.uniqueVisitors.toLocaleString() }}</div>
          <div class="stat-change" :class="statistics.visitorsChange >= 0 ? 'positive' : 'negative'">
            {{ statistics.visitorsChange >= 0 ? '+' : '' }}{{ statistics.visitorsChange }}%
          </div>
        </div>
        
        <div class="stat-card">
          <div class="stat-title">Average Duration</div>
          <div class="stat-value">{{ formatDuration(statistics.avgDuration) }}</div>
          <div class="stat-change" :class="statistics.durationChange >= 0 ? 'positive' : 'negative'">
            {{ statistics.durationChange >= 0 ? '+' : '' }}{{ statistics.durationChange }}%
          </div>
        </div>
        
        <div class="stat-card">
          <div class="stat-title">Conversion Rate</div>
          <div class="stat-value">{{ statistics.conversionRate }}%</div>
          <div class="stat-change" :class="statistics.conversionChange >= 0 ? 'positive' : 'negative'">
            {{ statistics.conversionChange >= 0 ? '+' : '' }}{{ statistics.conversionChange }}%
          </div>
        </div>
      </div>
    </div>

Element Plus Study Guide