| | |
| | | start-placeholder="开始日期" |
| | | end-placeholder="结束日期" |
| | | value-format="YYYY-MM-DD" |
| | | :disabled-date="disabledChartDate" |
| | | :clearable="false" |
| | | @change="loadAppointmentStats" |
| | | @calendar-change="handleChartCalendarChange" |
| | | @change="handleChartDateRangeChange" |
| | | /> |
| | | </div> |
| | | </div> |
| | |
| | | }; |
| | | |
| | | const ALL_DEPT_VALUE = "ALL" as const; |
| | | const DAY_MS = 24 * 60 * 60 * 1000; |
| | | const MAX_CHART_RANGE_DAYS = 31; |
| | | |
| | | const deptOptions = ref<DeptTreeOption[]>([]); |
| | | const deptFilter = ref<number | typeof ALL_DEPT_VALUE>(ALL_DEPT_VALUE); |
| | |
| | | let lineChartInstance: ECharts | null = null; |
| | | |
| | | const chartDateRange = ref<[string, string]>(getCurrentWeekRange()); |
| | | const lastValidChartDateRange = ref<[string, string]>([...chartDateRange.value]); |
| | | const chartPickerBaseDate = ref<Date | null>(null); |
| | | |
| | | const overview = ref<DashboardOverview>({ |
| | | startDate: "", |
| | |
| | | return `${year}-${month}-${day}`; |
| | | } |
| | | |
| | | function parseDateOnly(value?: string): Date | null { |
| | | if (!value) { |
| | | return null; |
| | | } |
| | | const parts = value.split("-").map(Number); |
| | | if (parts.length !== 3 || parts.some(Number.isNaN)) { |
| | | return null; |
| | | } |
| | | return new Date(parts[0], parts[1] - 1, parts[2]); |
| | | } |
| | | |
| | | function daysBetween(start?: string, end?: string): number { |
| | | const startDate = parseDateOnly(start); |
| | | const endDate = parseDateOnly(end); |
| | | if (!startDate || !endDate) { |
| | | return 0; |
| | | } |
| | | return Math.floor(Math.abs(endDate.getTime() - startDate.getTime()) / DAY_MS) + 1; |
| | | } |
| | | |
| | | function isChartDateRangeValid(range?: [string, string] | null): range is [string, string] { |
| | | return Array.isArray(range) && range.length === 2 && daysBetween(range[0], range[1]) <= MAX_CHART_RANGE_DAYS; |
| | | } |
| | | |
| | | function disabledChartDate(date: Date) { |
| | | if (!chartPickerBaseDate.value) { |
| | | return false; |
| | | } |
| | | const diffDays = Math.abs(date.getTime() - chartPickerBaseDate.value.getTime()) / DAY_MS; |
| | | return diffDays > MAX_CHART_RANGE_DAYS - 1; |
| | | } |
| | | |
| | | function handleChartCalendarChange(value: Date[]) { |
| | | chartPickerBaseDate.value = value?.[0] || null; |
| | | } |
| | | |
| | | function handleChartDateRangeChange(value: [string, string] | null) { |
| | | chartPickerBaseDate.value = null; |
| | | if (!isChartDateRangeValid(value)) { |
| | | chartDateRange.value = [...lastValidChartDateRange.value]; |
| | | ElMessage.warning(`预约数据最多只能查询${MAX_CHART_RANGE_DAYS}天`); |
| | | return; |
| | | } |
| | | lastValidChartDateRange.value = [...value]; |
| | | loadAppointmentStats(); |
| | | } |
| | | |
| | | function formatNumber(value: number | undefined): string { |
| | | return new Intl.NumberFormat("zh-CN").format(Number(value) || 0); |
| | | } |