fix(style): fix
This commit is contained in:
@@ -9,12 +9,9 @@ import {
|
||||
} from "@/components/label/store/auth"
|
||||
import { OwnTaskStatusOpts } from "@/components/label/utils/constants"
|
||||
import {
|
||||
ActionIcon,
|
||||
Button,
|
||||
Collapse,
|
||||
Flex,
|
||||
Group,
|
||||
Paper,
|
||||
Select,
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
@@ -28,9 +25,17 @@ import {
|
||||
IconRefresh,
|
||||
IconSearch,
|
||||
} from "@tabler/icons-react"
|
||||
import { DataTable, DataTableColumn } from "mantine-datatable"
|
||||
import { DataTableColumn } from "mantine-datatable"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import {
|
||||
SettingContentPanel,
|
||||
SettingDataTable,
|
||||
SettingFilterActions,
|
||||
SettingFilterPanel,
|
||||
SettingHeaderActions,
|
||||
SettingListHeader,
|
||||
} from "@/components/setting/PageSurface"
|
||||
import TaskStatusTag from "./components/TaskStatusTag"
|
||||
|
||||
function createInitialFilters() {
|
||||
@@ -109,7 +114,6 @@ export default function OwnTaskTableContainer(props: {
|
||||
}
|
||||
return next
|
||||
}, [
|
||||
,
|
||||
form.search_id,
|
||||
form.search_status,
|
||||
pageNumber,
|
||||
@@ -173,6 +177,24 @@ export default function OwnTaskTableContainer(props: {
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
setPageNumber(1)
|
||||
setForm({ ...filters })
|
||||
setQueryTrigger((v) => v + 1)
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
const next = createInitialFilters()
|
||||
setFilters(next)
|
||||
setForm(next)
|
||||
setPageNumber(1)
|
||||
setQueryTrigger((v) => v + 1)
|
||||
}
|
||||
|
||||
const handleRefresh = () => {
|
||||
setQueryTrigger((v) => v + 1)
|
||||
}
|
||||
|
||||
const columns = useMemo(() => {
|
||||
const cols: DataTableColumn<Task.DataProps>[] = [
|
||||
{
|
||||
@@ -267,12 +289,11 @@ export default function OwnTaskTableContainer(props: {
|
||||
|
||||
return (
|
||||
<Stack gap="sm" style={{ flex: 1, minHeight: 0 }}>
|
||||
<Paper withBorder p="sm" radius="sm">
|
||||
<SettingFilterPanel>
|
||||
<Group justify="space-between" wrap="wrap" gap="sm">
|
||||
<Group gap="xs">
|
||||
<Text fw={700}>我的任务</Text>
|
||||
<Text fw={700}>筛选条件</Text>
|
||||
<Button
|
||||
size="xs"
|
||||
variant="transparent"
|
||||
rightSection={
|
||||
searchExpanded ? (
|
||||
@@ -285,43 +306,20 @@ export default function OwnTaskTableContainer(props: {
|
||||
{searchExpanded ? "收起筛选" : "展开筛选"}
|
||||
</Button>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<Button
|
||||
size="xs"
|
||||
radius={"xs"}
|
||||
variant="default"
|
||||
leftSection={<IconRefresh size={16} />}
|
||||
onClick={() => {
|
||||
const next = createInitialFilters()
|
||||
setFilters(next)
|
||||
setForm(next)
|
||||
setPageNumber(1)
|
||||
}}>
|
||||
重置
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
radius={"xs"}
|
||||
leftSection={<IconSearch size={16} />}
|
||||
onClick={() => {
|
||||
setPageNumber(1)
|
||||
setForm({ ...filters })
|
||||
}}>
|
||||
查询
|
||||
</Button>
|
||||
</Group>
|
||||
</Group>
|
||||
<Collapse
|
||||
in={searchExpanded}
|
||||
transitionDuration={250}
|
||||
transitionTimingFunction="ease"
|
||||
animateOpacity>
|
||||
<Stack gap="sm" mt="sm">
|
||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 3, lg: 4 }} spacing="sm">
|
||||
<Stack gap="md" mt="md">
|
||||
<SimpleGrid
|
||||
cols={{ base: 1, sm: 2, md: 3, lg: 4 }}
|
||||
spacing="md"
|
||||
verticalSpacing="md">
|
||||
<TextInput
|
||||
size="xs"
|
||||
radius="xs"
|
||||
label="任务ID"
|
||||
placeholder="请输入任务 ID"
|
||||
value={filters.search_id}
|
||||
onChange={(e) =>
|
||||
setFilters((s) => ({
|
||||
@@ -332,8 +330,6 @@ export default function OwnTaskTableContainer(props: {
|
||||
style={{ width: 160 }}
|
||||
/>
|
||||
<Select
|
||||
size="xs"
|
||||
radius="xs"
|
||||
label="状态"
|
||||
data={Object.entries(OwnTaskStatusOpts).map(
|
||||
([value, label]) => ({
|
||||
@@ -346,60 +342,70 @@ export default function OwnTaskTableContainer(props: {
|
||||
setFilters((s) => ({ ...s, search_status: v ?? "0" }))
|
||||
}
|
||||
allowDeselect={false}
|
||||
style={{ width: 160 }}
|
||||
/>
|
||||
</SimpleGrid>
|
||||
</Stack>
|
||||
</Collapse>
|
||||
</Paper>
|
||||
<SettingFilterActions>
|
||||
<Button
|
||||
variant="default"
|
||||
leftSection={<IconRefresh size={16} />}
|
||||
onClick={handleReset}>
|
||||
重置
|
||||
</Button>
|
||||
<Button
|
||||
leftSection={<IconSearch size={16} />}
|
||||
onClick={handleSearch}
|
||||
loading={loading}>
|
||||
查询
|
||||
</Button>
|
||||
</SettingFilterActions>
|
||||
</SettingFilterPanel>
|
||||
|
||||
<Paper
|
||||
withBorder
|
||||
p="md"
|
||||
radius="sm"
|
||||
<SettingContentPanel
|
||||
style={{
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}>
|
||||
<Stack gap={12} align="stretch" style={{ flex: 1, minHeight: 0 }}>
|
||||
<Flex justify={"space-between"} align="center" w="100%">
|
||||
<Flex gap={12}>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="xs"
|
||||
variant="default"
|
||||
disabled={!isLabel}
|
||||
onClick={() => handleAcquireTask(1)}>
|
||||
领取标注
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="xs"
|
||||
variant="default"
|
||||
disabled={!isReview1}
|
||||
onClick={() => handleAcquireTask(2)}>
|
||||
领取审核
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="xs"
|
||||
variant="default"
|
||||
disabled={!isReview2}
|
||||
onClick={() => handleAcquireTask(3)}>
|
||||
领取复审
|
||||
</Button>
|
||||
</Flex>
|
||||
<ActionIcon onClick={load} variant="transparent">
|
||||
<IconRefresh size={16} />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
<DataTable<Task.DataProps>
|
||||
<Stack gap="sm" align="stretch" style={{ flex: 1, minHeight: 0 }}>
|
||||
<SettingListHeader
|
||||
title="我的任务"
|
||||
count={`${total} 条`}
|
||||
actions={
|
||||
<SettingHeaderActions>
|
||||
<Button
|
||||
variant="default"
|
||||
leftSection={<IconRefresh size={16} />}
|
||||
onClick={handleRefresh}
|
||||
loading={loading}>
|
||||
刷新
|
||||
</Button>
|
||||
<Button
|
||||
variant="default"
|
||||
disabled={!isLabel}
|
||||
onClick={() => handleAcquireTask(1)}>
|
||||
领取标注
|
||||
</Button>
|
||||
<Button
|
||||
variant="default"
|
||||
disabled={!isReview1}
|
||||
onClick={() => handleAcquireTask(2)}>
|
||||
领取审核
|
||||
</Button>
|
||||
<Button
|
||||
variant="default"
|
||||
disabled={!isReview2}
|
||||
onClick={() => handleAcquireTask(3)}>
|
||||
领取复审
|
||||
</Button>
|
||||
</SettingHeaderActions>
|
||||
}
|
||||
/>
|
||||
<SettingDataTable<Task.DataProps>
|
||||
width="100%"
|
||||
style={{ width: "100%" }}
|
||||
withTableBorder
|
||||
withRowBorders
|
||||
style={{ width: "100%", flex: 1 }}
|
||||
pinFirstColumn
|
||||
pinLastColumn
|
||||
scrollAreaProps={{ type: "auto" }}
|
||||
@@ -415,11 +421,9 @@ export default function OwnTaskTableContainer(props: {
|
||||
setPageNumber(1)
|
||||
}}
|
||||
recordsPerPageOptions={[10, 15, 20]}
|
||||
noRecordsText="暂无数据"
|
||||
recordsPerPageLabel="当前页数"
|
||||
/>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</SettingContentPanel>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,13 +12,19 @@ import {
|
||||
TaskStatusOpts,
|
||||
} from "@/components/label/utils/constants"
|
||||
import {
|
||||
ActionIcon,
|
||||
SettingContentPanel,
|
||||
SettingDataTable,
|
||||
SettingFilterActions,
|
||||
SettingFilterPanel,
|
||||
SettingHeaderActions,
|
||||
SettingListHeader,
|
||||
} from "@/components/setting/PageSurface"
|
||||
import {
|
||||
Button,
|
||||
Collapse,
|
||||
Flex,
|
||||
Group,
|
||||
MultiSelect,
|
||||
Paper,
|
||||
Select,
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
@@ -35,7 +41,7 @@ import {
|
||||
} from "@tabler/icons-react"
|
||||
import dayjs from "dayjs"
|
||||
import duration from "dayjs/plugin/duration"
|
||||
import { DataTable, DataTableColumn } from "mantine-datatable"
|
||||
import { DataTableColumn } from "mantine-datatable"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import * as XLSX from "xlsx-js-style"
|
||||
@@ -125,7 +131,6 @@ export default function TaskTableContainer(props: {
|
||||
}
|
||||
}) {
|
||||
const { info, userEnums } = props
|
||||
false && console.log(info)
|
||||
|
||||
const { user_id } = usePermissionStore()
|
||||
|
||||
@@ -444,6 +449,24 @@ export default function TaskTableContainer(props: {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
setPageNumber(1)
|
||||
setForm({ ...filters })
|
||||
setQueryTrigger((v) => v + 1)
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
const next = createInitialFilters()
|
||||
setFilters(next)
|
||||
setForm(next)
|
||||
setPageNumber(1)
|
||||
setQueryTrigger((v) => v + 1)
|
||||
}
|
||||
|
||||
const handleRefresh = () => {
|
||||
setQueryTrigger((v) => v + 1)
|
||||
}
|
||||
|
||||
const getTime = (time: number) => {
|
||||
return dayjs.duration(time, "seconds").format("HH:mm:ss")
|
||||
}
|
||||
@@ -775,12 +798,11 @@ export default function TaskTableContainer(props: {
|
||||
return (
|
||||
<>
|
||||
<Stack gap="sm" style={{ flex: 1, minHeight: 0 }}>
|
||||
<Paper withBorder p="sm" radius="sm">
|
||||
<SettingFilterPanel>
|
||||
<Group justify="space-between" wrap="wrap" gap="sm">
|
||||
<Group gap="xs">
|
||||
<Text fw={700}>任务列表</Text>
|
||||
<Text fw={700}>筛选条件</Text>
|
||||
<Button
|
||||
size="xs"
|
||||
variant="transparent"
|
||||
rightSection={
|
||||
searchExpanded ? (
|
||||
@@ -793,43 +815,20 @@ export default function TaskTableContainer(props: {
|
||||
{searchExpanded ? "收起筛选" : "展开筛选"}
|
||||
</Button>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<Button
|
||||
size="xs"
|
||||
radius={"xs"}
|
||||
variant="default"
|
||||
leftSection={<IconRefresh size={16} />}
|
||||
onClick={() => {
|
||||
const next = createInitialFilters()
|
||||
setFilters(next)
|
||||
setForm(next)
|
||||
setPageNumber(1)
|
||||
}}>
|
||||
重置
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
radius={"xs"}
|
||||
leftSection={<IconSearch size={16} />}
|
||||
onClick={() => {
|
||||
setPageNumber(1)
|
||||
setForm({ ...filters })
|
||||
}}>
|
||||
查询
|
||||
</Button>
|
||||
</Group>
|
||||
</Group>
|
||||
<Collapse
|
||||
in={searchExpanded}
|
||||
transitionDuration={250}
|
||||
transitionTimingFunction="ease"
|
||||
animateOpacity>
|
||||
<Stack gap="sm" mt="sm">
|
||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 3, lg: 4 }} spacing="sm">
|
||||
<Stack gap="md" mt="md">
|
||||
<SimpleGrid
|
||||
cols={{ base: 1, sm: 2, md: 3, lg: 4 }}
|
||||
spacing="md"
|
||||
verticalSpacing="md">
|
||||
<TextInput
|
||||
size="xs"
|
||||
radius="xs"
|
||||
label="任务ID"
|
||||
placeholder="请输入任务 ID"
|
||||
value={filters.search_id}
|
||||
onChange={(e) =>
|
||||
setFilters((s) => ({
|
||||
@@ -839,8 +838,6 @@ export default function TaskTableContainer(props: {
|
||||
}
|
||||
/>
|
||||
<Select
|
||||
size="xs"
|
||||
radius="xs"
|
||||
label="任务状态"
|
||||
data={Object.entries(TaskStatusOpts).map(
|
||||
([value, label]) => ({
|
||||
@@ -855,8 +852,6 @@ export default function TaskTableContainer(props: {
|
||||
allowDeselect={false}
|
||||
/>
|
||||
<Select
|
||||
size="xs"
|
||||
radius="xs"
|
||||
label="是否返工"
|
||||
data={[
|
||||
{ label: "全部", value: "" },
|
||||
@@ -871,8 +866,6 @@ export default function TaskTableContainer(props: {
|
||||
/>
|
||||
<MultiSelect
|
||||
label="当前负责人"
|
||||
size="xs"
|
||||
radius="xs"
|
||||
data={Array.from(userEnums.allEnum.entries()).map(
|
||||
([x, y]) => ({ label: y, value: x.toString() })
|
||||
)}
|
||||
@@ -885,8 +878,6 @@ export default function TaskTableContainer(props: {
|
||||
/>
|
||||
<MultiSelect
|
||||
label="标注员"
|
||||
size="xs"
|
||||
radius="xs"
|
||||
data={Array.from(userEnums.labelEnum.entries()).map(
|
||||
([x, y]) => ({ label: y, value: x.toString() })
|
||||
)}
|
||||
@@ -897,8 +888,6 @@ export default function TaskTableContainer(props: {
|
||||
/>
|
||||
<MultiSelect
|
||||
label="审核员"
|
||||
size="xs"
|
||||
radius="xs"
|
||||
data={Array.from(userEnums.review1Enum.entries()).map(
|
||||
([x, y]) => ({ label: y, value: x.toString() })
|
||||
)}
|
||||
@@ -911,8 +900,6 @@ export default function TaskTableContainer(props: {
|
||||
/>
|
||||
<MultiSelect
|
||||
label="复审员"
|
||||
size="xs"
|
||||
radius="xs"
|
||||
data={Array.from(userEnums.review2Enum.entries()).map(
|
||||
([x, y]) => ({ label: y, value: x.toString() })
|
||||
)}
|
||||
@@ -926,54 +913,69 @@ export default function TaskTableContainer(props: {
|
||||
</SimpleGrid>
|
||||
</Stack>
|
||||
</Collapse>
|
||||
</Paper>
|
||||
<Paper
|
||||
withBorder
|
||||
p="md"
|
||||
radius="sm"
|
||||
<SettingFilterActions>
|
||||
<Button
|
||||
variant="default"
|
||||
leftSection={<IconRefresh size={16} />}
|
||||
onClick={handleReset}>
|
||||
重置
|
||||
</Button>
|
||||
<Button
|
||||
leftSection={<IconSearch size={16} />}
|
||||
onClick={handleSearch}
|
||||
loading={loading}>
|
||||
查询
|
||||
</Button>
|
||||
</SettingFilterActions>
|
||||
</SettingFilterPanel>
|
||||
<SettingContentPanel
|
||||
style={{
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}>
|
||||
<Stack gap={12} align="stretch" style={{ flex: 1, minHeight: 0 }}>
|
||||
<Flex justify={"space-between"} align="center" w="100%">
|
||||
<Flex gap={12}>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="xs"
|
||||
variant="outline"
|
||||
disabled={!selectedRecords.length}
|
||||
onClick={handleExport}>
|
||||
下载选中项
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="xs"
|
||||
variant="outline"
|
||||
disabled={!selectedRecords.length}
|
||||
onClick={clickDispatchTask}>
|
||||
调度选中项
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="xs"
|
||||
variant="outline"
|
||||
disabled={!selectedRecords.length}
|
||||
onClick={clickReleaseTask}>
|
||||
释放选中项
|
||||
</Button>
|
||||
</Flex>
|
||||
<ActionIcon onClick={load} variant="transparent">
|
||||
<IconRefresh size={16} />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
<DataTable<Task.DataProps>
|
||||
<Stack gap="sm" align="stretch" style={{ flex: 1, minHeight: 0 }}>
|
||||
<SettingListHeader
|
||||
title="任务列表"
|
||||
count={
|
||||
selectedRecords.length
|
||||
? `${total} 条 / 已选 ${selectedRecords.length}`
|
||||
: `${total} 条`
|
||||
}
|
||||
actions={
|
||||
<SettingHeaderActions>
|
||||
<Button
|
||||
variant="default"
|
||||
leftSection={<IconRefresh size={16} />}
|
||||
onClick={handleRefresh}
|
||||
loading={loading}>
|
||||
刷新
|
||||
</Button>
|
||||
<Button
|
||||
variant="default"
|
||||
disabled={!selectedRecords.length}
|
||||
onClick={handleExport}>
|
||||
下载选中项
|
||||
</Button>
|
||||
<Button
|
||||
variant="default"
|
||||
disabled={!selectedRecords.length}
|
||||
onClick={clickDispatchTask}>
|
||||
调度选中项
|
||||
</Button>
|
||||
<Button
|
||||
variant="default"
|
||||
disabled={!selectedRecords.length}
|
||||
onClick={clickReleaseTask}>
|
||||
释放选中项
|
||||
</Button>
|
||||
</SettingHeaderActions>
|
||||
}
|
||||
/>
|
||||
<SettingDataTable<Task.DataProps>
|
||||
width="100%"
|
||||
style={{ width: "100%" }}
|
||||
withTableBorder
|
||||
withRowBorders
|
||||
style={{ width: "100%", flex: 1 }}
|
||||
pinFirstColumn
|
||||
pinLastColumn
|
||||
scrollAreaProps={{ type: "auto" }}
|
||||
@@ -991,11 +993,9 @@ export default function TaskTableContainer(props: {
|
||||
selectedRecords={selectedRecords}
|
||||
onSelectedRecordsChange={setSelectedRecords}
|
||||
recordsPerPageOptions={[10, 15, 20]}
|
||||
noRecordsText="暂无数据"
|
||||
recordsPerPageLabel="当前页数"
|
||||
/>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</SettingContentPanel>
|
||||
</Stack>
|
||||
|
||||
<WorkflowModal
|
||||
|
||||
@@ -6,8 +6,9 @@ import {
|
||||
selectModeMap,
|
||||
staticsColor,
|
||||
} from "@/components/label/utils/constants"
|
||||
import { SettingDataTable } from "@/components/setting/PageSurface"
|
||||
import { Badge, Button, Group, Modal, Paper, Stack, Text } from "@mantine/core"
|
||||
import { DataTable, DataTableColumn } from "mantine-datatable"
|
||||
import { DataTableColumn } from "mantine-datatable"
|
||||
import { useState } from "react"
|
||||
|
||||
type DetailRow = ProjectDetail.SubAttributesDescribe
|
||||
@@ -188,9 +189,7 @@ export default function LabelSchemeContainer(props: {
|
||||
}}>
|
||||
<Stack gap="sm">
|
||||
<Text fw={700}>标注方案</Text>
|
||||
<DataTable<ProjectDetail.LabelSchemaList>
|
||||
withTableBorder
|
||||
withRowBorders
|
||||
<SettingDataTable<ProjectDetail.LabelSchemaList>
|
||||
idAccessor="category_id"
|
||||
records={schemas}
|
||||
columns={columns}
|
||||
@@ -209,9 +208,7 @@ export default function LabelSchemeContainer(props: {
|
||||
title="子属性描述"
|
||||
centered
|
||||
size="70%">
|
||||
<DataTable<DetailRow>
|
||||
withTableBorder
|
||||
withRowBorders
|
||||
<SettingDataTable<DetailRow>
|
||||
records={detailRows}
|
||||
columns={detailColumns}
|
||||
noRecordsText="暂无数据"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { projectConfig } from "@/components/label/api/project"
|
||||
import { ProjectDetail } from "@/components/label/api/project/typing"
|
||||
import { usePermissionStore } from "@/components/label/store/auth"
|
||||
import { SettingDataTable } from "@/components/setting/PageSurface"
|
||||
import {
|
||||
ActionIcon,
|
||||
Badge,
|
||||
@@ -18,7 +19,7 @@ import { modals } from "@mantine/modals"
|
||||
import { notifications } from "@mantine/notifications"
|
||||
import { IconPlus, IconTrash } from "@tabler/icons-react"
|
||||
import dayjs from "dayjs"
|
||||
import { DataTable, DataTableColumn } from "mantine-datatable"
|
||||
import { DataTableColumn } from "mantine-datatable"
|
||||
import { useMemo, useState } from "react"
|
||||
|
||||
type RulePayload = {
|
||||
@@ -221,9 +222,7 @@ export default function NoteRulesTable(props: {
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
<DataTable<RuleRow>
|
||||
withTableBorder
|
||||
withRowBorders
|
||||
<SettingDataTable<RuleRow>
|
||||
records={rows}
|
||||
columns={columns}
|
||||
noRecordsText="暂无规则"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { projectConfig } from "@/components/label/api/project"
|
||||
import { Project, ProjectDetail } from "@/components/label/api/project/typing"
|
||||
import { SettingDataTable } from "@/components/setting/PageSurface"
|
||||
import {
|
||||
ActionIcon,
|
||||
Badge,
|
||||
@@ -22,7 +23,7 @@ import {
|
||||
import { modals } from "@mantine/modals"
|
||||
import { notifications } from "@mantine/notifications"
|
||||
import { IconEdit, IconPlus, IconX } from "@tabler/icons-react"
|
||||
import { DataTable, DataTableColumn } from "mantine-datatable"
|
||||
import { DataTableColumn } from "mantine-datatable"
|
||||
import { ReactNode, useEffect, useMemo, useState } from "react"
|
||||
import StageUserModal from "./StageUserModal"
|
||||
import {
|
||||
@@ -1429,9 +1430,7 @@ export default function TaskStageTabsContainer(props: {
|
||||
批量操作
|
||||
</Button>
|
||||
</Group>
|
||||
<DataTable<StageUserRow>
|
||||
withTableBorder
|
||||
withRowBorders
|
||||
<SettingDataTable<StageUserRow>
|
||||
records={rows}
|
||||
columns={userColumns(stage)}
|
||||
noRecordsText="暂无人员"
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import { getTaskWorkFlow } from "@/components/label/api/task"
|
||||
import { SettingDataTable } from "@/components/setting/PageSurface"
|
||||
import { Button, Group, Modal, ScrollArea, Stack, Text } from "@mantine/core"
|
||||
import { notifications } from "@mantine/notifications"
|
||||
import { DataTable, DataTableColumn } from "mantine-datatable"
|
||||
import { DataTableColumn } from "mantine-datatable"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import TaskStatusTag from "./TaskStatusTag"
|
||||
|
||||
@@ -139,15 +140,13 @@ export default function WorkflowModal(props: {
|
||||
size={720}>
|
||||
<Stack gap="sm">
|
||||
<ScrollArea h={360} type="auto">
|
||||
<DataTable<WorkflowRow>
|
||||
withTableBorder
|
||||
withRowBorders
|
||||
<SettingDataTable<WorkflowRow>
|
||||
pinLastColumn
|
||||
fetching={loading}
|
||||
idAccessor="__rowId"
|
||||
records={rows}
|
||||
width={Math.max(columns.length * 140, 640)}
|
||||
columns={columns}
|
||||
noRecordsText={loading ? "加载中..." : "暂无数据"}
|
||||
minHeight={220}
|
||||
/>
|
||||
</ScrollArea>
|
||||
|
||||
@@ -7,13 +7,16 @@ import {
|
||||
Breadcrumbs,
|
||||
Flex,
|
||||
Group,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import {
|
||||
SettingFilterPanel,
|
||||
SettingPage,
|
||||
} from "@/components/setting/PageSurface"
|
||||
import InfoSettingContainer from "./InfoSettingContainer"
|
||||
import OwnTaskTableContainer from "./OwnTaskTableContainer"
|
||||
import TaskBoardContainer from "./TaskBoardContainer"
|
||||
@@ -193,15 +196,15 @@ export default function CollectionDetailPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack w="100%" h="calc(100vh - 56px)" p="md" gap="md">
|
||||
<Paper withBorder p="md" radius="sm">
|
||||
<SettingPage>
|
||||
<SettingFilterPanel>
|
||||
<Group justify="space-between" wrap="wrap" gap="sm">
|
||||
<Flex gap="sm" w="100%" justify="space-between">
|
||||
<Breadcrumbs separator=">">{breadcrumbsItems}</Breadcrumbs>
|
||||
{DetailMenuItems()}
|
||||
</Flex>
|
||||
</Group>
|
||||
</Paper>
|
||||
</SettingFilterPanel>
|
||||
|
||||
<Stack style={{ flex: 1, minHeight: 0 }} gap="md">
|
||||
{selectKey === "own" ? (
|
||||
@@ -219,6 +222,6 @@ export default function CollectionDetailPage() {
|
||||
/>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</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;
|
||||
}
|
||||
312
components/setting/PageSurface.tsx
Normal file
312
components/setting/PageSurface.tsx
Normal file
@@ -0,0 +1,312 @@
|
||||
"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%",
|
||||
fontSize: "14px",
|
||||
color: "#31373D",
|
||||
borderBottom: "1px solid #E8EDF3",
|
||||
borderRadius: 10,
|
||||
overflow: "hidden",
|
||||
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,
|
||||
paddingTop: 16,
|
||||
},
|
||||
} as const
|
||||
Reference in New Issue
Block a user