From f3a348e036478ae3d9da33e97455e551a1030c3f Mon Sep 17 00:00:00 2001 From: zhangheng Date: Tue, 2 Jun 2026 10:43:54 +0800 Subject: [PATCH] fix(task): time+download bug --- .../components/PersonalProjectTable.tsx | 17 +------- app/person/dashboard/page.tsx | 21 ++-------- app/person/report/page.tsx | 12 ++---- app/person/workload/WorkloadMetricsTable.tsx | 13 ++---- components/label/utils/time.ts | 41 +++++++++++++++++++ 5 files changed, 55 insertions(+), 49 deletions(-) create mode 100644 components/label/utils/time.ts diff --git a/app/person/dashboard/components/PersonalProjectTable.tsx b/app/person/dashboard/components/PersonalProjectTable.tsx index e821296..37c95cb 100644 --- a/app/person/dashboard/components/PersonalProjectTable.tsx +++ b/app/person/dashboard/components/PersonalProjectTable.tsx @@ -2,6 +2,7 @@ import { getPersonProjectDashBoard } from "@/components/label/api/project" import { Project } from "@/components/label/api/project/typing" +import { formatWorkTimeHoursMinutes } from "@/components/label/utils/time" import { SettingDataTable, SettingListHeader, @@ -18,24 +19,10 @@ import { DatePickerInput, type DatesRangeValue } from "@mantine/dates" import { IconRefresh, IconSearch } from "@tabler/icons-react" import dayjs from "dayjs" import "dayjs/locale/zh-cn" -import duration from "dayjs/plugin/duration" import { DataTableColumn } from "mantine-datatable" import { useCallback, useEffect, useMemo, useState } from "react" import classes from "../page.module.css" -dayjs.extend(duration) - -function formatDurationSeconds(seconds?: number | null) { - const value = seconds ?? 0 - if (!value) return "-" - const durationObj = dayjs.duration(value, "seconds") - const hours = durationObj.hours() - const minutes = durationObj.minutes() - if (hours) return `${hours}小时${minutes}分钟` - if (minutes) return `${minutes}分钟` - return "-" -} - interface FilterFormRecord { query_date: DatesRangeValue } @@ -201,7 +188,7 @@ export default function PersonalProjectTable() { render: (record: Project.PersonProjectDashboardResponseItem) => { return ( - {formatDurationSeconds(record.work_time)} + {formatWorkTimeHoursMinutes(record.work_time)} ) }, diff --git a/app/person/dashboard/page.tsx b/app/person/dashboard/page.tsx index 1c969c9..ce338ef 100644 --- a/app/person/dashboard/page.tsx +++ b/app/person/dashboard/page.tsx @@ -1,6 +1,7 @@ "use client" import { getCurrentPersonData } from "@/components/label/api/project" +import { formatWorkTimeHoursMinutes } from "@/components/label/utils/time" import { SettingContentPanel, SettingPage, @@ -8,14 +9,11 @@ import { } from "@/components/setting/PageSurface" import { Stack, Text } from "@mantine/core" import dayjs from "dayjs" -import duration from "dayjs/plugin/duration" import { useEffect, useMemo, useState } from "react" import PersonalProjectTable from "./components/PersonalProjectTable" import PersonalTaskTable from "./components/PersonalTaskTable" import classes from "./page.module.css" -dayjs.extend(duration) - function monthText(month: string) { const list = [ "一月", @@ -35,17 +33,6 @@ function monthText(month: string) { return list[idx] ?? "" } -function formatDurationSeconds(seconds?: number | null) { - const value = seconds ?? 0 - if (!value) return "-" - const durationObj = dayjs.duration(value, "seconds") - const hours = durationObj.hours() - const minutes = durationObj.minutes() - if (hours) return `${hours}小时${minutes}分钟` - if (minutes) return `${minutes}分钟` - return "-" -} - export default function PersonDashboardPage() { const [data, setData] = useState<{ label_data_size: number @@ -68,9 +55,9 @@ export default function PersonDashboardPage() { const dataText = useMemo(() => { return { - label: formatDurationSeconds(data?.label_work_time ?? 0), - review1: formatDurationSeconds(data?.review1_work_time ?? 0), - review2: formatDurationSeconds(data?.review2_work_time ?? 0), + label: formatWorkTimeHoursMinutes(data?.label_work_time ?? 0), + review1: formatWorkTimeHoursMinutes(data?.review1_work_time ?? 0), + review2: formatWorkTimeHoursMinutes(data?.review2_work_time ?? 0), } }, [data]) diff --git a/app/person/report/page.tsx b/app/person/report/page.tsx index 216be81..2aa95ee 100644 --- a/app/person/report/page.tsx +++ b/app/person/report/page.tsx @@ -7,6 +7,7 @@ import { import { Daily } from "@/components/label/api/daily/typing" import { getSelfProjectList } from "@/components/label/api/project" import { useAllUserStore } from "@/components/label/store/auth" +import { formatWorkTime } from "@/components/label/utils/time" import { SettingContentPanel, SettingCustomPageSizeControl, @@ -47,7 +48,6 @@ import { VirtualTreeSelect } from "@/components/tree-select/tree" import { DateInput } from "@mantine/dates" import dayjs from "dayjs" import "dayjs/locale/zh-cn" -import duration from "dayjs/plugin/duration" import { DataTableColumn, DataTableSortStatus } from "mantine-datatable" import { useCallback, useEffect, useMemo, useState } from "react" import DailyReportModal from "./components/DailyReportModal" @@ -62,7 +62,6 @@ import { workTypeOpts, } from "./config" import classes from "./page.module.css" -dayjs.extend(duration) function weekDayText(day: number) { const list = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"] @@ -241,10 +240,6 @@ export default function PersonReportPage() { return accumulator + (Number.isFinite(value) ? value : 0) }, 0) - const getTime = (time: number) => { - return dayjs.duration(time, "seconds").format("HH:mm:ss") - } - const columns = useMemo(() => { const cols: DataTableColumn[] = [ { @@ -335,8 +330,9 @@ export default function PersonReportPage() { title: "工时", width: 90, sortable: true, - render: (record) => (record.work_time ? getTime(record.work_time) : 0), - footer: getTime(sum(records.map((r) => r.work_time ?? 0))), + render: (record) => + record.work_time ? formatWorkTime(record.work_time) : 0, + footer: formatWorkTime(sum(records.map((r) => r.work_time ?? 0))), }, { accessor: "rate", title: "额定倍率", width: 90 }, { diff --git a/app/person/workload/WorkloadMetricsTable.tsx b/app/person/workload/WorkloadMetricsTable.tsx index 1fa32e3..dc47d9a 100644 --- a/app/person/workload/WorkloadMetricsTable.tsx +++ b/app/person/workload/WorkloadMetricsTable.tsx @@ -1,6 +1,7 @@ "use client" import { TimeFilter } from "@/components/label/api/workload/typing" +import { formatWorkTime } from "@/components/label/utils/time" import { SettingContentPanel, SettingDataTable, @@ -23,12 +24,10 @@ import { } from "@tabler/icons-react" import dayjs from "dayjs" import "dayjs/locale/zh-cn" -import duration from "dayjs/plugin/duration" import { DataTableColumn, DataTableSortStatus } from "mantine-datatable" import { useEffect, useMemo, useState } from "react" import * as XLSX from "xlsx-js-style" import { DimensionType, GroupedData, MetricColumn } from "./config" -dayjs.extend(duration) function emptyGroupedData(): GroupedData { return { @@ -157,10 +156,6 @@ export default function WorkloadMetricsTable(props: { return records }, [appliedFilters, data]) - const getTime = (time: number) => { - return dayjs.duration(time, "seconds").format("HH:mm:ss") - } - const baseColumns = useMemo(() => { const allCols: Record[]> = { all: [ @@ -253,12 +248,12 @@ export default function WorkloadMetricsTable(props: { return acc + (Number.isFinite(value) ? value : 0) }, 0) if (["work_time"].includes(metric.key)) { - return [metric.key, getTime(sum ?? 0)] + return [metric.key, formatWorkTime(sum)] } else { return [metric.key, sum] } }) - ) as Record + ) as Record const metricCols = metrics.map((metric) => { if (["work_time"].includes(metric.key)) { @@ -268,7 +263,7 @@ export default function WorkloadMetricsTable(props: { width: 140, sortable: true, footer: totalsByKey[metric.key] ?? 0, - render: (record) => getTime(record[metric.key] ?? 0), + render: (record) => formatWorkTime(record[metric.key]), } as DataTableColumn } else { return { diff --git a/components/label/utils/time.ts b/components/label/utils/time.ts new file mode 100644 index 0000000..7e6ae14 --- /dev/null +++ b/components/label/utils/time.ts @@ -0,0 +1,41 @@ +function normalizeDurationSeconds(value: unknown) { + const totalSeconds = Number(value ?? 0) + if (!Number.isFinite(totalSeconds) || totalSeconds <= 0) { + return 0 + } + + return Math.floor(totalSeconds) +} + +export function formatWorkTime(value: unknown) { + const normalizedSeconds = normalizeDurationSeconds(value) + if (!normalizedSeconds) { + return "00:00:00" + } + + const hours = Math.floor(normalizedSeconds / 3600) + const minutes = Math.floor((normalizedSeconds % 3600) / 60) + const seconds = normalizedSeconds % 60 + + return [hours, minutes, seconds] + .map((part) => String(part).padStart(2, "0")) + .join(":") +} + +export function formatWorkTimeHoursMinutes(value: unknown) { + const normalizedSeconds = normalizeDurationSeconds(value) + if (!normalizedSeconds) { + return "-" + } + + const hours = Math.floor(normalizedSeconds / 3600) + const minutes = Math.floor((normalizedSeconds % 3600) / 60) + + if (hours > 0) { + return `${hours}小时${minutes}分钟` + } + if (minutes > 0) { + return `${minutes}分钟` + } + return "-" +}