Files
DataMate/frontend/src/components/CardView.tsx
chenghh-9609 442e561817 change settings page and add ratio task detail page (#77)
* feat: Update site name to DataMate and refine text for AI data processing

* feat: Refactor settings page and implement model access functionality

- Created a new ModelAccess component for managing model configurations.
- Removed the old Settings component and replaced it with a new SettingsPage component that integrates ModelAccess, SystemConfig, and WebhookConfig.
- Added SystemConfig component for managing system settings.
- Implemented WebhookConfig component for managing webhook configurations.
- Updated API functions for model management in settings.apis.ts.
- Adjusted routing to point to the new SettingsPage component.

* feat: Implement Data Collection Page with Task Management and Execution Log

- Created DataCollectionPage component to manage data collection tasks.
- Added TaskManagement and ExecutionLog components for task handling and logging.
- Integrated task operations including start, stop, edit, and delete functionalities.
- Implemented filtering and searching capabilities in task management.
- Introduced SimpleCronScheduler for scheduling tasks with cron expressions.
- Updated CreateTask component to utilize new scheduling and template features.
- Enhanced BasicInformation component to conditionally render fields based on visibility settings.
- Refactored ImportConfiguration component to remove NAS import section.

* feat: Update task creation API endpoint and enhance task creation form with new fields and validation

* Refactor file upload and operator management components

- Removed unnecessary console logs from file download and export functions.
- Added size property to TaskItem interface for better task management.
- Simplified TaskUpload component by utilizing useFileSliceUpload hook for file upload logic.
- Enhanced OperatorPluginCreate component to handle file uploads and parsing more efficiently.
- Updated ConfigureStep component to use Ant Design Form for better data handling and validation.
- Improved PreviewStep component to navigate back to the operator market.
- Added support for additional file types in UploadStep component.
- Implemented delete operator functionality in OperatorMarketPage with confirmation prompts.
- Cleaned up unused API functions in operator.api.ts to streamline the codebase.
- Fixed number formatting utility to handle zero values correctly.

* Refactor Knowledge Generation to Knowledge Base

- Created new API service for Knowledge Base operations including querying, creating, updating, and deleting knowledge bases and files.
- Added constants for Knowledge Base status and type mappings.
- Defined models for Knowledge Base and related files.
- Removed obsolete Knowledge Base creation and home components, replacing them with new implementations under the Knowledge Base structure.
- Updated routing to reflect the new Knowledge Base paths.
- Adjusted menu items to align with the new Knowledge Base terminology.
- Modified ModelAccess interface to include modelName and type properties.

* feat: Implement Knowledge Base Page with CRUD operations and data management

- Added KnowledgeBasePage component for displaying and managing knowledge bases.
- Integrated search and filter functionalities with SearchControls component.
- Implemented CreateKnowledgeBase component for creating and editing knowledge bases.
- Enhanced AddDataDialog for file uploads and dataset selections.
- Introduced TableTransfer component for managing data transfers between tables.
- Updated API functions for knowledge base operations, including file management.
- Refactored knowledge base model to include file status and metadata.
- Adjusted routing to point to the new KnowledgeBasePage.

* feat: enhance OperatorPluginCreate and ConfigureStep for better upload handling and UI updates

* refactor: remove unused components and clean up API logging in KnowledgeBase

* feat: update icons in various components and improve styling for better UI consistency

* fix: adjust upload step handling and improve error display in configuration step

* feat: Add RatioTransfer component for dataset selection and configuration

- Implemented RatioTransfer component to manage dataset selection and ratio configuration.
- Integrated dataset fetching with search and filter capabilities.
- Added RatioConfig component for displaying and updating selected datasets' configurations.
- Enhanced SelectDataset component with improved UI and functionality for dataset selection.
- Updated RatioTasksPage to utilize new ratio task status mapping and improved error handling for task deletion.
- Refactored ratio model and constants for better type safety and clarity.
- Changed Vite configuration to use local backend service for development.

* feat: Add .editorconfig and enhance SystemConfig with table for settings display

* feat: Enhance parameter configuration for range inputs and update default values

* feat: Update site name to DataMate and refine text for AI data processing

* Refactor file upload and operator management components

- Removed unnecessary console logs from file download and export functions.
- Added size property to TaskItem interface for better task management.
- Simplified TaskUpload component by utilizing useFileSliceUpload hook for file upload logic.
- Enhanced OperatorPluginCreate component to handle file uploads and parsing more efficiently.
- Updated ConfigureStep component to use Ant Design Form for better data handling and validation.
- Improved PreviewStep component to navigate back to the operator market.
- Added support for additional file types in UploadStep component.
- Implemented delete operator functionality in OperatorMarketPage with confirmation prompts.
- Cleaned up unused API functions in operator.api.ts to streamline the codebase.
- Fixed number formatting utility to handle zero values correctly.

* Refactor Knowledge Generation to Knowledge Base

- Created new API service for Knowledge Base operations including querying, creating, updating, and deleting knowledge bases and files.
- Added constants for Knowledge Base status and type mappings.
- Defined models for Knowledge Base and related files.
- Removed obsolete Knowledge Base creation and home components, replacing them with new implementations under the Knowledge Base structure.
- Updated routing to reflect the new Knowledge Base paths.
- Adjusted menu items to align with the new Knowledge Base terminology.
- Modified ModelAccess interface to include modelName and type properties.

* feat: Implement Knowledge Base Page with CRUD operations and data management

- Added KnowledgeBasePage component for displaying and managing knowledge bases.
- Integrated search and filter functionalities with SearchControls component.
- Implemented CreateKnowledgeBase component for creating and editing knowledge bases.
- Enhanced AddDataDialog for file uploads and dataset selections.
- Introduced TableTransfer component for managing data transfers between tables.
- Updated API functions for knowledge base operations, including file management.
- Refactored knowledge base model to include file status and metadata.
- Adjusted routing to point to the new KnowledgeBasePage.

* feat: enhance OperatorPluginCreate and ConfigureStep for better upload handling and UI updates

* feat: update icons in various components and improve styling for better UI consistency

* fix: adjust upload step handling and improve error display in configuration step

* feat: Update site name to DataMate and refine text for AI data processing

* Refactor file upload and operator management components

- Removed unnecessary console logs from file download and export functions.
- Added size property to TaskItem interface for better task management.
- Simplified TaskUpload component by utilizing useFileSliceUpload hook for file upload logic.
- Enhanced OperatorPluginCreate component to handle file uploads and parsing more efficiently.
- Updated ConfigureStep component to use Ant Design Form for better data handling and validation.
- Improved PreviewStep component to navigate back to the operator market.
- Added support for additional file types in UploadStep component.
- Implemented delete operator functionality in OperatorMarketPage with confirmation prompts.
- Cleaned up unused API functions in operator.api.ts to streamline the codebase.
- Fixed number formatting utility to handle zero values correctly.

* Refactor Knowledge Generation to Knowledge Base

- Created new API service for Knowledge Base operations including querying, creating, updating, and deleting knowledge bases and files.
- Added constants for Knowledge Base status and type mappings.
- Defined models for Knowledge Base and related files.
- Removed obsolete Knowledge Base creation and home components, replacing them with new implementations under the Knowledge Base structure.
- Updated routing to reflect the new Knowledge Base paths.
- Adjusted menu items to align with the new Knowledge Base terminology.
- Modified ModelAccess interface to include modelName and type properties.

* feat: Implement Knowledge Base Page with CRUD operations and data management

- Added KnowledgeBasePage component for displaying and managing knowledge bases.
- Integrated search and filter functionalities with SearchControls component.
- Implemented CreateKnowledgeBase component for creating and editing knowledge bases.
- Enhanced AddDataDialog for file uploads and dataset selections.
- Introduced TableTransfer component for managing data transfers between tables.
- Updated API functions for knowledge base operations, including file management.
- Refactored knowledge base model to include file status and metadata.
- Adjusted routing to point to the new KnowledgeBasePage.

* feat: enhance OperatorPluginCreate and ConfigureStep for better upload handling and UI updates

* feat: update icons in various components and improve styling for better UI consistency

* fix: adjust upload step handling and improve error display in configuration step

* feat: add settings drawer and integrate SettingsPage component

* feat: add ratio task management features including detail view and API integration
2025-11-12 17:23:24 +08:00

293 lines
9.6 KiB
TypeScript

import React, { useState, useEffect, useRef } from "react";
import { Tag, Pagination, Tooltip, Empty, Popover, Spin } from "antd";
import { ClockCircleOutlined, StarFilled } from "@ant-design/icons";
import type { ItemType } from "antd/es/menu/interface";
import { formatDateTime } from "@/utils/unit";
import ActionDropdown from "./ActionDropdown";
import { Database } from "lucide-react";
interface BaseCardDataType {
id: string | number;
name: string;
type: string;
icon?: React.JSX.Element;
iconColor?: string;
status: {
label: string;
icon?: React.JSX.Element;
color?: string;
} | null;
description: string;
tags?: string[];
statistics?: { label: string; value: string | number }[];
updatedAt?: string;
}
interface CardViewProps<T> {
data: T[];
pagination: {
[key: string]: any;
current: number;
pageSize: number;
total: number;
};
operations:
| {
key: string;
label: string;
danger?: boolean;
icon?: React.JSX.Element;
onClick?: (item: T) => void;
}[]
| ((item: T) => ItemType[]);
loading?: boolean;
onView?: (item: T) => void;
onFavorite?: (item: T) => void;
isFavorite?: (item: T) => boolean;
}
// 标签渲染组件
const TagsRenderer = ({ tags }: { tags?: any[] }) => {
const [visibleTags, setVisibleTags] = useState<any[]>([]);
const [hiddenTags, setHiddenTags] = useState<any[]>([]);
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!tags || tags.length === 0) return;
const calculateVisibleTags = () => {
if (!containerRef.current) return;
const containerWidth = containerRef.current.offsetWidth;
const tempDiv = document.createElement("div");
tempDiv.style.visibility = "hidden";
tempDiv.style.position = "absolute";
tempDiv.style.top = "-9999px";
tempDiv.className = "flex flex-wrap gap-1";
document.body.appendChild(tempDiv);
let totalWidth = 0;
let visibleCount = 0;
const tagElements: HTMLElement[] = [];
// 为每个tag创建临时元素来测量宽度
tags.forEach((tag, index) => {
const tagElement = document.createElement("span");
tagElement.className = "ant-tag ant-tag-default";
tagElement.style.margin = "2px";
tagElement.textContent = typeof tag === "string" ? tag : tag.name;
tempDiv.appendChild(tagElement);
tagElements.push(tagElement);
const tagWidth = tagElement.offsetWidth + 4; // 加上gap的宽度
// 如果不是最后一个标签,需要预留+n标签的空间
const plusTagWidth = index < tags.length - 1 ? 35 : 0; // +n标签大约35px宽度
if (totalWidth + tagWidth + plusTagWidth <= containerWidth) {
totalWidth += tagWidth;
visibleCount++;
} else {
// 如果当前标签放不下,且已经有可见标签,则停止
if (visibleCount > 0) return;
// 如果是第一个标签就放不下,至少显示一个
if (index === 0) {
totalWidth += tagWidth;
visibleCount = 1;
}
}
});
document.body.removeChild(tempDiv);
setVisibleTags(tags.slice(0, visibleCount));
setHiddenTags(tags.slice(visibleCount));
};
// 延迟执行以确保DOM已渲染
const timer = setTimeout(calculateVisibleTags, 0);
// 监听窗口大小变化
const handleResize = () => {
calculateVisibleTags();
};
window.addEventListener("resize", handleResize);
return () => {
clearTimeout(timer);
window.removeEventListener("resize", handleResize);
};
}, [tags]);
if (!tags || tags.length === 0) return null;
const popoverContent = (
<div className="max-w-xs">
<div className="flex flex-wrap gap-1">
{hiddenTags.map((tag, index) => (
<Tag key={index}>{typeof tag === "string" ? tag : tag.name}</Tag>
))}
</div>
</div>
);
return (
<div ref={containerRef} className="flex flex-wrap gap-1 w-full">
{visibleTags.map((tag, index) => (
<Tag key={index}>{typeof tag === "string" ? tag : tag.name}</Tag>
))}
{hiddenTags.length > 0 && (
<Popover
content={popoverContent}
title="更多标签"
trigger="hover"
placement="topLeft"
>
<Tag className="cursor-pointer bg-gray-100 border-gray-300 text-gray-600 hover:bg-gray-200">
+{hiddenTags.length}
</Tag>
</Popover>
)}
</div>
);
};
function CardView<T extends BaseCardDataType>(props: CardViewProps<T>) {
const {
data,
pagination,
operations,
loading,
onView,
onFavorite,
isFavorite,
} = props;
if (data.length === 0) {
return (
<div className="flex flex-col items-center justify-center h-full text-gray-500">
<Empty />
</div>
);
}
const ops = (item) =>
typeof operations === "function" ? operations(item) : operations;
return (
<div className="flex-overflow-hidden">
<div className="overflow-auto grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-4">
{data.map((item) => (
<div
key={item.id}
className="border-card p-4 bg-white hover:shadow-lg transition-shadow duration-200"
>
<div className="flex flex-col space-y-4 h-full">
<div
className="flex flex-col space-y-4 h-full"
onClick={() => onView?.(item)}
style={{ cursor: onView ? "pointer" : "default" }}
>
{/* Header */}
<div className="flex items-start justify-between">
<div className="flex items-center gap-3 min-w-0">
{item?.icon && (
<div
className={`flex-shrink-0 w-12 h-12 bg-gradient-to-br from-sky-300 to-blue-500 text-white rounded-lg flex items-center justify-center`}
>
<div className="w-6 h-6 text-gray-50">{item?.icon}</div>
</div>
)}
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<h3
className={`text-base flex-1 text-ellipsis overflow-hidden whitespace-nowrap font-semibold text-gray-900 truncate`}
>
{item?.name}
</h3>
{item?.status && (
<Tag color={item?.status?.color}>
<div className="flex items-center gap-2 text-xs py-0.5">
{item?.status?.icon && (
<span>{item?.status?.icon}</span>
)}
<span>{item?.status?.label}</span>
</div>
</Tag>
)}
</div>
</div>
</div>
{onFavorite && (
<StarFilled
style={{
fontSize: "16px",
color: isFavorite?.(item) ? "#ffcc00ff" : "#d1d5db",
cursor: "pointer",
}}
onClick={() => onFavorite?.(item)}
/>
)}
</div>
<div className="flex-1 flex flex-col justify-end">
{/* Tags */}
<TagsRenderer tags={item?.tags || []} />
{/* Description */}
<p className="text-gray-600 text-xs text-ellipsis overflow-hidden whitespace-nowrap text-xs line-clamp-2 mt-2">
<Tooltip title={item?.description}>
{item?.description}
</Tooltip>
</p>
{/* Statistics */}
<div className="grid grid-cols-2 gap-4 py-3">
{item?.statistics?.map((stat, idx) => (
<div key={idx}>
<div className="text-sm text-gray-500 overflow-hidden whitespace-nowrap text-ellipsis w-full">
{stat?.label}:
</div>
<div className="text-base font-semibold text-gray-900 overflow-hidden whitespace-nowrap text-ellipsis w-full">
{stat?.value}
</div>
</div>
))}
</div>
</div>
</div>
{/* Actions */}
<div className="flex items-center justify-between pt-3 border-t border-t-gray-200">
<div className=" text-gray-500 text-right">
<div className="flex items-center gap-1">
<ClockCircleOutlined className="w-4 h-4" />{" "}
{formatDateTime(item?.updatedAt)}
</div>
</div>
{operations && (
<ActionDropdown
actions={ops(item)}
onAction={(key) => {
const operation = ops(item).find((op) => op.key === key);
if (operation?.onClick) {
operation.onClick(item);
}
}}
/>
)}
</div>
</div>
</div>
))}
</div>
<div className="flex justify-end mt-6">
<Pagination {...pagination} />
</div>
</div>
);
}
export default CardView;