migrate date range selection sheet / dialog to composition API and typescript

This commit is contained in:
MaysWind
2025-01-11 15:44:16 +08:00
parent 89b233e51b
commit 3e6a054913
10 changed files with 457 additions and 300 deletions
+5 -4
View File
@@ -15,6 +15,7 @@ import { useI18n } from '@/locales/helpers.ts';
import { useRootStore } from '@/stores/index.js';
import { useSettingsStore } from '@/stores/setting.ts';
import { useEnvironmentsStore } from '@/stores/environment.ts';
import { useUserStore } from '@/stores/user.ts';
import { useTokensStore } from '@/stores/token.ts';
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
@@ -32,6 +33,7 @@ const { tt, getCurrentLanguageInfo, setLanguage, initLocale } = useI18n();
const rootStore = useRootStore();
const settingsStore = useSettingsStore();
const environmentsStore = useEnvironmentsStore();
const userStore = useUserStore();
const tokensStore = useTokensStore();
const exchangeRatesStore = useExchangeRatesStore();
@@ -102,7 +104,6 @@ const f7params = ref<object>({
const notification = ref<Notification.Notification | null>(null);
const isDarkMode = ref<boolean | undefined>(undefined);
const hasPushPopupBackdrop = ref<boolean | undefined>(undefined);
const hasBackdrop = ref<boolean | undefined>(undefined);
const currentNotificationContent = computed<string | null>(() => rootStore.currentNotification);
@@ -145,14 +146,14 @@ function onBackdropChanged(element: { push?: boolean, opened?: boolean }): void
hasBackdrop.value = element.opened;
}
setThemeColorMeta(isDarkMode.value);
setThemeColorMeta(environmentsStore.framework7DarkMode);
}
onMounted(() => {
setAppFontSize(settingsStore.appSettings.fontSize);
f7ready((f7) => {
isDarkMode.value = f7.darkMode;
environmentsStore.framework7DarkMode = f7.darkMode;
setThemeColorMeta(f7.darkMode);
f7.on('actionsOpen', (actions: Actions.Actions) => onBackdropChanged(actions));
@@ -177,7 +178,7 @@ onMounted(() => {
});
f7.on('darkModeChange', (darkMode) => {
isDarkMode.value = darkMode;
environmentsStore.framework7DarkMode = darkMode;
setThemeColorMeta(darkMode);
});
});
@@ -0,0 +1,138 @@
import { ref, computed } from 'vue';
import { type TimeRangeAndDateType, type PresetDateRange, type UnixTimeRange, DateRange } from '@/core/datetime.ts';
import { arrangeArrayWithNewStartIndex } from '@/lib/common.ts';
import {
getCurrentUnixTime,
getCurrentYear,
getUnixTime,
getLocalDatetimeFromUnixTime,
getTodayFirstUnixTime,
getDummyUnixTimeForLocalUsage,
getActualUnixTimeForStore,
getTimezoneOffsetMinutes,
getBrowserTimezoneOffsetMinutes,
getDateRangeByDateType
} from '@/lib/datetime.ts';
import { useI18n } from '@/locales/helpers.ts';
import { useUserStore } from '@/stores/user.ts';
export interface CommonDateRangeSelectionProps {
minTime?: number;
maxTime?: number;
title?: string;
hint?: string;
show: boolean;
}
function getDateRangeFromProps(props: CommonDateRangeSelectionProps): { minDate: number; maxDate: number } {
let minDate = getTodayFirstUnixTime();
let maxDate = getCurrentUnixTime();
if (props.minTime) {
minDate = props.minTime;
}
if (props.maxTime) {
maxDate = props.maxTime;
}
return {
minDate,
maxDate
};
}
export function useDateRangeSelectionBase(props: CommonDateRangeSelectionProps) {
const { tt, getAllMinWeekdayNames, formatUnixTimeToLongDateTime, isLongDateMonthAfterYear, isLongTime24HourFormat } = useI18n();
const userStore = useUserStore();
const { minDate, maxDate } = getDateRangeFromProps(props);
const yearRange = ref<number[]>([
2000,
getCurrentYear() + 1
]);
const dateRange = ref<Date[]>([
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(minDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(maxDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
]);
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
const dayNames = computed<string[]>(() => arrangeArrayWithNewStartIndex(getAllMinWeekdayNames(), firstDayOfWeek.value));
const isYearFirst = computed<boolean>(() => isLongDateMonthAfterYear());
const is24Hour = computed<boolean>(() => isLongTime24HourFormat());
const beginDateTime = computed<string>(() => {
const actualBeginUnixTime = getActualUnixTimeForStore(getUnixTime(dateRange.value[0]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
return formatUnixTimeToLongDateTime(actualBeginUnixTime);
});
const endDateTime = computed<string>(() => {
const actualEndUnixTime = getActualUnixTimeForStore(getUnixTime(dateRange.value[1]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
return formatUnixTimeToLongDateTime(actualEndUnixTime);
});
const presetRanges = computed<PresetDateRange[]>(() => {
const presetRanges:PresetDateRange[] = [];
[
DateRange.Today,
DateRange.LastSevenDays,
DateRange.LastThirtyDays,
DateRange.ThisWeek,
DateRange.ThisMonth,
DateRange.ThisYear
].forEach(dateRangeType => {
const dateRange = getDateRangeByDateType(dateRangeType.type, firstDayOfWeek.value) as TimeRangeAndDateType;
presetRanges.push({
label: tt(dateRangeType.name),
value: [
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
]
});
});
return presetRanges;
});
function getFinalDateRange(): UnixTimeRange | null {
if (!dateRange.value[0] || !dateRange.value[1]) {
return null;
}
const currentMinDate = dateRange.value[0];
const currentMaxDate = dateRange.value[1];
let minUnixTime = getUnixTime(currentMinDate);
let maxUnixTime = getUnixTime(currentMaxDate);
if (minUnixTime < 0 || maxUnixTime < 0) {
throw new Error('Date is too early');
}
minUnixTime = getActualUnixTimeForStore(minUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
maxUnixTime = getActualUnixTimeForStore(maxUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
return {
minUnixTime,
maxUnixTime
};
}
return {
// states
yearRange,
dateRange,
// computed states
dayNames,
isYearFirst,
is24Hour,
beginDateTime,
endDateTime,
presetRanges,
// functions
getFinalDateRange
};
}
@@ -19,7 +19,6 @@
</template>
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
<vue-date-picker inline enable-seconds auto-apply
ref="datetimepicker"
month-name-format="long"
six-weeks="center"
:clearable="false"
@@ -39,186 +38,94 @@
{{ getMonthShortName(text) }}
</template>
<template #am-pm-button="{ toggle, value }">
<button class="dp__pm_am_button" tabindex="0" @click="toggle">{{ $t(`datetime.${value}.content`) }}</button>
<button class="dp__pm_am_button" tabindex="0" @click="toggle">{{ tt(`datetime.${value}.content`) }}</button>
</template>
</vue-date-picker>
</v-card-text>
<v-card-text class="overflow-y-visible">
<div class="w-100 d-flex justify-center gap-4">
<v-btn :disabled="!dateRange[0] || !dateRange[1]" @click="confirm">{{ $t('OK') }}</v-btn>
<v-btn color="secondary" variant="tonal" @click="cancel">{{ $t('Cancel') }}</v-btn>
<v-btn :disabled="!dateRange[0] || !dateRange[1]" @click="confirm">{{ tt('OK') }}</v-btn>
<v-btn color="secondary" variant="tonal" @click="cancel">{{ tt('Cancel') }}</v-btn>
</div>
</v-card-text>
</v-card>
</v-dialog>
</template>
<script>
<script setup lang="ts">
import { computed, watch } from 'vue';
import { useTheme } from 'vuetify';
import { mapStores } from 'pinia';
import { type CommonDateRangeSelectionProps, useDateRangeSelectionBase } from '@/components/base/DateRangeSelectionBase.ts';
import { useI18n } from '@/locales/helpers.ts';
import { useUserStore } from '@/stores/user.ts';
import { DateRange } from '@/core/datetime.ts';
import { ThemeType } from '@/core/theme.ts';
import { arrangeArrayWithNewStartIndex } from '@/lib/common.ts';
import {
getCurrentUnixTime,
getCurrentYear,
getUnixTime,
getLocalDatetimeFromUnixTime,
getTodayFirstUnixTime,
getDummyUnixTimeForLocalUsage,
getActualUnixTimeForStore,
getTimezoneOffsetMinutes,
getBrowserTimezoneOffsetMinutes,
getDateRangeByDateType
getBrowserTimezoneOffsetMinutes
} from '@/lib/datetime.ts';
export default {
props: [
'minTime',
'maxTime',
'title',
'hint',
'persistent',
'show'
],
emits: [
'update:show',
'dateRange:change'
],
data() {
const self = this;
let minDate = getTodayFirstUnixTime();
let maxDate = getCurrentUnixTime();
interface DesktopDateRangeSelectionProps extends CommonDateRangeSelectionProps {
persistent?: boolean;
}
if (self.minTime) {
minDate = self.minTime;
const props = defineProps<DesktopDateRangeSelectionProps>();
const emit = defineEmits<{
(e: 'update:show', value: boolean): void;
(e: 'dateRange:change', minUnixTime: number, maxUnixTime: number): void;
(e: 'error', message: string): void;
}>();
const theme = useTheme();
const { tt, getMonthShortName } = useI18n();
const userStore = useUserStore();
const { yearRange, dateRange, dayNames, isYearFirst, is24Hour, beginDateTime, endDateTime, presetRanges, getFinalDateRange } = useDateRangeSelectionBase(props);
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
const showState = computed<boolean>({
get: () => props.show || false,
set: (value) => emit('update:show', value)
});
function confirm(): void {
try {
const finalDateRange = getFinalDateRange();
if (!finalDateRange) {
return;
}
if (self.maxTime) {
maxDate = self.maxTime;
}
return {
yearRange: [
2000,
getCurrentYear() + 1
],
dateRange: [
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(minDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(maxDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
]
}
},
computed: {
...mapStores(useUserStore),
showState: {
get: function () {
return this.show;
},
set: function (value) {
this.$emit('update:show', value);
}
},
isDarkMode() {
return this.globalTheme.global.name.value === ThemeType.Dark;
},
firstDayOfWeek() {
return this.userStore.currentUserFirstDayOfWeek;
},
dayNames() {
return arrangeArrayWithNewStartIndex(this.$locale.getAllMinWeekdayNames(), this.firstDayOfWeek);
},
isYearFirst() {
return this.$locale.isLongDateMonthAfterYear(this.userStore);
},
is24Hour() {
return this.$locale.isLongTime24HourFormat(this.userStore);
},
beginDateTime() {
const actualBeginUnixTime = getActualUnixTimeForStore(getUnixTime(this.dateRange[0]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, actualBeginUnixTime);
},
endDateTime() {
const actualEndUnixTime = getActualUnixTimeForStore(getUnixTime(this.dateRange[1]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, actualEndUnixTime);
},
presetRanges() {
const presetRanges = [];
[
DateRange.Today,
DateRange.LastSevenDays,
DateRange.LastThirtyDays,
DateRange.ThisWeek,
DateRange.ThisMonth,
DateRange.ThisYear
].forEach(dateRangeType => {
const dateRange = getDateRangeByDateType(dateRangeType.type, this.firstDayOfWeek);
presetRanges.push({
label: this.$t(dateRangeType.name),
value: [
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
]
});
});
return presetRanges;
}
},
watch: {
'minTime': function (newValue) {
if (newValue) {
this.dateRange[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(newValue, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
}
},
'maxTime': function (newValue) {
if (newValue) {
this.dateRange[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(newValue, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
}
}
},
setup() {
const theme = useTheme();
return {
globalTheme: theme
};
},
methods: {
confirm() {
if (!this.dateRange[0] || !this.dateRange[1]) {
return;
}
const currentMinDate = this.dateRange[0];
const currentMaxDate = this.dateRange[1];
let minUnixTime = getUnixTime(currentMinDate);
let maxUnixTime = getUnixTime(currentMaxDate);
if (minUnixTime < 0 || maxUnixTime < 0) {
this.$toast('Date is too early');
return;
}
minUnixTime = getActualUnixTimeForStore(minUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
maxUnixTime = getActualUnixTimeForStore(maxUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
this.$emit('dateRange:change', minUnixTime, maxUnixTime);
},
cancel() {
this.$emit('update:show', false);
},
getMonthShortName(month) {
return this.$locale.getMonthShortName(month);
emit('dateRange:change', finalDateRange.minUnixTime, finalDateRange.maxUnixTime);
} catch (ex: unknown) {
if (ex instanceof Error) {
emit('error', ex.message);
}
}
}
function cancel(): void {
emit('update:show', false);
}
watch(() => props.minTime, (newValue) => {
if (newValue) {
dateRange.value[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(newValue, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
}
});
watch(() => props.maxTime, (newValue) => {
if (newValue) {
dateRange.value[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(newValue, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
}
});
</script>
<style>
+66 -139
View File
@@ -36,170 +36,97 @@
{{ getMonthShortName(text) }}
</template>
<template #am-pm-button="{ toggle, value }">
<button class="dp__pm_am_button" tabindex="0" @click="toggle">{{ $t(`datetime.${value}.content`) }}</button>
<button class="dp__pm_am_button" tabindex="0" @click="toggle">{{ tt(`datetime.${value}.content`) }}</button>
</template>
</vue-date-picker>
<f7-button large fill
:class="{ 'disabled': !dateRange[0] || !dateRange[1] }"
:text="$t('Continue')"
:text="tt('Continue')"
@click="confirm">
</f7-button>
<div class="margin-top text-align-center">
<f7-link @click="cancel" :text="$t('Cancel')"></f7-link>
<f7-link @click="cancel" :text="tt('Cancel')"></f7-link>
</div>
</div>
</f7-page-content>
</f7-sheet>
</template>
<script>
import { mapStores } from 'pinia';
<script setup lang="ts">
import { computed, useTemplateRef } from 'vue';
import VueDatePicker from '@vuepic/vue-datepicker';
import { type CommonDateRangeSelectionProps, useDateRangeSelectionBase } from '@/components/base/DateRangeSelectionBase.ts';
import { useI18n } from '@/locales/helpers.ts';
import { useI18nUIComponents } from '@/lib/ui/mobile.ts';
import { useEnvironmentsStore } from '@/stores/environment.ts';
import { useUserStore } from '@/stores/user.ts';
import { DateRange } from '@/core/datetime.ts';
import { arrangeArrayWithNewStartIndex } from '@/lib/common.ts';
import {
getCurrentUnixTime,
getCurrentYear,
getUnixTime,
getLocalDatetimeFromUnixTime,
getTodayFirstUnixTime,
getDummyUnixTimeForLocalUsage,
getActualUnixTimeForStore,
getTimezoneOffsetMinutes,
getBrowserTimezoneOffsetMinutes,
getDateRangeByDateType
getBrowserTimezoneOffsetMinutes
} from '@/lib/datetime.ts';
export default {
props: [
'minTime',
'maxTime',
'title',
'hint',
'show'
],
emits: [
'update:show',
'dateRange:change'
],
data() {
const self = this;
let minDate = getTodayFirstUnixTime();
let maxDate = getCurrentUnixTime();
type VueDatePickerType = InstanceType<typeof VueDatePicker>;
if (self.minTime) {
minDate = self.minTime;
const props = defineProps<CommonDateRangeSelectionProps>();
const emit = defineEmits<{
(e: 'update:show', value: boolean): void;
(e: 'dateRange:change', minUnixTime: number, maxUnixTime: number): void;
}>();
const { tt, getMonthShortName } = useI18n();
const { showToast } = useI18nUIComponents();
const environmentsStore = useEnvironmentsStore();
const userStore = useUserStore();
const { yearRange, dateRange, dayNames, isYearFirst, is24Hour, beginDateTime, endDateTime, presetRanges, getFinalDateRange } = useDateRangeSelectionBase(props);
const datetimepicker = useTemplateRef<VueDatePickerType>('datetimepicker');
const isDarkMode = computed<boolean>(() => environmentsStore.framework7DarkMode || false);
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
function confirm(): void {
try {
const finalDateRange = getFinalDateRange();
if (!finalDateRange) {
return;
}
if (self.maxTime) {
maxDate = self.maxTime;
}
return {
yearRange: [
2000,
getCurrentYear() + 1
],
dateRange: [
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(minDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(maxDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
]
}
},
computed: {
...mapStores(useUserStore),
isDarkMode() {
return this.$root.isDarkMode;
},
firstDayOfWeek() {
return this.userStore.currentUserFirstDayOfWeek;
},
dayNames() {
return arrangeArrayWithNewStartIndex(this.$locale.getAllMinWeekdayNames(), this.firstDayOfWeek);
},
isYearFirst() {
return this.$locale.isLongDateMonthAfterYear(this.userStore);
},
is24Hour() {
return this.$locale.isLongTime24HourFormat(this.userStore);
},
beginDateTime() {
const actualBeginUnixTime = getActualUnixTimeForStore(getUnixTime(this.dateRange[0]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, actualBeginUnixTime);
},
endDateTime() {
const actualEndUnixTime = getActualUnixTimeForStore(getUnixTime(this.dateRange[1]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, actualEndUnixTime);
},
presetRanges() {
const presetRanges = [];
[
DateRange.Today,
DateRange.LastSevenDays,
DateRange.LastThirtyDays,
DateRange.ThisWeek,
DateRange.ThisMonth,
DateRange.ThisYear
].forEach(dateRangeType => {
const dateRange = getDateRangeByDateType(dateRangeType.type, this.firstDayOfWeek);
presetRanges.push({
label: this.$t(dateRangeType.name),
value: [
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
]
});
});
return presetRanges;
}
},
methods: {
onSheetOpen() {
if (this.minTime) {
this.dateRange[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(this.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
}
if (this.maxTime) {
this.dateRange[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(this.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
}
window.dispatchEvent(new Event('resize')); // fix vue-datepicker preset max-width
this.$refs.datetimepicker.switchView('calendar');
},
onSheetClosed() {
this.$emit('update:show', false);
},
confirm() {
if (!this.dateRange[0] || !this.dateRange[1]) {
return;
}
const currentMinDate = this.dateRange[0];
const currentMaxDate = this.dateRange[1];
let minUnixTime = getUnixTime(currentMinDate);
let maxUnixTime = getUnixTime(currentMaxDate);
if (minUnixTime < 0 || maxUnixTime < 0) {
this.$toast('Date is too early');
return;
}
minUnixTime = getActualUnixTimeForStore(minUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
maxUnixTime = getActualUnixTimeForStore(maxUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
this.$emit('dateRange:change', minUnixTime, maxUnixTime);
},
cancel() {
this.$emit('update:show', false);
},
getMonthShortName(month) {
return this.$locale.getMonthShortName(month);
emit('dateRange:change', finalDateRange.minUnixTime, finalDateRange.maxUnixTime);
} catch (ex: unknown) {
if (ex instanceof Error) {
showToast(ex.message);
}
}
}
function cancel(): void {
emit('update:show', false);
}
function onSheetOpen(): void {
if (props.minTime) {
dateRange.value[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(props.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
}
if (props.maxTime) {
dateRange.value[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(props.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
}
window.dispatchEvent(new Event('resize')); // fix vue-datepicker preset max-width
if (datetimepicker.value) {
datetimepicker.value.switchView('calendar');
}
}
function onSheetClosed(): void {
emit('update:show', false);
}
</script>
+5
View File
@@ -52,6 +52,11 @@ export interface RecentMonthDateRange {
readonly month: number;
}
export interface PresetDateRange {
readonly label: string;
readonly value: Date[];
}
export interface LocalizedRecentMonthDateRange {
readonly dateType: number;
readonly minTime: number;
+157 -1
View File
@@ -1,6 +1,8 @@
import { useI18n as useVueI18n } from 'vue-i18n';
import moment from 'moment-timezone';
import type { TypeAndDisplayName } from '@/core/base.ts';
import { type LanguageInfo, allLanguages, DEFAULT_LANGUAGE } from '@/locales/index.ts';
import {
@@ -23,6 +25,7 @@ import { KnownErrorCode, SPECIFIED_API_NOT_FOUND_ERRORS, PARAMETERIZED_ERRORS }
import {
isString,
isNumber,
isObject
} from '@/lib/common.ts';
@@ -235,8 +238,12 @@ export function useI18n() {
return ret;
}
function getLocalizedDateTimeType<T extends DateFormat | TimeFormat>(allFormatMap: Record<string, T>, allFormatArray: T[], formatTypeValue: number, languageDefaultTypeNameKey: string, systemDefaultFormatType: T): T {
return getDateTimeFormatType(allFormatMap, allFormatArray, formatTypeValue, t(`default.${languageDefaultTypeNameKey}`), systemDefaultFormatType);
}
function getLocalizedDateTimeFormat<T extends DateFormat | TimeFormat>(type: string, allFormatMap: Record<string, T>, allFormatArray: T[], formatTypeValue: number, languageDefaultTypeNameKey: string, systemDefaultFormatType: T): string {
const formatType = getDateTimeFormatType(allFormatMap, allFormatArray, formatTypeValue, t(`default.${languageDefaultTypeNameKey}`), systemDefaultFormatType);
const formatType = getLocalizedDateTimeType(allFormatMap, allFormatArray, formatTypeValue, languageDefaultTypeNameKey, systemDefaultFormatType);
return t(`format.${type}.${formatType.key}`);
}
@@ -306,6 +313,16 @@ export function useI18n() {
return t(finalMessage, parameters);
}
function joinMultiText(textArray: string[]): string {
if (!textArray || !textArray.length) {
return '';
}
const separator = t('format.misc.multiTextJoinSeparator');
return textArray.join(separator);
}
function getCurrentLanguageTag(): string {
return locale.value;
}
@@ -375,6 +392,129 @@ export function useI18n() {
return getAllWeekdayNames('min');
}
function getAllWeekDays(firstDayOfWeek: number): TypeAndDisplayName[] {
const ret: TypeAndDisplayName[] = [];
const allWeekDays = WeekDay.values();
if (!isNumber(firstDayOfWeek)) {
firstDayOfWeek = WeekDay.DefaultFirstDay.type;
}
for (let i = firstDayOfWeek; i < allWeekDays.length; i++) {
const weekDay = allWeekDays[i];
ret.push({
type: weekDay.type,
displayName: t(`datetime.${weekDay.name}.long`)
});
}
for (let i = 0; i < firstDayOfWeek; i++) {
const weekDay = allWeekDays[i];
ret.push({
type: weekDay.type,
displayName: t(`datetime.${weekDay.name}.long`)
});
}
return ret;
}
function getMonthShortName(monthName: string): string {
return t(`datetime.${monthName}.short`);
}
function getMonthLongName(monthName: string): string {
return t(`datetime.${monthName}.long`);
}
function getMonthdayOrdinal(monthDay: number): string {
return t(`datetime.monthDayOrdinal.${monthDay}`);
}
function getMonthdayShortName(monthDay: number): string {
return t('format.misc.monthDay', {
ordinal: getMonthdayOrdinal(monthDay)
});
}
function getWeekdayShortName(weekDayName: string): string {
return t(`datetime.${weekDayName}.short`);
}
function getWeekdayLongName(weekDayName: string): string {
return t(`datetime.${weekDayName}.long`);
}
function getMultiMonthdayShortNames(monthDays: number[]): string {
if (!monthDays) {
return '';
}
if (monthDays.length === 1) {
return t('format.misc.monthDay', {
ordinal: getMonthdayOrdinal(monthDays[0])
});
} else {
return t('format.misc.monthDays', {
multiMonthDays: joinMultiText(monthDays.map(monthDay =>
t('format.misc.eachMonthDayInMonthDays', {
ordinal: getMonthdayOrdinal(monthDay)
})))
});
}
}
function getMultiWeekdayLongNames(weekdayTypes: number[], firstDayOfWeek?: number): string {
const weekdayTypesMap: Record<number, boolean> = {};
if (!isNumber(firstDayOfWeek)) {
firstDayOfWeek = WeekDay.DefaultFirstDay.type;
}
for (let i = 0; i < weekdayTypes.length; i++) {
weekdayTypesMap[weekdayTypes[i]] = true;
}
const allWeekDays = getAllWeekDays(firstDayOfWeek as number);
const finalWeekdayNames = [];
for (let i = 0; i < allWeekDays.length; i++) {
const weekDay = allWeekDays[i];
if (weekdayTypesMap[weekDay.type]) {
finalWeekdayNames.push(weekDay.displayName);
}
}
return joinMultiText(finalWeekdayNames);
}
function isLongDateMonthAfterYear() {
return getLocalizedDateTimeType(LongDateFormat.all(), LongDateFormat.values(), userStore.currentUserLongDateFormat, 'longDateFormat', LongDateFormat.Default).isMonthAfterYear;
}
function isShortDateMonthAfterYear() {
return getLocalizedDateTimeType(ShortDateFormat.all(), ShortDateFormat.values(), userStore.currentUserShortDateFormat, 'shortDateFormat', ShortDateFormat.Default).isMonthAfterYear;
}
function isLongTime24HourFormat() {
return getLocalizedDateTimeType(LongTimeFormat.all(), LongTimeFormat.values(), userStore.currentUserLongTimeFormat, 'longTimeFormat', LongTimeFormat.Default).is24HourFormat;
}
function isLongTimeMeridiemIndicatorFirst() {
return getLocalizedDateTimeType(LongTimeFormat.all(), LongTimeFormat.values(), userStore.currentUserLongTimeFormat, 'longTimeFormat', LongTimeFormat.Default).isMeridiemIndicatorFirst;
}
function isShortTime24HourFormat() {
return getLocalizedDateTimeType(ShortTimeFormat.all(), ShortTimeFormat.values(), userStore.currentUserShortTimeFormat, 'shortTimeFormat', ShortTimeFormat.Default).is24HourFormat;
}
function isShortTimeMeridiemIndicatorFirst() {
return getLocalizedDateTimeType(ShortTimeFormat.all(), ShortTimeFormat.values(), userStore.currentUserShortTimeFormat, 'shortTimeFormat', ShortTimeFormat.Default).isMeridiemIndicatorFirst;
}
function formatYearQuarter(year: number, quarter: number): string {
if (1 <= quarter && quarter <= 4) {
return t('format.yearQuarter.q' + quarter, {
@@ -469,6 +609,7 @@ export function useI18n() {
tt: t,
ti: translateIf,
te: translateError,
joinMultiText,
getCurrentLanguageTag,
getCurrentLanguageInfo,
getCurrentLanguageDisplayName,
@@ -481,6 +622,21 @@ export function useI18n() {
getAllLongWeekdayNames,
getAllShortWeekdayNames,
getAllMinWeekdayNames,
getAllWeekDays,
getMonthShortName,
getMonthLongName,
getMonthdayOrdinal,
getMonthdayShortName,
getWeekdayShortName,
getWeekdayLongName,
getMultiMonthdayShortNames,
getMultiWeekdayLongNames,
isLongDateMonthAfterYear,
isShortDateMonthAfterYear,
isLongTime24HourFormat,
isLongTimeMeridiemIndicatorFirst,
isShortTime24HourFormat,
isShortTimeMeridiemIndicatorFirst,
formatUnixTimeToLongDateTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongDateFormat() + ' ' + getLocalizedLongTimeFormat(), utcOffset, currentUtcOffset),
formatUnixTimeToShortDateTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortDateFormat() + ' ' + getLocalizedShortTimeFormat(), utcOffset, currentUtcOffset),
formatUnixTimeToLongDate: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongDateFormat(), utcOffset, currentUtcOffset),
+11
View File
@@ -0,0 +1,11 @@
import { ref } from 'vue';
import { defineStore } from 'pinia';
export const useEnvironmentsStore = defineStore('environments', () => {
const framework7DarkMode = ref<boolean | undefined>(undefined);
return {
// states
framework7DarkMode
};
});
@@ -287,7 +287,8 @@
:min-time="query.categoricalChartStartTime"
:max-time="query.categoricalChartEndTime"
v-model:show="showCustomDateRangeDialog"
@dateRange:change="setCustomDateFilter" />
@dateRange:change="setCustomDateFilter"
@error="showError" />
<month-range-selection-dialog :title="$t('Custom Date Range')"
:min-time="query.trendChartStartYearMonth"
@@ -1042,6 +1043,9 @@ export default {
clickTrendChartItem(item) {
this.$router.push(this.getTransactionItemLinkUrl(item.itemId, item.dateRange));
},
showError(message) {
this.$refs.snackbar.showError(message);
},
getDisplayAmount(amount, currency, textLimit) {
amount = this.getDisplayCurrency(amount, currency);
+5 -1
View File
@@ -554,7 +554,8 @@
:min-time="customMinDatetime"
:max-time="customMaxDatetime"
v-model:show="showCustomDateRangeDialog"
@dateRange:change="changeCustomDateFilter" />
@dateRange:change="changeCustomDateFilter"
@error="showError" />
<edit-dialog ref="editDialog" type="transaction" :persistent="true" />
<import-dialog ref="importDialog" :persistent="true" />
@@ -1611,6 +1612,9 @@ export default {
scrollToSelectedItem(menu.contentEl, 'div.v-list', 'div.v-list-item.list-item-selected');
});
},
showError(message) {
this.$refs.snackbar.showError(message);
},
getDisplayTime(transaction) {
return this.$locale.formatUnixTimeToShortTime(this.userStore, transaction.time, transaction.utcOffset, this.currentTimezoneOffsetMinutes);
},
@@ -585,7 +585,8 @@
:min-time="filters.minDatetime"
:max-time="filters.maxDatetime"
v-model:show="showCustomDateRangeDialog"
@dateRange:change="changeCustomDateFilter" />
@dateRange:change="changeCustomDateFilter"
@error="showError" />
<batch-replace-dialog ref="batchReplaceDialog" />
<confirm-dialog ref="confirmDialog"/>
<snack-bar ref="snackbar" />
@@ -1568,6 +1569,9 @@ export default {
}
});
},
showError(message) {
this.$refs.snackbar.showError(message);
},
getAllUsedCategoryNames() {
const categoryNames = {};