fix(daily): fix style
This commit is contained in:
81
app/person/report/components/DailyReportModal.module.css
Normal file
81
app/person/report/components/DailyReportModal.module.css
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
.modalContent {
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalHeader {
|
||||||
|
padding: 20px 24px 0;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalTitle {
|
||||||
|
color: #1f2329;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalBody {
|
||||||
|
padding: 16px 24px 24px;
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formSurface {
|
||||||
|
border-color: #e6ebf2;
|
||||||
|
background: linear-gradient(180deg, #ffffff 0%, #fcfdff 100%);
|
||||||
|
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.formPanel :global(.mantine-InputWrapper-label) {
|
||||||
|
color: #1f2329;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 22px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formPanel :global(.mantine-Input-input),
|
||||||
|
.formPanel :global(.mantine-Select-input),
|
||||||
|
.formPanel :global(.mantine-NumberInput-input),
|
||||||
|
.formPanel :global(.mantine-Textarea-input) {
|
||||||
|
min-height: 36px;
|
||||||
|
border-color: #d0d5dd;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #31373d;
|
||||||
|
font-size: 14px;
|
||||||
|
box-shadow: none;
|
||||||
|
transition:
|
||||||
|
border-color 0.15s ease,
|
||||||
|
box-shadow 0.15s ease,
|
||||||
|
background-color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formPanel :global(.mantine-Input-input::placeholder),
|
||||||
|
.formPanel :global(.mantine-Select-input::placeholder),
|
||||||
|
.formPanel :global(.mantine-NumberInput-input::placeholder),
|
||||||
|
.formPanel :global(.mantine-Textarea-input::placeholder) {
|
||||||
|
color: #98a2b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formPanel :global(.mantine-Input-input:focus),
|
||||||
|
.formPanel :global(.mantine-Select-input:focus),
|
||||||
|
.formPanel :global(.mantine-NumberInput-input:focus),
|
||||||
|
.formPanel :global(.mantine-Textarea-input:focus) {
|
||||||
|
border-color: #69c0ff;
|
||||||
|
box-shadow: 0 0 0 3px rgba(105, 192, 255, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.formPanel :global(.mantine-Input-input[readonly]),
|
||||||
|
.formPanel :global(.mantine-NumberInput-input[readonly]) {
|
||||||
|
background: #f8fafc;
|
||||||
|
color: #667085;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formPanel :global(.mantine-Button-root) {
|
||||||
|
min-height: 36px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding-inline: 14px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
Group,
|
Group,
|
||||||
Modal,
|
Modal,
|
||||||
NumberInput,
|
NumberInput,
|
||||||
|
Paper,
|
||||||
Select,
|
Select,
|
||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
Stack,
|
Stack,
|
||||||
@@ -23,6 +24,7 @@ import {
|
|||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { notifications } from "@mantine/notifications"
|
import { notifications } from "@mantine/notifications"
|
||||||
import { useEffect, useMemo } from "react"
|
import { useEffect, useMemo } from "react"
|
||||||
|
import classes from "./DailyReportModal.module.css"
|
||||||
import {
|
import {
|
||||||
accountTypeOpts,
|
accountTypeOpts,
|
||||||
actualTypeOpts,
|
actualTypeOpts,
|
||||||
@@ -332,213 +334,181 @@ export default function DailyReportModal(props: {
|
|||||||
title={record ? "编辑日报" : "填写日报"}
|
title={record ? "编辑日报" : "填写日报"}
|
||||||
size={840}
|
size={840}
|
||||||
centered
|
centered
|
||||||
|
classNames={{
|
||||||
|
content: classes.modalContent,
|
||||||
|
header: classes.modalHeader,
|
||||||
|
title: classes.modalTitle,
|
||||||
|
body: classes.modalBody,
|
||||||
|
}}
|
||||||
|
overlayProps={{ backgroundOpacity: 0.22 }}
|
||||||
|
transitionProps={{
|
||||||
|
transition: "fade",
|
||||||
|
duration: 100,
|
||||||
|
timingFunction: "ease",
|
||||||
|
}}
|
||||||
closeOnClickOutside={false}>
|
closeOnClickOutside={false}>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<Stack gap="sm">
|
<Paper
|
||||||
<SimpleGrid cols={{ base: 1, sm: 3 }} spacing="sm">
|
withBorder
|
||||||
<TextInput
|
radius="lg"
|
||||||
label="姓名"
|
p="md"
|
||||||
size="xs"
|
shadow="xs"
|
||||||
radius="xs"
|
className={classes.formSurface}>
|
||||||
value={form.values.user_name}
|
<Stack gap="md" className={classes.formPanel}>
|
||||||
readOnly
|
<SimpleGrid cols={{ base: 1, sm: 3 }} spacing="sm">
|
||||||
/>
|
<TextInput label="姓名" value={form.values.user_name} readOnly />
|
||||||
<TextInput
|
<TextInput
|
||||||
label="日期"
|
label="日期"
|
||||||
size="xs"
|
type="date"
|
||||||
radius="xs"
|
withAsterisk
|
||||||
type="date"
|
{...form.getInputProps("date")}
|
||||||
withAsterisk
|
/>
|
||||||
{...form.getInputProps("date")}
|
<div />
|
||||||
/>
|
|
||||||
<div />
|
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label="开始时间"
|
label="开始时间"
|
||||||
size="xs"
|
type="time"
|
||||||
radius="xs"
|
{...form.getInputProps("start_time")}
|
||||||
type="time"
|
/>
|
||||||
{...form.getInputProps("start_time")}
|
<TextInput
|
||||||
/>
|
label="结束时间"
|
||||||
<TextInput
|
type="time"
|
||||||
label="结束时间"
|
{...form.getInputProps("end_time")}
|
||||||
size="xs"
|
/>
|
||||||
radius="xs"
|
<div />
|
||||||
type="time"
|
|
||||||
{...form.getInputProps("end_time")}
|
|
||||||
/>
|
|
||||||
<div />
|
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
label="规定类型"
|
label="规定类型"
|
||||||
size="xs"
|
withAsterisk
|
||||||
radius="xs"
|
data={setTypeOpts}
|
||||||
withAsterisk
|
searchable
|
||||||
data={setTypeOpts}
|
clearable
|
||||||
searchable
|
{...form.getInputProps("set_type")}
|
||||||
clearable
|
/>
|
||||||
{...form.getInputProps("set_type")}
|
<Select
|
||||||
/>
|
label="出勤类型"
|
||||||
<Select
|
withAsterisk
|
||||||
label="出勤类型"
|
data={actualTypeOpts}
|
||||||
size="xs"
|
searchable
|
||||||
radius="xs"
|
clearable
|
||||||
withAsterisk
|
{...form.getInputProps("actual_type")}
|
||||||
data={actualTypeOpts}
|
/>
|
||||||
searchable
|
<div />
|
||||||
clearable
|
|
||||||
{...form.getInputProps("actual_type")}
|
|
||||||
/>
|
|
||||||
<div />
|
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
label="项目名称"
|
label="项目名称"
|
||||||
size="xs"
|
data={projectOptions}
|
||||||
radius="xs"
|
searchable
|
||||||
data={projectOptions}
|
clearable
|
||||||
searchable
|
{...form.getInputProps("project_id")}
|
||||||
clearable
|
/>
|
||||||
{...form.getInputProps("project_id")}
|
<Select
|
||||||
/>
|
label="任务组长"
|
||||||
<Select
|
data={leaderOptions}
|
||||||
label="任务组长"
|
searchable
|
||||||
size="xs"
|
clearable
|
||||||
radius="xs"
|
{...form.getInputProps("leader_uid")}
|
||||||
data={leaderOptions}
|
/>
|
||||||
searchable
|
<Select
|
||||||
clearable
|
label="工作类别"
|
||||||
{...form.getInputProps("leader_uid")}
|
data={workTypeOpts}
|
||||||
/>
|
searchable
|
||||||
<Select
|
clearable
|
||||||
label="工作类别"
|
{...form.getInputProps("work_type")}
|
||||||
size="xs"
|
/>
|
||||||
radius="xs"
|
|
||||||
data={workTypeOpts}
|
|
||||||
searchable
|
|
||||||
clearable
|
|
||||||
{...form.getInputProps("work_type")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
label="对象类型"
|
label="对象类型"
|
||||||
size="xs"
|
data={objTypeOpts}
|
||||||
radius="xs"
|
searchable
|
||||||
data={objTypeOpts}
|
clearable
|
||||||
searchable
|
{...form.getInputProps("obj_type")}
|
||||||
clearable
|
/>
|
||||||
{...form.getInputProps("obj_type")}
|
<Select
|
||||||
/>
|
label="绩效核算"
|
||||||
<Select
|
data={performanceOpts}
|
||||||
label="绩效核算"
|
searchable
|
||||||
size="xs"
|
clearable
|
||||||
radius="xs"
|
{...form.getInputProps("performance_accounting")}
|
||||||
data={performanceOpts}
|
/>
|
||||||
searchable
|
<Select
|
||||||
clearable
|
label="核算方式"
|
||||||
{...form.getInputProps("performance_accounting")}
|
data={accountTypeOpts}
|
||||||
/>
|
searchable
|
||||||
<Select
|
clearable
|
||||||
label="核算方式"
|
{...form.getInputProps("accounting_type")}
|
||||||
size="xs"
|
/>
|
||||||
radius="xs"
|
|
||||||
data={accountTypeOpts}
|
|
||||||
searchable
|
|
||||||
clearable
|
|
||||||
{...form.getInputProps("accounting_type")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<NumberInput
|
<NumberInput
|
||||||
label="工时"
|
label="工时"
|
||||||
size="xs"
|
min={0}
|
||||||
radius="xs"
|
decimalScale={1}
|
||||||
min={0}
|
allowNegative={false}
|
||||||
decimalScale={1}
|
hideControls
|
||||||
allowNegative={false}
|
rightSection={<HourRightSection />}
|
||||||
hideControls
|
rightSectionWidth={78}
|
||||||
rightSection={<HourRightSection />}
|
{...form.getInputProps("work_time")}
|
||||||
rightSectionWidth={78}
|
/>
|
||||||
{...form.getInputProps("work_time")}
|
<NumberInput
|
||||||
/>
|
label="额定倍速"
|
||||||
<NumberInput
|
min={0}
|
||||||
label="额定倍速"
|
allowNegative={false}
|
||||||
size="xs"
|
hideControls
|
||||||
radius="xs"
|
{...form.getInputProps("rate")}
|
||||||
min={0}
|
/>
|
||||||
allowNegative={false}
|
<div />
|
||||||
hideControls
|
|
||||||
{...form.getInputProps("rate")}
|
|
||||||
/>
|
|
||||||
<div />
|
|
||||||
|
|
||||||
<NumberInput
|
<NumberInput
|
||||||
label="标准量"
|
label="标准量"
|
||||||
size="xs"
|
value={form.values.standard_size}
|
||||||
radius="xs"
|
readOnly
|
||||||
value={form.values.standard_size}
|
hideControls
|
||||||
readOnly
|
/>
|
||||||
hideControls
|
<NumberInput
|
||||||
/>
|
label="完成量"
|
||||||
<NumberInput
|
min={0}
|
||||||
label="完成量"
|
allowNegative={false}
|
||||||
size="xs"
|
hideControls
|
||||||
radius="xs"
|
{...form.getInputProps("completed_size")}
|
||||||
min={0}
|
/>
|
||||||
allowNegative={false}
|
<div />
|
||||||
hideControls
|
|
||||||
{...form.getInputProps("completed_size")}
|
|
||||||
/>
|
|
||||||
<div />
|
|
||||||
|
|
||||||
<NumberInput
|
<NumberInput
|
||||||
label="余量"
|
label="余量"
|
||||||
size="xs"
|
value={form.values.residual_size}
|
||||||
radius="xs"
|
readOnly
|
||||||
value={form.values.residual_size}
|
hideControls
|
||||||
readOnly
|
/>
|
||||||
hideControls
|
<NumberInput
|
||||||
/>
|
label="余量工时"
|
||||||
<NumberInput
|
value={form.values.residual_work_time}
|
||||||
label="余量工时"
|
readOnly
|
||||||
size="xs"
|
hideControls
|
||||||
radius="xs"
|
rightSection={<HourRightSection />}
|
||||||
value={form.values.residual_work_time}
|
rightSectionWidth={78}
|
||||||
readOnly
|
/>
|
||||||
hideControls
|
<TextInput
|
||||||
rightSection={<HourRightSection />}
|
label="余量正负"
|
||||||
rightSectionWidth={78}
|
value={form.values.residual_pm}
|
||||||
/>
|
readOnly
|
||||||
<TextInput
|
/>
|
||||||
label="余量正负"
|
</SimpleGrid>
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
value={form.values.residual_pm}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</SimpleGrid>
|
|
||||||
|
|
||||||
<TextInput
|
<TextInput label="备注" {...form.getInputProps("remarks")} />
|
||||||
label="备注"
|
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
{...form.getInputProps("remarks")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Group justify="flex-end" gap="sm">
|
<Group justify="flex-end" gap="sm">
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
size="xs"
|
onClick={() => {
|
||||||
radius="xs"
|
form.reset()
|
||||||
onClick={() => {
|
onCloseAction()
|
||||||
form.reset()
|
}}>
|
||||||
onCloseAction()
|
取消
|
||||||
}}>
|
</Button>
|
||||||
取消
|
<Button type="submit">提交</Button>
|
||||||
</Button>
|
</Group>
|
||||||
<Button size="xs" radius="xs" type="submit">
|
</Stack>
|
||||||
提交
|
</Paper>
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</form>
|
</form>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,15 +7,24 @@ 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 {
|
||||||
|
SettingContentPanel,
|
||||||
|
SettingDataTable,
|
||||||
|
SettingFilterActions,
|
||||||
|
SettingFilterPanel,
|
||||||
|
SettingHeaderActions,
|
||||||
|
SettingListHeader,
|
||||||
|
SettingPage,
|
||||||
|
SettingPanel,
|
||||||
|
} from "@/components/setting/PageSurface"
|
||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
Collapse,
|
Collapse,
|
||||||
Flex,
|
|
||||||
Group,
|
Group,
|
||||||
|
Modal,
|
||||||
MultiSelect,
|
MultiSelect,
|
||||||
Paper,
|
|
||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
@@ -34,11 +43,7 @@ import {
|
|||||||
} from "@tabler/icons-react"
|
} from "@tabler/icons-react"
|
||||||
|
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import {
|
import { DataTableColumn, DataTableSortStatus } from "mantine-datatable"
|
||||||
DataTable,
|
|
||||||
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"
|
||||||
import {
|
import {
|
||||||
@@ -105,6 +110,8 @@ function toNumber(value?: string) {
|
|||||||
return Number.isFinite(n) ? n : undefined
|
return Number.isFinite(n) ? n : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_RECORDS_PER_PAGE_OPTIONS = [10, 15, 20, 50]
|
||||||
|
|
||||||
export default function PersonReportPage() {
|
export default function PersonReportPage() {
|
||||||
const treeData = useAllUserStore((s) => s.treeData)
|
const treeData = useAllUserStore((s) => s.treeData)
|
||||||
|
|
||||||
@@ -137,6 +144,12 @@ export default function PersonReportPage() {
|
|||||||
|
|
||||||
const [page, setPage] = useState(1)
|
const [page, setPage] = useState(1)
|
||||||
const [pageSize, setPageSize] = useState(20)
|
const [pageSize, setPageSize] = useState(20)
|
||||||
|
const [recordsPerPageOptions, setRecordsPerPageOptions] = useState<number[]>(
|
||||||
|
() => [...DEFAULT_RECORDS_PER_PAGE_OPTIONS]
|
||||||
|
)
|
||||||
|
const [customPageSizeInput, setCustomPageSizeInput] = useState("")
|
||||||
|
const [customPageSizeModalOpened, setCustomPageSizeModalOpened] =
|
||||||
|
useState(false)
|
||||||
const [totalItems, setTotalItems] = useState(0)
|
const [totalItems, setTotalItems] = useState(0)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [records, setRecords] = useState<Daily.DailyWorkList[]>([])
|
const [records, setRecords] = useState<Daily.DailyWorkList[]>([])
|
||||||
@@ -229,6 +242,53 @@ export default function PersonReportPage() {
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const openCustomPageSizeModal = useCallback(() => {
|
||||||
|
setCustomPageSizeModalOpened(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const closeCustomPageSizeModal = useCallback(() => {
|
||||||
|
setCustomPageSizeModalOpened(false)
|
||||||
|
setCustomPageSizeInput("")
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleAddCustomPageSize = useCallback(() => {
|
||||||
|
const rawValue = customPageSizeInput.trim()
|
||||||
|
if (!rawValue) return
|
||||||
|
const size = Number(rawValue)
|
||||||
|
if (!Number.isInteger(size) || size <= 0) {
|
||||||
|
notifications.show({
|
||||||
|
color: "red",
|
||||||
|
title: "输入无效",
|
||||||
|
message: "请填写大于 0 的整数条数",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (recordsPerPageOptions.includes(size)) {
|
||||||
|
notifications.show({
|
||||||
|
color: "blue",
|
||||||
|
title: "已存在",
|
||||||
|
message: `每页 ${size} 条已在可选项中`,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setRecordsPerPageOptions((prev) => [...prev, size].sort((a, b) => a - b))
|
||||||
|
closeCustomPageSizeModal()
|
||||||
|
notifications.show({
|
||||||
|
color: "green",
|
||||||
|
title: "添加成功",
|
||||||
|
message: `已新增每页 ${size} 条`,
|
||||||
|
})
|
||||||
|
}, [closeCustomPageSizeModal, customPageSizeInput, recordsPerPageOptions])
|
||||||
|
|
||||||
|
const handleCustomPageSizeEnter = useCallback(
|
||||||
|
(event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (event.key !== "Enter") return
|
||||||
|
event.preventDefault()
|
||||||
|
handleAddCustomPageSize()
|
||||||
|
},
|
||||||
|
[handleAddCustomPageSize]
|
||||||
|
)
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
const cols: DataTableColumn<Daily.DailyWorkList>[] = [
|
const cols: DataTableColumn<Daily.DailyWorkList>[] = [
|
||||||
{ accessor: "id", title: "ID", width: 80, sortable: true },
|
{ accessor: "id", title: "ID", width: 80, sortable: true },
|
||||||
@@ -429,21 +489,13 @@ export default function PersonReportPage() {
|
|||||||
}, [asyncGetSelfProjectList])
|
}, [asyncGetSelfProjectList])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack w="100%" h="calc(100vh - 56px)" p="md" gap="md">
|
<SettingPage>
|
||||||
<Paper
|
<SettingFilterPanel>
|
||||||
withBorder
|
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
style={{
|
|
||||||
borderColor:
|
|
||||||
"light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4))",
|
|
||||||
}}>
|
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Group justify="space-between" wrap="wrap" gap="sm">
|
<Group justify="space-between" wrap="wrap" gap="sm">
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<Text fw={700}>搜索条件</Text>
|
<Text fw={700}>筛选条件</Text>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
|
||||||
variant="transparent"
|
variant="transparent"
|
||||||
rightSection={
|
rightSection={
|
||||||
searchExpanded ? (
|
searchExpanded ? (
|
||||||
@@ -457,11 +509,9 @@ export default function PersonReportPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Group gap="sm">
|
<SettingFilterActions mt={0}>
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
leftSection={<IconRefresh size={16} />}
|
leftSection={<IconRefresh size={16} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFilters(defaultFilters)
|
setFilters(defaultFilters)
|
||||||
@@ -472,8 +522,6 @@ export default function PersonReportPage() {
|
|||||||
重置
|
重置
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
leftSection={<IconSearch size={16} />}
|
leftSection={<IconSearch size={16} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setAppliedFilters(filters)
|
setAppliedFilters(filters)
|
||||||
@@ -482,18 +530,12 @@ export default function PersonReportPage() {
|
|||||||
}}>
|
}}>
|
||||||
查询
|
查询
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</SettingFilterActions>
|
||||||
</Group>
|
</Group>
|
||||||
<Collapse
|
<Collapse in={searchExpanded} transitionDuration={150} animateOpacity>
|
||||||
in={searchExpanded}
|
|
||||||
transitionDuration={150}
|
|
||||||
transitionTimingFunction="ease"
|
|
||||||
animateOpacity>
|
|
||||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 3, lg: 4 }} spacing="sm">
|
<SimpleGrid cols={{ base: 1, sm: 2, md: 3, lg: 4 }} spacing="sm">
|
||||||
<TextInput
|
<TextInput
|
||||||
label="开始日期"
|
label="开始日期"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
type="date"
|
type="date"
|
||||||
value={filters.date_start}
|
value={filters.date_start}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -505,8 +547,6 @@ export default function PersonReportPage() {
|
|||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="结束日期"
|
label="结束日期"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
type="date"
|
type="date"
|
||||||
value={filters.date_end}
|
value={filters.date_end}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -515,8 +555,6 @@ export default function PersonReportPage() {
|
|||||||
/>
|
/>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
label="项目名称"
|
label="项目名称"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
data={projectOptions}
|
data={projectOptions}
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
@@ -525,8 +563,6 @@ export default function PersonReportPage() {
|
|||||||
/>
|
/>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
label="任务组长"
|
label="任务组长"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
data={leaderOptions}
|
data={leaderOptions}
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
@@ -535,8 +571,6 @@ export default function PersonReportPage() {
|
|||||||
/>
|
/>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
label="规定类型"
|
label="规定类型"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
data={setTypeOpts}
|
data={setTypeOpts}
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
@@ -547,8 +581,6 @@ export default function PersonReportPage() {
|
|||||||
/>
|
/>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
label="出勤类型"
|
label="出勤类型"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
data={actualTypeOpts}
|
data={actualTypeOpts}
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
@@ -559,8 +591,6 @@ export default function PersonReportPage() {
|
|||||||
/>
|
/>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
label="工作类别"
|
label="工作类别"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
data={workTypeOpts}
|
data={workTypeOpts}
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
@@ -571,8 +601,6 @@ export default function PersonReportPage() {
|
|||||||
/>
|
/>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
label="核算方式"
|
label="核算方式"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
data={accountTypeOpts}
|
data={accountTypeOpts}
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
@@ -583,8 +611,6 @@ export default function PersonReportPage() {
|
|||||||
/>
|
/>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
label="绩效核算"
|
label="绩效核算"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
data={performanceOpts}
|
data={performanceOpts}
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
@@ -602,8 +628,6 @@ export default function PersonReportPage() {
|
|||||||
/>
|
/>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
label="对象类型"
|
label="对象类型"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
data={objTypeOpts}
|
data={objTypeOpts}
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
@@ -615,42 +639,44 @@ export default function PersonReportPage() {
|
|||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</SettingFilterPanel>
|
||||||
|
|
||||||
<Paper
|
<SettingContentPanel
|
||||||
withBorder
|
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
style={{
|
style={{
|
||||||
borderColor:
|
|
||||||
"light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4))",
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minHeight: 0,
|
minHeight: 0,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: 12,
|
gap: 12,
|
||||||
}}>
|
}}>
|
||||||
|
<SettingListHeader
|
||||||
|
title="我的日报"
|
||||||
|
count={`${totalItems} 条`}
|
||||||
|
actions={
|
||||||
|
<SettingHeaderActions>
|
||||||
|
<Button
|
||||||
|
leftSection={<IconPlus size={16} />}
|
||||||
|
onClick={() => {
|
||||||
|
setEditingRecord(null)
|
||||||
|
setModalOpened(true)
|
||||||
|
}}>
|
||||||
|
填写日报
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
leftSection={<IconRefresh size={16} />}
|
||||||
|
onClick={load}
|
||||||
|
loading={loading}>
|
||||||
|
刷新
|
||||||
|
</Button>
|
||||||
|
</SettingHeaderActions>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Stack gap={12} align="stretch" style={{ flex: 1, minHeight: 0 }}>
|
<Stack gap={12} align="stretch" style={{ flex: 1, minHeight: 0 }}>
|
||||||
<Flex justify={"space-between"} align="center" w="100%">
|
<SettingDataTable<Daily.DailyWorkList>
|
||||||
<Button
|
|
||||||
leftSection={<IconPlus size={12} />}
|
|
||||||
size="xs"
|
|
||||||
radius={"xs"}
|
|
||||||
onClick={() => {
|
|
||||||
setEditingRecord(null)
|
|
||||||
setModalOpened(true)
|
|
||||||
}}>
|
|
||||||
填写日报
|
|
||||||
</Button>
|
|
||||||
<ActionIcon onClick={load} variant="transparent">
|
|
||||||
<IconRefresh size={16} />
|
|
||||||
</ActionIcon>
|
|
||||||
</Flex>
|
|
||||||
<DataTable<Daily.DailyWorkList>
|
|
||||||
width="100%"
|
width="100%"
|
||||||
style={{ width: "100%" }}
|
minHeight={0}
|
||||||
withTableBorder
|
style={{ width: "100%", flex: 1 }}
|
||||||
withRowBorders
|
|
||||||
pinFirstColumn
|
pinFirstColumn
|
||||||
pinLastColumn
|
pinLastColumn
|
||||||
scrollAreaProps={{ type: "auto" }}
|
scrollAreaProps={{ type: "auto" }}
|
||||||
@@ -665,16 +691,35 @@ export default function PersonReportPage() {
|
|||||||
setPageSize(v)
|
setPageSize(v)
|
||||||
setPage(1)
|
setPage(1)
|
||||||
}}
|
}}
|
||||||
recordsPerPageOptions={[10, 15, 20, 50]}
|
recordsPerPageOptions={recordsPerPageOptions}
|
||||||
noRecordsText="暂无数据"
|
|
||||||
recordsPerPageLabel="当前页数"
|
|
||||||
sortStatus={sortStatus}
|
sortStatus={sortStatus}
|
||||||
onSortStatusChange={(s) => {
|
onSortStatusChange={(s) => {
|
||||||
setSortStatus(s)
|
setSortStatus(s)
|
||||||
setPage(1)
|
setPage(1)
|
||||||
}}
|
}}
|
||||||
|
renderPagination={({ Controls }) => (
|
||||||
|
<Group
|
||||||
|
justify="space-between"
|
||||||
|
align="center"
|
||||||
|
w="100%"
|
||||||
|
gap="sm"
|
||||||
|
wrap="wrap">
|
||||||
|
<Group gap="xs" align="center" wrap="wrap">
|
||||||
|
<Controls.Text />
|
||||||
|
<Controls.PageSizeSelector />
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="gray"
|
||||||
|
size="xs"
|
||||||
|
onClick={openCustomPageSizeModal}>
|
||||||
|
自定义每页条数
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
<Controls.Pagination />
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<Paper withBorder radius="xs" p="sm">
|
<SettingPanel radius="md" p="sm">
|
||||||
<Group justify="space-between" wrap="wrap">
|
<Group justify="space-between" wrap="wrap">
|
||||||
<Text size="sm" fw={700}>
|
<Text size="sm" fw={700}>
|
||||||
当前页总计
|
当前页总计
|
||||||
@@ -691,9 +736,9 @@ export default function PersonReportPage() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</Paper>
|
</SettingPanel>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</SettingContentPanel>
|
||||||
|
|
||||||
<DailyReportModal
|
<DailyReportModal
|
||||||
opened={modalOpened}
|
opened={modalOpened}
|
||||||
@@ -705,6 +750,31 @@ export default function PersonReportPage() {
|
|||||||
if (refresh) load()
|
if (refresh) load()
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
|
||||||
|
<Modal
|
||||||
|
opened={customPageSizeModalOpened}
|
||||||
|
onClose={closeCustomPageSizeModal}
|
||||||
|
title="添加每页条数"
|
||||||
|
centered>
|
||||||
|
<Stack gap="sm">
|
||||||
|
<TextInput
|
||||||
|
data-autofocus
|
||||||
|
label="每页条数"
|
||||||
|
placeholder="请输入大于 0 的整数,例如 99"
|
||||||
|
value={customPageSizeInput}
|
||||||
|
onChange={(event) =>
|
||||||
|
setCustomPageSizeInput(event.currentTarget.value)
|
||||||
|
}
|
||||||
|
onKeyDown={handleCustomPageSizeEnter}
|
||||||
|
/>
|
||||||
|
<Group justify="flex-end">
|
||||||
|
<Button variant="default" onClick={closeCustomPageSizeModal}>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleAddCustomPageSize}>添加</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
</SettingPage>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
128
components/setting/PageSurface.module.css
Normal file
128
components/setting/PageSurface.module.css
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
.page {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 56px);
|
||||||
|
padding: 16px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
border-color: #e6ebf2;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterPanel {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterPanel :global(.mantine-InputWrapper-label) {
|
||||||
|
color: #1f2329;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 22px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterPanel :global(.mantine-Input-input),
|
||||||
|
.filterPanel :global(.mantine-Select-input),
|
||||||
|
.filterPanel :global(.mantine-NumberInput-input),
|
||||||
|
.filterPanel :global(.mantine-PillsInput-input),
|
||||||
|
.filterPanel :global(.mantine-Textarea-input) {
|
||||||
|
min-height: 36px;
|
||||||
|
border-color: #d0d5dd;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #31373d;
|
||||||
|
font-size: 14px;
|
||||||
|
box-shadow: none;
|
||||||
|
transition:
|
||||||
|
border-color 0.15s ease,
|
||||||
|
box-shadow 0.15s ease,
|
||||||
|
background-color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterPanel :global(.mantine-Textarea-input) {
|
||||||
|
min-height: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterPanel :global(.mantine-Input-input::placeholder),
|
||||||
|
.filterPanel :global(.mantine-Select-input::placeholder),
|
||||||
|
.filterPanel :global(.mantine-NumberInput-input::placeholder),
|
||||||
|
.filterPanel :global(.mantine-PillsInput-input::placeholder),
|
||||||
|
.filterPanel :global(.mantine-Textarea-input::placeholder) {
|
||||||
|
color: #98a2b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterPanel :global(.mantine-Input-input:focus),
|
||||||
|
.filterPanel :global(.mantine-Select-input:focus),
|
||||||
|
.filterPanel :global(.mantine-NumberInput-input:focus),
|
||||||
|
.filterPanel :global(.mantine-PillsInput-input:focus-within),
|
||||||
|
.filterPanel :global(.mantine-Textarea-input:focus) {
|
||||||
|
border-color: #69c0ff;
|
||||||
|
box-shadow: 0 0 0 3px rgba(105, 192, 255, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterPanel :global(.mantine-Button-root[data-variant="transparent"]) {
|
||||||
|
color: #168de8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterPanel :global(.mantine-Button-root:not([data-variant="transparent"])),
|
||||||
|
.headerActions :global(.mantine-Button-root) {
|
||||||
|
min-height: 36px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding-inline: 14px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterPanel :global(.mantine-ActionIcon-root),
|
||||||
|
.headerActions :global(.mantine-ActionIcon-root) {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterPanel :global(.mantine-Pill-root) {
|
||||||
|
background: #eef7ff;
|
||||||
|
color: #168de8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentPanel {
|
||||||
|
border-color: #e6ebf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterActions,
|
||||||
|
.headerActions {
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listHeader {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeader {
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #eef2f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionEyebrow {
|
||||||
|
color: #168de8;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 18px;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
color: #1f2329;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionDescription {
|
||||||
|
color: #667085;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
316
components/setting/PageSurface.tsx
Normal file
316
components/setting/PageSurface.tsx
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Badge,
|
||||||
|
Group,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
type GroupProps,
|
||||||
|
type PaperProps,
|
||||||
|
type StackProps,
|
||||||
|
} from "@mantine/core"
|
||||||
|
import type { PropsWithChildren, ReactNode } from "react"
|
||||||
|
import {
|
||||||
|
DataTable,
|
||||||
|
type DataTableDefaultColumnProps,
|
||||||
|
type DataTableProps,
|
||||||
|
} from "mantine-datatable"
|
||||||
|
import classes from "./PageSurface.module.css"
|
||||||
|
|
||||||
|
function mergeClassName(...classNames: Array<string | undefined>) {
|
||||||
|
return classNames.filter(Boolean).join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingPage({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<StackProps>) {
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
w="100%"
|
||||||
|
h="calc(100vh - 56px)"
|
||||||
|
p="md"
|
||||||
|
gap="md"
|
||||||
|
className={mergeClassName(classes.page, className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingPanel({
|
||||||
|
className,
|
||||||
|
withBorder = true,
|
||||||
|
radius = "lg",
|
||||||
|
p = "md",
|
||||||
|
shadow = "xs",
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<PaperProps>) {
|
||||||
|
return (
|
||||||
|
<Paper
|
||||||
|
withBorder={withBorder}
|
||||||
|
radius={radius}
|
||||||
|
p={p}
|
||||||
|
shadow={shadow}
|
||||||
|
className={mergeClassName(classes.panel, className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingFilterPanel({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<PaperProps>) {
|
||||||
|
return (
|
||||||
|
<SettingPanel
|
||||||
|
className={mergeClassName(classes.filterPanel, className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingContentPanel({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<PaperProps>) {
|
||||||
|
return (
|
||||||
|
<SettingPanel
|
||||||
|
className={mergeClassName(classes.contentPanel, className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SettingSectionHeaderProps = GroupProps & {
|
||||||
|
eyebrow?: string
|
||||||
|
title: ReactNode
|
||||||
|
description?: ReactNode
|
||||||
|
actions?: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingSectionHeader({
|
||||||
|
eyebrow,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
actions,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: SettingSectionHeaderProps) {
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
justify="space-between"
|
||||||
|
align="flex-start"
|
||||||
|
gap="sm"
|
||||||
|
wrap="wrap"
|
||||||
|
className={mergeClassName(classes.sectionHeader, className)}
|
||||||
|
{...props}>
|
||||||
|
<Stack gap={2}>
|
||||||
|
{eyebrow ? (
|
||||||
|
<Text className={classes.sectionEyebrow}>{eyebrow}</Text>
|
||||||
|
) : null}
|
||||||
|
<Text className={classes.sectionTitle}>{title}</Text>
|
||||||
|
{description ? (
|
||||||
|
<Text className={classes.sectionDescription}>{description}</Text>
|
||||||
|
) : null}
|
||||||
|
</Stack>
|
||||||
|
{actions}
|
||||||
|
</Group>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SettingListHeaderProps = {
|
||||||
|
title: ReactNode
|
||||||
|
count?: ReactNode
|
||||||
|
actions?: ReactNode
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingListHeader({
|
||||||
|
title,
|
||||||
|
count,
|
||||||
|
actions,
|
||||||
|
className,
|
||||||
|
}: SettingListHeaderProps) {
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
justify="space-between"
|
||||||
|
align="center"
|
||||||
|
wrap="wrap"
|
||||||
|
gap="sm"
|
||||||
|
className={mergeClassName(classes.listHeader, className)}>
|
||||||
|
<Group gap="xs">
|
||||||
|
<Text fw={700}>{title}</Text>
|
||||||
|
{count !== undefined ? (
|
||||||
|
<Badge variant="light" color="gray">
|
||||||
|
{count}
|
||||||
|
</Badge>
|
||||||
|
) : null}
|
||||||
|
</Group>
|
||||||
|
{actions}
|
||||||
|
</Group>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingHeaderActions({
|
||||||
|
className,
|
||||||
|
gap = "sm",
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<GroupProps>) {
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
gap={gap}
|
||||||
|
wrap="wrap"
|
||||||
|
className={mergeClassName(classes.headerActions, className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingFilterActions({
|
||||||
|
className,
|
||||||
|
gap = "sm",
|
||||||
|
justify = "flex-end",
|
||||||
|
mt = "md",
|
||||||
|
...props
|
||||||
|
}: PropsWithChildren<GroupProps>) {
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
justify={justify}
|
||||||
|
gap={gap}
|
||||||
|
wrap="wrap"
|
||||||
|
mt={mt}
|
||||||
|
className={mergeClassName(classes.filterActions, className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingDataTableStyles = {
|
||||||
|
header: {
|
||||||
|
backgroundColor: "#F8F9FB",
|
||||||
|
borderBottom: "1px solid #EEF2F6",
|
||||||
|
color: "#56606A",
|
||||||
|
fontSize: "14px",
|
||||||
|
},
|
||||||
|
root: {
|
||||||
|
height: "100%",
|
||||||
|
maxWidth: "100%",
|
||||||
|
minWidth: 0,
|
||||||
|
fontSize: "14px",
|
||||||
|
color: "#31373D",
|
||||||
|
borderBottom: "1px solid #E8EDF3",
|
||||||
|
borderRadius: 10,
|
||||||
|
overflow: "hidden",
|
||||||
|
boxSizing: "border-box",
|
||||||
|
backgroundColor: "#FFFFFF",
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
padding: "14px 16px 18px",
|
||||||
|
},
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const settingDataTableDefaultColumnProps: DataTableDefaultColumnProps<
|
||||||
|
Record<string, unknown>
|
||||||
|
> = {
|
||||||
|
titleStyle: {
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingDataTable<T = Record<string, unknown>>(
|
||||||
|
props: DataTableProps<T>
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
styles,
|
||||||
|
defaultColumnProps,
|
||||||
|
withTableBorder,
|
||||||
|
withRowBorders,
|
||||||
|
striped,
|
||||||
|
highlightOnHover,
|
||||||
|
loaderBackgroundBlur,
|
||||||
|
loadingText,
|
||||||
|
noRecordsText,
|
||||||
|
minHeight,
|
||||||
|
page,
|
||||||
|
onPageChange,
|
||||||
|
totalRecords,
|
||||||
|
recordsPerPage,
|
||||||
|
recordsPerPageLabel,
|
||||||
|
paginationText,
|
||||||
|
...rest
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const paginationProps =
|
||||||
|
page !== undefined &&
|
||||||
|
onPageChange !== undefined &&
|
||||||
|
recordsPerPage !== undefined
|
||||||
|
? {
|
||||||
|
page,
|
||||||
|
onPageChange,
|
||||||
|
totalRecords,
|
||||||
|
recordsPerPage,
|
||||||
|
loadingText: loadingText ?? "正在加载数据...",
|
||||||
|
recordsPerPageLabel: recordsPerPageLabel ?? "每页条数",
|
||||||
|
paginationText:
|
||||||
|
paginationText ??
|
||||||
|
(({
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
totalRecords,
|
||||||
|
}: {
|
||||||
|
from: number
|
||||||
|
to: number
|
||||||
|
totalRecords: number
|
||||||
|
}) => `${from}-${to} / ${totalRecords}`),
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
|
||||||
|
const tableProps = {
|
||||||
|
withTableBorder: withTableBorder ?? true,
|
||||||
|
withRowBorders: withRowBorders ?? true,
|
||||||
|
striped: striped ?? true,
|
||||||
|
highlightOnHover: highlightOnHover ?? true,
|
||||||
|
defaultColumnProps:
|
||||||
|
defaultColumnProps ??
|
||||||
|
(settingDataTableDefaultColumnProps as DataTableDefaultColumnProps<T>),
|
||||||
|
loaderBackgroundBlur: loaderBackgroundBlur ?? 1,
|
||||||
|
noRecordsText: noRecordsText ?? "暂无数据",
|
||||||
|
minHeight: minHeight ?? 360,
|
||||||
|
styles: styles
|
||||||
|
? { ...settingDataTableStyles, ...styles }
|
||||||
|
: settingDataTableStyles,
|
||||||
|
...paginationProps,
|
||||||
|
...rest,
|
||||||
|
} as DataTableProps<T>
|
||||||
|
|
||||||
|
return <DataTable {...tableProps} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export const settingTabsStyles = {
|
||||||
|
list: {
|
||||||
|
padding: 4,
|
||||||
|
backgroundColor: "#F8F9FB",
|
||||||
|
border: "1px solid #EEF2F6",
|
||||||
|
borderRadius: 10,
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
tab: {
|
||||||
|
minHeight: 36,
|
||||||
|
borderRadius: 8,
|
||||||
|
color: "#56606A",
|
||||||
|
fontWeight: 600,
|
||||||
|
"&[data-active]": {
|
||||||
|
backgroundColor: "#FFFFFF",
|
||||||
|
color: "#1F2329",
|
||||||
|
boxShadow: "0 6px 18px rgba(15, 23, 42, 0.08)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
panel: {
|
||||||
|
flex: 1,
|
||||||
|
minHeight: 0,
|
||||||
|
minWidth: 0,
|
||||||
|
paddingTop: 16,
|
||||||
|
},
|
||||||
|
} as const
|
||||||
Reference in New Issue
Block a user