add date util

This commit is contained in:
Timi
2026-09-15 13:41:33 +08:00
parent 151427a675
commit b661101a4a
8 changed files with 132 additions and 355 deletions
+3 -49
View File
@@ -168,6 +168,7 @@ import { useSettingStore } from "@/store/settingStore";
import type { Customer } from "@/types/Customer"; import type { Customer } from "@/types/Customer";
import type { NavBarAction } from "@/types/NavBarAction"; import type { NavBarAction } from "@/types/NavBarAction";
import type { Tag } from "@/types/Tag"; import type { Tag } from "@/types/Tag";
import { buildPresetDateRange, parseDateTime } from "@/utils/date";
import { corePermissionCodes, gaoRoleCodes, permissionCodes } from "@/utils/permission"; import { corePermissionCodes, gaoRoleCodes, permissionCodes } from "@/utils/permission";
defineOptions({ defineOptions({
@@ -206,7 +207,7 @@ const dateOptions: Array<{ label: string; value: DateFilterType }> = [
{ label: "自定义范围", value: "custom" } { label: "自定义范围", value: "custom" }
]; ];
const dateFilterType = ref<DateFilterType>("all"); const dateFilterType = ref<DateFilterType>("all");
const dateRange = ref<[string, string]>(buildPresetRange("all")); const dateRange = ref<[string, string]>(buildPresetDateRange("all"));
const startDraft = ref(""); const startDraft = ref("");
const endDraft = ref(""); const endDraft = ref("");
const startVisible = ref(false); const startVisible = ref(false);
@@ -279,7 +280,7 @@ async function selectDateFilter(value: DropdownValue): Promise<void> {
dateFilterType.value = String(value) as DateFilterType; dateFilterType.value = String(value) as DateFilterType;
if (dateFilterType.value !== "custom") { if (dateFilterType.value !== "custom") {
dateRange.value = buildPresetRange(dateFilterType.value); dateRange.value = buildPresetDateRange(dateFilterType.value);
await refresh(); await refresh();
} }
} }
@@ -499,53 +500,6 @@ function buildCreatedAtRange(): [number | undefined, number | undefined] {
]; ];
} }
function buildPresetRange(type: Exclude<DateFilterType, "custom">): [string, string] {
if (type === "all") {
return ["", ""];
}
if (type === "lastWeek") {
const current = new Date();
current.setHours(0, 0, 0, 0);
const day = current.getDay() || 7;
const thisWeekMonday = new Date(current);
thisWeekMonday.setDate(current.getDate() - day + 1);
const lastWeekMonday = new Date(thisWeekMonday);
lastWeekMonday.setDate(thisWeekMonday.getDate() - 7);
const lastWeekSunday = new Date(thisWeekMonday);
lastWeekSunday.setDate(thisWeekMonday.getDate() - 1);
return [`${Time.formatDate(lastWeekMonday)} 00:00`, `${Time.formatDate(lastWeekSunday)} 23:59`];
}
if (type === "thisMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth(), 1);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(current)} 23:59`];
}
if (type === "lastMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth() - 1, 1);
const end = new Date(current.getFullYear(), current.getMonth(), 0);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(end)} 23:59`];
}
const offsetMap: Record<Exclude<DateFilterType, "all" | "custom" | "lastWeek" | "thisMonth" | "lastMonth">, number> = {
today: 0,
yesterday: 1
};
const date = new Date();
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() - offsetMap[type]);
return [`${Time.formatDate(date)} 00:00`, `${Time.formatDate(date)} 23:59`];
}
function parseDateTime(value: string, isEnd: boolean): Date {
if (!value) {
return isEnd ? new Date() : new Date(0);
}
const [dateText, timeText = isEnd ? "23:59" : "00:00"] = value.split(" ");
const [year, month, day] = dateText.split("-").map(Number);
const [hour, minute] = timeText.split(":").map(Number);
return new Date(year, month - 1, day, hour || 0, minute || 0, isEnd ? 59 : 0, 0);
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
+4 -57
View File
@@ -148,6 +148,7 @@ import type { EventOptionStat, EventRecordPage, EventRecordPageResult, Verificat
import type { NavBarAction } from "@/types/NavBarAction"; import type { NavBarAction } from "@/types/NavBarAction";
import { isRetiredEvent } from "@/utils/event"; import { isRetiredEvent } from "@/utils/event";
import { formatEventNumberValue } from "@/utils/eventValue"; import { formatEventNumberValue } from "@/utils/eventValue";
import { buildPresetDateRange, parseDateFilterType, parseDateTime } from "@/utils/date";
import { permissionCodes } from "@/utils/permission"; import { permissionCodes } from "@/utils/permission";
import { Message } from "@/utils/message.ts"; import { Message } from "@/utils/message.ts";
@@ -178,7 +179,7 @@ const dateOptions: Array<{ label: string; value: DateFilterType }> = [
const keyword = ref(getInitialKeyword()); const keyword = ref(getInitialKeyword());
const customerId = ref(getQueryText("customerId")); const customerId = ref(getQueryText("customerId"));
const customerSelectorVisible = ref(false); const customerSelectorVisible = ref(false);
const dateFilterType = ref<DateFilterType>(parseDateFilterType(route.query.dateType)); const dateFilterType = ref<DateFilterType>(parseDateFilterType(route.query.dateType, dateOptions, "today"));
const dateRange = ref(parseDateRange(dateFilterType.value)); const dateRange = ref(parseDateRange(dateFilterType.value));
const startDraft = ref(""); const startDraft = ref("");
const endDraft = ref(""); const endDraft = ref("");
@@ -471,7 +472,7 @@ async function selectDateFilter(type: DropdownValue): Promise<void> {
dateFilterType.value = String(type) as DateFilterType; dateFilterType.value = String(type) as DateFilterType;
if (dateFilterType.value !== "custom") { if (dateFilterType.value !== "custom") {
dateRange.value = buildPresetRange(dateFilterType.value); dateRange.value = buildPresetDateRange(dateFilterType.value);
await replaceFilterQuery(); await replaceFilterQuery();
await reloadRecords(); await reloadRecords();
} else { } else {
@@ -586,14 +587,9 @@ function getQueryText(key: string): string {
return Array.isArray(value) ? String(value[0] || "") : String(value || ""); return Array.isArray(value) ? String(value[0] || "") : String(value || "");
} }
function parseDateFilterType(value: unknown): DateFilterType {
const text = Array.isArray(value) ? String(value[0] || "") : String(value || "");
return dateOptions.some(item => item.value === text) ? text as DateFilterType : "today";
}
function parseDateRange(type: DateFilterType): [string, string] { function parseDateRange(type: DateFilterType): [string, string] {
if (type !== "custom") { if (type !== "custom") {
return buildPresetRange(type); return buildPresetDateRange(type);
} }
const beginAt = getQueryText("beginAt"); const beginAt = getQueryText("beginAt");
@@ -657,45 +653,6 @@ async function replaceFilterQuery(): Promise<void> {
}); });
} }
function buildPresetRange(type: Exclude<DateFilterType, "custom">): [string, string] {
if (type === "all") {
return ["", ""];
}
if (type === "lastWeek") {
const current = new Date();
current.setHours(0, 0, 0, 0);
const day = current.getDay() || 7;
const thisWeekMonday = new Date(current);
thisWeekMonday.setDate(current.getDate() - day + 1);
const lastWeekMonday = new Date(thisWeekMonday);
lastWeekMonday.setDate(thisWeekMonday.getDate() - 7);
const lastWeekSunday = new Date(thisWeekMonday);
lastWeekSunday.setDate(thisWeekMonday.getDate() - 1);
return [`${Time.formatDate(lastWeekMonday)} 00:00`, `${Time.formatDate(lastWeekSunday)} 23:59`];
}
if (type === "thisMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth(), 1);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(current)} 23:59`];
}
if (type === "lastMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth() - 1, 1);
const end = new Date(current.getFullYear(), current.getMonth(), 0);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(end)} 23:59`];
}
const offsetMap: Record<Exclude<DateFilterType, "all" | "custom" | "lastWeek" | "thisMonth" | "lastMonth">, number> = {
today: 0,
yesterday: 1
};
const date = new Date();
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() - offsetMap[type]);
const value = `${Time.formatDate(date)} 00:00`;
return [value, `${Time.formatDate(date)} 23:59`];
}
function buildUnixRange(): [number, number] { function buildUnixRange(): [number, number] {
if (dateFilterType.value === "all") { if (dateFilterType.value === "all") {
return [0, Time.toTimestamp(new Date())]; return [0, Time.toTimestamp(new Date())];
@@ -707,16 +664,6 @@ function buildUnixRange(): [number, number] {
]; ];
} }
function parseDateTime(value: string, isEnd: boolean): Date {
if (!value) {
return isEnd ? new Date() : new Date(0);
}
const [dateText, timeText = isEnd ? "23:59" : "00:00"] = value.split(" ");
const [year, month, day] = dateText.split("-").map(Number);
const [hour, minute] = timeText.split(":").map(Number);
return new Date(year, month - 1, day, hour || 0, minute || 0, isEnd ? 59 : 0, 0);
}
onMounted(async () => { onMounted(async () => {
navBarStore.clearRight(); navBarStore.clearRight();
navBarStore.setCustomRightVisible(canShowActionMenu.value); navBarStore.setCustomRightVisible(canShowActionMenu.value);
+5 -51
View File
@@ -132,6 +132,7 @@ import EventAPI from "@/api/EventAPI";
import EventRecordAPI from "@/api/EventRecordAPI"; import EventRecordAPI from "@/api/EventRecordAPI";
import type { CustomerEventRankView, EventRecordRankPage, EventRecordRankType } from "@/types/EventRecord"; import type { CustomerEventRankView, EventRecordRankPage, EventRecordRankType } from "@/types/EventRecord";
import type { EventItem } from "@/types/EventItem"; import type { EventItem } from "@/types/EventItem";
import { buildPresetDateRange, parseDateFilterType, parseDateTime } from "@/utils/date";
import { getErrorMessage } from "@/utils/error"; import { getErrorMessage } from "@/utils/error";
import { formatEventNumberValue } from "@/utils/eventValue"; import { formatEventNumberValue } from "@/utils/eventValue";
import { permissionCodes } from "@/utils/permission"; import { permissionCodes } from "@/utils/permission";
@@ -164,7 +165,7 @@ const dateOptions: Array<{ label: string; value: DateFilterType }> = [
// ---------- 筛选 ---------- // ---------- 筛选 ----------
const rankType = ref<EventRecordRankType>(parseRankType(route.query.rankType)); const rankType = ref<EventRecordRankType>(parseRankType(route.query.rankType));
const dateFilterType = ref<DateFilterType>(parseDateFilterType(route.query.dateType)); const dateFilterType = ref<DateFilterType>(parseDateFilterType(route.query.dateType, dateOptions, "all"));
const dateRange = ref(parseDateRange(dateFilterType.value)); const dateRange = ref(parseDateRange(dateFilterType.value));
const eventFilter = ref<string[]>([]); const eventFilter = ref<string[]>([]);
const eventList = ref<EventItem[]>([]); const eventList = ref<EventItem[]>([]);
@@ -341,9 +342,9 @@ async function selectDateFilter(value: DropdownValue): Promise<void> {
return; return;
} }
dateFilterType.value = parseDateFilterType(value); dateFilterType.value = parseDateFilterType(value, dateOptions, "all");
if (dateFilterType.value !== "custom") { if (dateFilterType.value !== "custom") {
dateRange.value = buildPresetRange(dateFilterType.value); dateRange.value = buildPresetDateRange(dateFilterType.value);
await replaceFilterQuery(); await replaceFilterQuery();
await reloadRanks(); await reloadRanks();
return; return;
@@ -388,14 +389,9 @@ function parseRankType(value: unknown): EventRecordRankType {
return rankTypeOptions.some(item => item.value === text) ? text as EventRecordRankType : "TOTAL_DAYS"; return rankTypeOptions.some(item => item.value === text) ? text as EventRecordRankType : "TOTAL_DAYS";
} }
function parseDateFilterType(value: unknown): DateFilterType {
const text = Array.isArray(value) ? String(value[0] || "") : String(value || "");
return dateOptions.some(item => item.value === text) ? text as DateFilterType : "all";
}
function parseDateRange(type: DateFilterType): [string, string] { function parseDateRange(type: DateFilterType): [string, string] {
if (type !== "custom") { if (type !== "custom") {
return buildPresetRange(type); return buildPresetDateRange(type);
} }
return [getQueryText("beginAt"), getQueryText("endAt")]; return [getQueryText("beginAt"), getQueryText("endAt")];
@@ -436,38 +432,6 @@ async function replaceFilterQuery(): Promise<void> {
}); });
} }
function buildPresetRange(type: Exclude<DateFilterType, "custom">): [string, string] {
if (type === "all") {
return ["", ""];
}
if (type === "lastWeek") {
const current = new Date();
current.setHours(0, 0, 0, 0);
const day = current.getDay() || 7;
const thisWeekMonday = new Date(current);
thisWeekMonday.setDate(current.getDate() - day + 1);
const lastWeekMonday = new Date(thisWeekMonday);
lastWeekMonday.setDate(thisWeekMonday.getDate() - 7);
const lastWeekSunday = new Date(thisWeekMonday);
lastWeekSunday.setDate(thisWeekMonday.getDate() - 1);
return [`${Time.formatDate(lastWeekMonday)} 00:00`, `${Time.formatDate(lastWeekSunday)} 23:59`];
}
if (type === "thisMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth(), 1);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(current)} 23:59`];
}
const offsetMap: Record<Exclude<DateFilterType, "all" | "custom" | "lastWeek" | "thisMonth">, number> = {
today: 0,
yesterday: 1
};
const date = new Date();
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() - offsetMap[type]);
return [`${Time.formatDate(date)} 00:00`, `${Time.formatDate(date)} 23:59`];
}
function buildUnixRange(): [number | undefined, number | undefined] { function buildUnixRange(): [number | undefined, number | undefined] {
if (dateFilterType.value === "all") { if (dateFilterType.value === "all") {
return [undefined, undefined]; return [undefined, undefined];
@@ -478,16 +442,6 @@ function buildUnixRange(): [number | undefined, number | undefined] {
]; ];
} }
function parseDateTime(value: string, isEnd: boolean): Date {
if (!value) {
return isEnd ? new Date() : new Date(0);
}
const [dateText, timeText = isEnd ? "23:59" : "00:00"] = value.split(" ");
const [year, month, day] = dateText.split("-").map(Number);
const [hour, minute] = timeText.split(":").map(Number);
return new Date(year, month - 1, day, hour || 0, minute || 0, isEnd ? 59 : 0, 0);
}
onMounted(async () => { onMounted(async () => {
await loadEvents(); await loadEvents();
initialized = true; initialized = true;
+4 -57
View File
@@ -142,6 +142,7 @@ import type { GaoFulfillmentStatus, OrderFulfillment } from "@/types/Fulfillment
import type { NavBarAction } from "@/types/NavBarAction"; import type { NavBarAction } from "@/types/NavBarAction";
import type { Customer } from "@/types/Customer"; import type { Customer } from "@/types/Customer";
import type { SalesOrder } from "@/types/SalesOrder"; import type { SalesOrder } from "@/types/SalesOrder";
import { buildPresetDateRange, parseDateFilterType, parseDateTime } from "@/utils/date";
import { getErrorMessage } from "@/utils/error"; import { getErrorMessage } from "@/utils/error";
import { Message } from "@/utils/message"; import { Message } from "@/utils/message";
import { permissionCodes } from "@/utils/permission"; import { permissionCodes } from "@/utils/permission";
@@ -186,7 +187,7 @@ const selectedCustomer = ref<Customer>();
const salesOrder = ref<SalesOrder>(); const salesOrder = ref<SalesOrder>();
const customerSelectorVisible = ref(false); const customerSelectorVisible = ref(false);
const customerSearchKeyword = ref(""); const customerSearchKeyword = ref("");
const dateFilterType = ref<DateFilterType>(parseDateFilterType(route.query.dateType)); const dateFilterType = ref<DateFilterType>(parseDateFilterType(getQueryText(route.query.dateType), dateOptions, "today"));
const dateRange = ref<[string, string]>(parseDateRange(dateFilterType.value)); const dateRange = ref<[string, string]>(parseDateRange(dateFilterType.value));
const startDraft = ref(""); const startDraft = ref("");
const endDraft = ref(""); const endDraft = ref("");
@@ -224,14 +225,9 @@ function getInitialStatus(value: unknown): "ALL" | GaoFulfillmentStatus {
return "ALL"; return "ALL";
} }
function parseDateFilterType(value: unknown): DateFilterType {
const dateFilter = getQueryText(value);
return dateOptions.some(item => item.value === dateFilter) ? dateFilter as DateFilterType : "today";
}
function parseDateRange(type: DateFilterType): [string, string] { function parseDateRange(type: DateFilterType): [string, string] {
if (type !== "custom") { if (type !== "custom") {
return buildPresetRange(type); return buildPresetDateRange(type);
} }
return [getQueryText(route.query.beginAt), getQueryText(route.query.endAt)]; return [getQueryText(route.query.beginAt), getQueryText(route.query.endAt)];
} }
@@ -368,7 +364,7 @@ async function selectDateFilter(value: DropdownValue): Promise<void> {
} }
dateFilterType.value = String(value) as DateFilterType; dateFilterType.value = String(value) as DateFilterType;
if (dateFilterType.value !== "custom") { if (dateFilterType.value !== "custom") {
dateRange.value = buildPresetRange(dateFilterType.value); dateRange.value = buildPresetDateRange(dateFilterType.value);
await replaceFilterQuery(); await replaceFilterQuery();
await reloadList(); await reloadList();
} else { } else {
@@ -444,45 +440,6 @@ function isValidTransitionDepth(value: string): boolean {
return value !== "" && Number.isInteger(depth) && 0 <= depth && depth <= 20; return value !== "" && Number.isInteger(depth) && 0 <= depth && depth <= 20;
} }
function buildPresetRange(type: Exclude<DateFilterType, "custom">): [string, string] {
if (type === "all") {
return ["", ""];
}
if (type === "lastWeek") {
const current = new Date();
current.setHours(0, 0, 0, 0);
const day = current.getDay() || 7;
const thisWeekMonday = new Date(current);
thisWeekMonday.setDate(current.getDate() - day + 1);
const lastWeekMonday = new Date(thisWeekMonday);
lastWeekMonday.setDate(thisWeekMonday.getDate() - 7);
const lastWeekSunday = new Date(thisWeekMonday);
lastWeekSunday.setDate(thisWeekMonday.getDate() - 1);
return [`${Time.formatDate(lastWeekMonday)} 00:00`, `${Time.formatDate(lastWeekSunday)} 23:59`];
}
if (type === "thisMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth(), 1);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(current)} 23:59`];
}
if (type === "lastMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth() - 1, 1);
const end = new Date(current.getFullYear(), current.getMonth(), 0);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(end)} 23:59`];
}
const offsetMap: Record<Exclude<DateFilterType, "all" | "custom" | "lastWeek" | "thisMonth" | "lastMonth">, number> = {
today: 0,
yesterday: 1
};
const date = new Date();
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() - offsetMap[type]);
const value = `${Time.formatDate(date)} 00:00`;
return [value, `${Time.formatDate(date)} 23:59`];
}
function buildUnixRange(): [number | undefined, number | undefined] { function buildUnixRange(): [number | undefined, number | undefined] {
if (dateFilterType.value === "all") { if (dateFilterType.value === "all") {
return [0, Time.toTimestamp(new Date())]; return [0, Time.toTimestamp(new Date())];
@@ -493,16 +450,6 @@ function buildUnixRange(): [number | undefined, number | undefined] {
]; ];
} }
function parseDateTime(value: string, isEnd: boolean): Date {
if (!value) {
return isEnd ? new Date() : new Date(0);
}
const [dateText, timeText = isEnd ? "23:59" : "00:00"] = value.split(" ");
const [year, month, day] = dateText.split("-").map(Number);
const [hour, minute] = timeText.split(":").map(Number);
return new Date(year, month - 1, day, hour || 0, minute || 0, isEnd ? 59 : 0, 0);
}
async function toDetail(item: OrderFulfillment): Promise<void> { async function toDetail(item: OrderFulfillment): Promise<void> {
if (item.id) { if (item.id) {
await router.push({ await router.push({
+8 -32
View File
@@ -142,7 +142,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { DropdownValue } from "tdesign-mobile-vue"; import type { DropdownValue } from "tdesign-mobile-vue";
import { PassTimeLabel, Time, Toolkit } from "timi-web"; import { PassTimeLabel, Toolkit } from "timi-web";
import { type PageResult, useUserStore } from "timi-web-api"; import { type PageResult, useUserStore } from "timi-web-api";
import CustomerAPI from "@/api/CustomerAPI"; import CustomerAPI from "@/api/CustomerAPI";
import PointAPI from "@/api/PointAPI"; import PointAPI from "@/api/PointAPI";
@@ -151,6 +151,7 @@ import { useCurrentStoreStore } from "@/store/currentStoreStore";
import type { Customer } from "@/types/Customer"; import type { Customer } from "@/types/Customer";
import type { PointLedger, PointLedgerChangeType, PointLedgerPage } from "@/types/Point"; import type { PointLedger, PointLedgerChangeType, PointLedgerPage } from "@/types/Point";
import { getCustomerPhotoURL } from "@/utils/customer"; import { getCustomerPhotoURL } from "@/utils/customer";
import { buildPresetDateRange, parseDateTime } from "@/utils/date";
import { Message } from "@/utils/message"; import { Message } from "@/utils/message";
import { gaoRoleCodes, permissionCodes } from "@/utils/permission"; import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
@@ -175,7 +176,7 @@ const customerKeyword = ref("");
const selectorVisible = ref(false); const selectorVisible = ref(false);
const changeTypeFilter = ref("ALL"); const changeTypeFilter = ref("ALL");
const dateFilter = ref<DateFilter>("last30"); const dateFilter = ref<DateFilter>("last30");
const dateRange = ref<[string, string]>(buildDateRange(dateFilter.value)); const dateRange = ref<[string, string]>(buildPresetDateRange("last30"));
const startDraft = ref(""); const startDraft = ref("");
const endDraft = ref(""); const endDraft = ref("");
const startDateVisible = ref(false); const startDateVisible = ref(false);
@@ -253,7 +254,7 @@ async function selectDateFilter(value: DropdownValue): Promise<void> {
} }
dateFilter.value = String(value) as DateFilter; dateFilter.value = String(value) as DateFilter;
if (dateFilter.value !== "custom") { if (dateFilter.value !== "custom") {
dateRange.value = buildDateRange(dateFilter.value); dateRange.value = buildPresetDateRange(dateFilter.value);
await reload(); await reload();
} }
} }
@@ -436,39 +437,14 @@ async function toDetail(item: PointLedger): Promise<void> {
}); });
} }
// ---------- 时间 ----------
function buildDateRange(type: DateFilter): [string, string] {
if (type === "all") {
return ["", ""];
}
const end = new Date();
end.setHours(23, 59, 0, 0);
if (type === "custom") {
return ["", Time.formatDateTime(end)];
}
const begin = new Date(end);
const dayCount = type === "today" ? 0 : type === "last7" ? 6 : 29;
begin.setDate(begin.getDate() - dayCount);
begin.setHours(0, 0, 0, 0);
return [Time.formatDateTime(begin), Time.formatDateTime(end)];
}
function buildUnixRange(): [number, number] { function buildUnixRange(): [number, number] {
if (!dateRange.value[0] && !dateRange.value[1]) { if (!dateRange.value[0] && !dateRange.value[1]) {
return [0, Date.now()]; return [0, Date.now()];
} }
return [toTimestamp(dateRange.value[0], false), toTimestamp(dateRange.value[1], true)]; return [
} parseDateTime(dateRange.value[0], false).getTime(),
parseDateTime(dateRange.value[1], true).getTime()
function toTimestamp(value: string, isEnd: boolean): number { ];
if (!value) {
return isEnd ? Date.now() : 0;
}
const [dateText, timeText = isEnd ? "23:59" : "00:00"] = value.split(" ");
const [year, month, day] = dateText.split("-").map(Number);
const [hour, minute] = timeText.split(":").map(Number);
return new Date(year, month - 1, day, hour || 0, minute || 0, isEnd ? 59 : 0).getTime();
} }
// ---------- 页面数据 ---------- // ---------- 页面数据 ----------
+4 -57
View File
@@ -158,6 +158,7 @@ import CustomerSelector from "@/components/CustomerSelector.vue";
import { useNavBarStore } from "@/store/navBarStore"; import { useNavBarStore } from "@/store/navBarStore";
import type { Customer } from "@/types/Customer"; import type { Customer } from "@/types/Customer";
import type { PointRedemption, PointRedemptionStatus } from "@/types/Point"; import type { PointRedemption, PointRedemptionStatus } from "@/types/Point";
import { buildPresetDateRange, parseDateFilterType, parseDateTime } from "@/utils/date";
import { Message } from "@/utils/message"; import { Message } from "@/utils/message";
import { getPackageQuantityText } from "@/utils/package"; import { getPackageQuantityText } from "@/utils/package";
import { getCustomerPhotoURL } from "@/utils/customer"; import { getCustomerPhotoURL } from "@/utils/customer";
@@ -183,7 +184,7 @@ const keyword = ref(getQueryText(route.query.keyword));
const customerId = ref(getQueryText(route.query.customerId)); const customerId = ref(getQueryText(route.query.customerId));
const selectedCustomer = ref<Customer>(); const selectedCustomer = ref<Customer>();
const customerSelectorVisible = ref(false); const customerSelectorVisible = ref(false);
const dateFilterType = ref<DateFilterType>(parseDateFilterType(route.query.dateType)); const dateFilterType = ref<DateFilterType>(parseDateFilterType(getQueryText(route.query.dateType), dateOptions, "today"));
const dateRange = ref<[string, string]>(parseDateRange(dateFilterType.value)); const dateRange = ref<[string, string]>(parseDateRange(dateFilterType.value));
const startDraft = ref(""); const startDraft = ref("");
const endDraft = ref(""); const endDraft = ref("");
@@ -202,14 +203,9 @@ function getQueryText(value: unknown): string {
return Array.isArray(value) ? "" : typeof value === "string" ? value : ""; return Array.isArray(value) ? "" : typeof value === "string" ? value : "";
} }
function parseDateFilterType(value: unknown): DateFilterType {
const dateFilter = getQueryText(value);
return dateOptions.some(item => item.value === dateFilter) ? dateFilter as DateFilterType : "today";
}
function parseDateRange(type: DateFilterType): [string, string] { function parseDateRange(type: DateFilterType): [string, string] {
if (type !== "custom") { if (type !== "custom") {
return buildPresetRange(type); return buildPresetDateRange(type);
} }
return [getQueryText(route.query.beginAt), getQueryText(route.query.endAt)]; return [getQueryText(route.query.beginAt), getQueryText(route.query.endAt)];
} }
@@ -296,7 +292,7 @@ async function selectDateFilter(value: DropdownValue): Promise<void> {
} }
dateFilterType.value = String(value) as DateFilterType; dateFilterType.value = String(value) as DateFilterType;
if (dateFilterType.value !== "custom") { if (dateFilterType.value !== "custom") {
dateRange.value = buildPresetRange(dateFilterType.value); dateRange.value = buildPresetDateRange(dateFilterType.value);
await replaceFilterQuery(); await replaceFilterQuery();
await reload(); await reload();
} else { } else {
@@ -358,45 +354,6 @@ async function replaceFilterQuery(): Promise<void> {
}); });
} }
function buildPresetRange(type: Exclude<DateFilterType, "custom">): [string, string] {
if (type === "all") {
return ["", ""];
}
if (type === "lastWeek") {
const current = new Date();
current.setHours(0, 0, 0, 0);
const day = current.getDay() || 7;
const thisWeekMonday = new Date(current);
thisWeekMonday.setDate(current.getDate() - day + 1);
const lastWeekMonday = new Date(thisWeekMonday);
lastWeekMonday.setDate(thisWeekMonday.getDate() - 7);
const lastWeekSunday = new Date(thisWeekMonday);
lastWeekSunday.setDate(thisWeekMonday.getDate() - 1);
return [`${Time.formatDate(lastWeekMonday)} 00:00`, `${Time.formatDate(lastWeekSunday)} 23:59`];
}
if (type === "thisMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth(), 1);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(current)} 23:59`];
}
if (type === "lastMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth() - 1, 1);
const end = new Date(current.getFullYear(), current.getMonth(), 0);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(end)} 23:59`];
}
const offsetMap: Record<Exclude<DateFilterType, "all" | "custom" | "lastWeek" | "thisMonth" | "lastMonth">, number> = {
today: 0,
yesterday: 1
};
const date = new Date();
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() - offsetMap[type]);
const value = `${Time.formatDate(date)} 00:00`;
return [value, `${Time.formatDate(date)} 23:59`];
}
function buildUnixRange(): [number | undefined, number | undefined] { function buildUnixRange(): [number | undefined, number | undefined] {
if (dateFilterType.value === "all") { if (dateFilterType.value === "all") {
return [0, Time.toTimestamp(new Date())]; return [0, Time.toTimestamp(new Date())];
@@ -407,16 +364,6 @@ function buildUnixRange(): [number | undefined, number | undefined] {
]; ];
} }
function parseDateTime(value: string, isEnd: boolean): Date {
if (!value) {
return isEnd ? new Date() : new Date(0);
}
const [dateText, timeText = isEnd ? "23:59" : "00:00"] = value.split(" ");
const [year, month, day] = dateText.split("-").map(Number);
const [hour, minute] = timeText.split(":").map(Number);
return new Date(year, month - 1, day, hour || 0, minute || 0, isEnd ? 59 : 0, 0);
}
function statusText(value?: PointRedemptionStatus): string { function statusText(value?: PointRedemptionStatus): string {
return ({ return ({
COMPLETED: "已兑换", COMPLETED: "已兑换",
+5 -52
View File
@@ -187,6 +187,7 @@ import type {
ConsumptionRecordSourceType, ConsumptionRecordSourceType,
ConsumptionRecordStatus ConsumptionRecordStatus
} from "@/types/ConsumptionRecord"; } from "@/types/ConsumptionRecord";
import { buildPresetDateRange, parseDateFilterType, parseDateTime } from "@/utils/date";
import { getErrorMessage } from "@/utils/error"; import { getErrorMessage } from "@/utils/error";
import { getCustomerPhotoURL } from "@/utils/customer"; import { getCustomerPhotoURL } from "@/utils/customer";
import { Message } from "@/utils/message"; import { Message } from "@/utils/message";
@@ -217,7 +218,7 @@ const customerId = ref("");
const selectedCustomer = ref<Customer>(); const selectedCustomer = ref<Customer>();
const customerSelectorVisible = ref(false); const customerSelectorVisible = ref(false);
const dateFilterType = ref<DateFilterType>("all"); const dateFilterType = ref<DateFilterType>("all");
const dateRange = ref<[string, string]>(buildPresetRange("all")); const dateRange = ref<[string, string]>(buildPresetDateRange("all"));
const startDraft = ref(""); const startDraft = ref("");
const endDraft = ref(""); const endDraft = ref("");
const startVisible = ref(false); const startVisible = ref(false);
@@ -247,7 +248,7 @@ function applyRouteQuery(): void {
customerId.value = getText(route.query.customerId); customerId.value = getText(route.query.customerId);
sourceType.value = ["ALL", "LEGACY_EVENT", "SALES_ORDER"].includes(routeSourceType) ? routeSourceType as ConsumptionRecordSourceType | "ALL" : "ALL"; sourceType.value = ["ALL", "LEGACY_EVENT", "SALES_ORDER"].includes(routeSourceType) ? routeSourceType as ConsumptionRecordSourceType | "ALL" : "ALL";
status.value = ["ALL", "ACTIVE", "REVOKED"].includes(routeStatus) ? routeStatus as ConsumptionRecordStatus | "ALL" : "ALL"; status.value = ["ALL", "ACTIVE", "REVOKED"].includes(routeStatus) ? routeStatus as ConsumptionRecordStatus | "ALL" : "ALL";
dateFilterType.value = dateOptions.some(item => item.value === routeDateType) ? routeDateType as DateFilterType : "all"; dateFilterType.value = parseDateFilterType(routeDateType, dateOptions, "all");
dateRange.value = parseDateRange(dateFilterType.value, getText); dateRange.value = parseDateRange(dateFilterType.value, getText);
} }
@@ -270,7 +271,7 @@ async function selectDateFilter(value: DropdownValue): Promise<void> {
dateFilterType.value = String(value) as DateFilterType; dateFilterType.value = String(value) as DateFilterType;
if (dateFilterType.value !== "custom") { if (dateFilterType.value !== "custom") {
dateRange.value = buildPresetRange(dateFilterType.value); dateRange.value = buildPresetDateRange(dateFilterType.value);
await reloadRecords(); await reloadRecords();
} }
} }
@@ -368,49 +369,11 @@ function buildPage(): ConsumptionRecordPage {
function parseDateRange(type: DateFilterType, getText: (value: unknown) => string): [string, string] { function parseDateRange(type: DateFilterType, getText: (value: unknown) => string): [string, string] {
if (type !== "custom") { if (type !== "custom") {
return buildPresetRange(type); return buildPresetDateRange(type);
} }
return [getText(route.query.beginAt), getText(route.query.endAt)]; return [getText(route.query.beginAt), getText(route.query.endAt)];
} }
function buildPresetRange(type: Exclude<DateFilterType, "custom">): [string, string] {
if (type === "all") {
return ["", ""];
}
if (type === "lastWeek") {
const current = new Date();
current.setHours(0, 0, 0, 0);
const day = current.getDay() || 7;
const thisWeekMonday = new Date(current);
thisWeekMonday.setDate(current.getDate() - day + 1);
const lastWeekMonday = new Date(thisWeekMonday);
lastWeekMonday.setDate(thisWeekMonday.getDate() - 7);
const lastWeekSunday = new Date(thisWeekMonday);
lastWeekSunday.setDate(thisWeekMonday.getDate() - 1);
return [`${Time.formatDate(lastWeekMonday)} 00:00`, `${Time.formatDate(lastWeekSunday)} 23:59`];
}
if (type === "thisMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth(), 1);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(current)} 23:59`];
}
if (type === "lastMonth") {
const current = new Date();
const begin = new Date(current.getFullYear(), current.getMonth() - 1, 1);
const end = new Date(current.getFullYear(), current.getMonth(), 0);
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(end)} 23:59`];
}
const offsetMap: Record<Exclude<DateFilterType, "all" | "custom" | "lastWeek" | "thisMonth" | "lastMonth">, number> = {
today: 0,
yesterday: 1
};
const date = new Date();
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() - offsetMap[type]);
return [`${Time.formatDate(date)} 00:00`, `${Time.formatDate(date)} 23:59`];
}
function buildUnixRange(): [number | undefined, number | undefined] { function buildUnixRange(): [number | undefined, number | undefined] {
if (dateFilterType.value === "all") { if (dateFilterType.value === "all") {
return [undefined, undefined]; return [undefined, undefined];
@@ -421,16 +384,6 @@ function buildUnixRange(): [number | undefined, number | undefined] {
]; ];
} }
function parseDateTime(value: string, isEnd: boolean): Date {
if (!value) {
return isEnd ? new Date() : new Date(0);
}
const [dateText, timeText = isEnd ? "23:59" : "00:00"] = value.split(" ");
const [year, month, day] = dateText.split("-").map(Number);
const [hour, minute] = timeText.split(":").map(Number);
return new Date(year, month - 1, day, hour || 0, minute || 0, isEnd ? 59 : 0, 0);
}
async function reloadRecords(value?: DropdownValue): Promise<void> { async function reloadRecords(value?: DropdownValue): Promise<void> {
if (value && Array.isArray(value)) { if (value && Array.isArray(value)) {
return; return;
+99
View File
@@ -0,0 +1,99 @@
import { Time } from "timi-web";
/** 日期筛选器支持的预设范围。 */
export type DateFilterPreset =
| "all"
| "today"
| "yesterday"
| "lastWeek"
| "thisMonth"
| "lastMonth"
| "last7"
| "last30";
/** 日期时间范围,文本格式为 YYYY-MM-DD HH:mm。 */
export type DateTimeRange = [string, string];
/**
* 根据预设筛选类型生成日期时间范围。
*
* `last7` 和 `last30` 为包含今天在内的最近 7 天和 30 天。
*/
export function buildPresetDateRange(type: DateFilterPreset, now = new Date()): DateTimeRange {
if (type === "all") {
return ["", ""];
}
if (type === "lastWeek") {
const current = getDateStart(now);
const day = current.getDay() || 7;
const thisWeekMonday = addDays(current, 1 - day);
const lastWeekMonday = addDays(thisWeekMonday, -7);
const lastWeekSunday = addDays(thisWeekMonday, -1);
return buildDayRange(lastWeekMonday, lastWeekSunday);
}
if (type === "thisMonth") {
const current = new Date(now);
const begin = new Date(current.getFullYear(), current.getMonth(), 1);
return buildDayRange(begin, current);
}
if (type === "lastMonth") {
const current = new Date(now);
const begin = new Date(current.getFullYear(), current.getMonth() - 1, 1);
const end = new Date(current.getFullYear(), current.getMonth(), 0);
return buildDayRange(begin, end);
}
if (type === "last7" || type === "last30") {
const end = new Date(now);
end.setHours(23, 59, 0, 0);
const begin = new Date(end);
begin.setDate(begin.getDate() - (type === "last7" ? 6 : 29));
begin.setHours(0, 0, 0, 0);
return [Time.formatDateTime(begin), Time.formatDateTime(end)];
}
const date = getDateStart(now);
date.setDate(date.getDate() - (type === "today" ? 0 : 1));
return buildDayRange(date, date);
}
/** 将路由或组件输入转换为合法的日期筛选类型。 */
export function parseDateFilterType<T extends string>(
value: unknown,
options: readonly { value: T }[],
fallback: T
): T {
const text = Array.isArray(value) ? String(value[0] || "") : String(value || "");
return options.some(item => item.value === text) ? text as T : fallback;
}
/** 将日期时间文本解析为本地时间。空开始时间和空结束时间分别回退到 Unix 起点和当前时间。 */
export function parseDateTime(value: string, isEnd: boolean): Date {
if (!value) {
return isEnd ? new Date() : new Date(0);
}
const [dateText, timeText = isEnd ? "23:59" : "00:00"] = value.split(" ");
const [year, month, day] = dateText.split("-").map(Number);
const [hour, minute] = timeText.split(":").map(Number);
return new Date(year, month - 1, day, hour || 0, minute || 0, isEnd ? 59 : 0, 0);
}
function buildDayRange(begin: Date, end: Date): DateTimeRange {
return [`${Time.formatDate(begin)} 00:00`, `${Time.formatDate(end)} 23:59`];
}
function getDateStart(date: Date): Date {
const value = new Date(date);
value.setHours(0, 0, 0, 0);
return value;
}
function addDays(date: Date, days: number): Date {
const value = new Date(date);
value.setDate(value.getDate() + days);
return value;
}