Merge branch 'zh' into 'main'
fix(task): time+download bug See merge request prism/lable/labelmain!28
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { getPersonProjectDashBoard } from "@/components/label/api/project"
|
import { getPersonProjectDashBoard } from "@/components/label/api/project"
|
||||||
import { Project } from "@/components/label/api/project/typing"
|
import { Project } from "@/components/label/api/project/typing"
|
||||||
|
import { formatWorkTimeHoursMinutes } from "@/components/label/utils/time"
|
||||||
import {
|
import {
|
||||||
SettingDataTable,
|
SettingDataTable,
|
||||||
SettingListHeader,
|
SettingListHeader,
|
||||||
@@ -18,24 +19,10 @@ import { DatePickerInput, type DatesRangeValue } from "@mantine/dates"
|
|||||||
import { IconRefresh, IconSearch } from "@tabler/icons-react"
|
import { IconRefresh, IconSearch } from "@tabler/icons-react"
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import "dayjs/locale/zh-cn"
|
import "dayjs/locale/zh-cn"
|
||||||
import duration from "dayjs/plugin/duration"
|
|
||||||
import { DataTableColumn } from "mantine-datatable"
|
import { DataTableColumn } from "mantine-datatable"
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||||
import classes from "../page.module.css"
|
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 {
|
interface FilterFormRecord {
|
||||||
query_date: DatesRangeValue
|
query_date: DatesRangeValue
|
||||||
}
|
}
|
||||||
@@ -201,7 +188,7 @@ export default function PersonalProjectTable() {
|
|||||||
render: (record: Project.PersonProjectDashboardResponseItem) => {
|
render: (record: Project.PersonProjectDashboardResponseItem) => {
|
||||||
return (
|
return (
|
||||||
<Text className={classes.tableCellText}>
|
<Text className={classes.tableCellText}>
|
||||||
{formatDurationSeconds(record.work_time)}
|
{formatWorkTimeHoursMinutes(record.work_time)}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { getCurrentPersonData } from "@/components/label/api/project"
|
import { getCurrentPersonData } from "@/components/label/api/project"
|
||||||
|
import { formatWorkTimeHoursMinutes } from "@/components/label/utils/time"
|
||||||
import {
|
import {
|
||||||
SettingContentPanel,
|
SettingContentPanel,
|
||||||
SettingPage,
|
SettingPage,
|
||||||
@@ -8,14 +9,11 @@ import {
|
|||||||
} from "@/components/setting/PageSurface"
|
} from "@/components/setting/PageSurface"
|
||||||
import { Stack, Text } from "@mantine/core"
|
import { Stack, Text } from "@mantine/core"
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import duration from "dayjs/plugin/duration"
|
|
||||||
import { useEffect, useMemo, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import PersonalProjectTable from "./components/PersonalProjectTable"
|
import PersonalProjectTable from "./components/PersonalProjectTable"
|
||||||
import PersonalTaskTable from "./components/PersonalTaskTable"
|
import PersonalTaskTable from "./components/PersonalTaskTable"
|
||||||
import classes from "./page.module.css"
|
import classes from "./page.module.css"
|
||||||
|
|
||||||
dayjs.extend(duration)
|
|
||||||
|
|
||||||
function monthText(month: string) {
|
function monthText(month: string) {
|
||||||
const list = [
|
const list = [
|
||||||
"一月",
|
"一月",
|
||||||
@@ -35,17 +33,6 @@ function monthText(month: string) {
|
|||||||
return list[idx] ?? ""
|
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() {
|
export default function PersonDashboardPage() {
|
||||||
const [data, setData] = useState<{
|
const [data, setData] = useState<{
|
||||||
label_data_size: number
|
label_data_size: number
|
||||||
@@ -68,9 +55,9 @@ export default function PersonDashboardPage() {
|
|||||||
|
|
||||||
const dataText = useMemo(() => {
|
const dataText = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
label: formatDurationSeconds(data?.label_work_time ?? 0),
|
label: formatWorkTimeHoursMinutes(data?.label_work_time ?? 0),
|
||||||
review1: formatDurationSeconds(data?.review1_work_time ?? 0),
|
review1: formatWorkTimeHoursMinutes(data?.review1_work_time ?? 0),
|
||||||
review2: formatDurationSeconds(data?.review2_work_time ?? 0),
|
review2: formatWorkTimeHoursMinutes(data?.review2_work_time ?? 0),
|
||||||
}
|
}
|
||||||
}, [data])
|
}, [data])
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
import { Daily } from "@/components/label/api/daily/typing"
|
import { Daily } from "@/components/label/api/daily/typing"
|
||||||
import { getSelfProjectList } from "@/components/label/api/project"
|
import { getSelfProjectList } from "@/components/label/api/project"
|
||||||
import { useAllUserStore } from "@/components/label/store/auth"
|
import { useAllUserStore } from "@/components/label/store/auth"
|
||||||
|
import { formatWorkTime } from "@/components/label/utils/time"
|
||||||
import {
|
import {
|
||||||
SettingContentPanel,
|
SettingContentPanel,
|
||||||
SettingCustomPageSizeControl,
|
SettingCustomPageSizeControl,
|
||||||
@@ -47,7 +48,6 @@ import { VirtualTreeSelect } from "@/components/tree-select/tree"
|
|||||||
import { DateInput } from "@mantine/dates"
|
import { DateInput } from "@mantine/dates"
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import "dayjs/locale/zh-cn"
|
import "dayjs/locale/zh-cn"
|
||||||
import duration from "dayjs/plugin/duration"
|
|
||||||
import { DataTableColumn, DataTableSortStatus } from "mantine-datatable"
|
import { DataTableColumn, DataTableSortStatus } from "mantine-datatable"
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||||
import DailyReportModal from "./components/DailyReportModal"
|
import DailyReportModal from "./components/DailyReportModal"
|
||||||
@@ -62,7 +62,6 @@ import {
|
|||||||
workTypeOpts,
|
workTypeOpts,
|
||||||
} from "./config"
|
} from "./config"
|
||||||
import classes from "./page.module.css"
|
import classes from "./page.module.css"
|
||||||
dayjs.extend(duration)
|
|
||||||
|
|
||||||
function weekDayText(day: number) {
|
function weekDayText(day: number) {
|
||||||
const list = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]
|
const list = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]
|
||||||
@@ -241,10 +240,6 @@ export default function PersonReportPage() {
|
|||||||
return accumulator + (Number.isFinite(value) ? value : 0)
|
return accumulator + (Number.isFinite(value) ? value : 0)
|
||||||
}, 0)
|
}, 0)
|
||||||
|
|
||||||
const getTime = (time: number) => {
|
|
||||||
return dayjs.duration(time, "seconds").format("HH:mm:ss")
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
const cols: DataTableColumn<Daily.DailyWorkList>[] = [
|
const cols: DataTableColumn<Daily.DailyWorkList>[] = [
|
||||||
{
|
{
|
||||||
@@ -335,8 +330,9 @@ export default function PersonReportPage() {
|
|||||||
title: "工时",
|
title: "工时",
|
||||||
width: 90,
|
width: 90,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
render: (record) => (record.work_time ? getTime(record.work_time) : 0),
|
render: (record) =>
|
||||||
footer: getTime(sum(records.map((r) => r.work_time ?? 0))),
|
record.work_time ? formatWorkTime(record.work_time) : 0,
|
||||||
|
footer: formatWorkTime(sum(records.map((r) => r.work_time ?? 0))),
|
||||||
},
|
},
|
||||||
{ accessor: "rate", title: "额定倍率", width: 90 },
|
{ accessor: "rate", title: "额定倍率", width: 90 },
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { TimeFilter } from "@/components/label/api/workload/typing"
|
import { TimeFilter } from "@/components/label/api/workload/typing"
|
||||||
|
import { formatWorkTime } from "@/components/label/utils/time"
|
||||||
import {
|
import {
|
||||||
SettingContentPanel,
|
SettingContentPanel,
|
||||||
SettingDataTable,
|
SettingDataTable,
|
||||||
@@ -23,12 +24,10 @@ import {
|
|||||||
} from "@tabler/icons-react"
|
} from "@tabler/icons-react"
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import "dayjs/locale/zh-cn"
|
import "dayjs/locale/zh-cn"
|
||||||
import duration from "dayjs/plugin/duration"
|
|
||||||
import { DataTableColumn, DataTableSortStatus } from "mantine-datatable"
|
import { DataTableColumn, DataTableSortStatus } from "mantine-datatable"
|
||||||
import { useEffect, useMemo, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import * as XLSX from "xlsx-js-style"
|
import * as XLSX from "xlsx-js-style"
|
||||||
import { DimensionType, GroupedData, MetricColumn } from "./config"
|
import { DimensionType, GroupedData, MetricColumn } from "./config"
|
||||||
dayjs.extend(duration)
|
|
||||||
|
|
||||||
function emptyGroupedData(): GroupedData {
|
function emptyGroupedData(): GroupedData {
|
||||||
return {
|
return {
|
||||||
@@ -157,10 +156,6 @@ export default function WorkloadMetricsTable(props: {
|
|||||||
return records
|
return records
|
||||||
}, [appliedFilters, data])
|
}, [appliedFilters, data])
|
||||||
|
|
||||||
const getTime = (time: number) => {
|
|
||||||
return dayjs.duration(time, "seconds").format("HH:mm:ss")
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseColumns = useMemo(() => {
|
const baseColumns = useMemo(() => {
|
||||||
const allCols: Record<DimensionType, DataTableColumn<any>[]> = {
|
const allCols: Record<DimensionType, DataTableColumn<any>[]> = {
|
||||||
all: [
|
all: [
|
||||||
@@ -253,12 +248,12 @@ export default function WorkloadMetricsTable(props: {
|
|||||||
return acc + (Number.isFinite(value) ? value : 0)
|
return acc + (Number.isFinite(value) ? value : 0)
|
||||||
}, 0)
|
}, 0)
|
||||||
if (["work_time"].includes(metric.key)) {
|
if (["work_time"].includes(metric.key)) {
|
||||||
return [metric.key, getTime(sum ?? 0)]
|
return [metric.key, formatWorkTime(sum)]
|
||||||
} else {
|
} else {
|
||||||
return [metric.key, sum]
|
return [metric.key, sum]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
) as Record<string, number>
|
) as Record<string, string | number>
|
||||||
|
|
||||||
const metricCols = metrics.map((metric) => {
|
const metricCols = metrics.map((metric) => {
|
||||||
if (["work_time"].includes(metric.key)) {
|
if (["work_time"].includes(metric.key)) {
|
||||||
@@ -268,7 +263,7 @@ export default function WorkloadMetricsTable(props: {
|
|||||||
width: 140,
|
width: 140,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
footer: totalsByKey[metric.key] ?? 0,
|
footer: totalsByKey[metric.key] ?? 0,
|
||||||
render: (record) => getTime(record[metric.key] ?? 0),
|
render: (record) => formatWorkTime(record[metric.key]),
|
||||||
} as DataTableColumn<any>
|
} as DataTableColumn<any>
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
|
|||||||
41
components/label/utils/time.ts
Normal file
41
components/label/utils/time.ts
Normal file
@@ -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 "-"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user