fix: Resolve TypeScript build errors and clean up unused imports
Fixed Build Issues: - Removed unused imports in ProjectList, ModelList, ProjectForm, DataTable, CardGrid - Fixed variant type mismatches in TableAction and GridAction interfaces - Replaced InteractiveTextarea with standard textarea in ProjectForm - Updated EmptyState usage to use correct component props - Removed unused ModelCardSkeleton component definition Code Cleanup: - Cleaned up unused Search, Filter, MoreHorizontal, Eye, Edit, Trash2 imports - Removed unused filters state in DataTable - Removed unused actions parameter in CardGrid - Simplified ProjectForm description field implementation - Fixed EmptyProjectList usage in ProjectList Build Status: - All TypeScript errors resolved - Hot reload working correctly - Development server running smoothly on port 5174 - No more compilation warnings or errors The application now builds successfully with all UI/UX enhancements intact.
This commit is contained in:
parent
496b26cdeb
commit
a6f9e82c65
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useMemo } from 'react';
|
import React, { useState, useMemo } from 'react';
|
||||||
import { Grid, List, Search, Filter, SortAsc, SortDesc } from 'lucide-react';
|
import { Grid, List, SortAsc, SortDesc } from 'lucide-react';
|
||||||
import { SearchInput } from './InteractiveInput';
|
import { SearchInput } from './InteractiveInput';
|
||||||
import { InteractiveButton } from './InteractiveButton';
|
import { InteractiveButton } from './InteractiveButton';
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ export interface GridAction<T> {
|
||||||
label: string;
|
label: string;
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
onClick: (item: T) => void;
|
onClick: (item: T) => void;
|
||||||
variant?: 'default' | 'primary' | 'danger';
|
variant?: 'primary' | 'secondary' | 'danger' | 'success' | 'ghost' | 'outline';
|
||||||
disabled?: (item: T) => boolean;
|
disabled?: (item: T) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +87,6 @@ export function CardGrid<T extends CardGridItem>({
|
||||||
emptyText = '暂无数据',
|
emptyText = '暂无数据',
|
||||||
emptyComponent,
|
emptyComponent,
|
||||||
className = '',
|
className = '',
|
||||||
actions = [],
|
|
||||||
selectedItems = [],
|
selectedItems = [],
|
||||||
onSelectionChange,
|
onSelectionChange,
|
||||||
bulkActions = [],
|
bulkActions = [],
|
||||||
|
|
@ -147,15 +146,6 @@ export function CardGrid<T extends CardGridItem>({
|
||||||
|
|
||||||
// 网格列数类名
|
// 网格列数类名
|
||||||
const getGridColsClass = () => {
|
const getGridColsClass = () => {
|
||||||
const colsMap = {
|
|
||||||
1: 'grid-cols-1',
|
|
||||||
2: 'grid-cols-2',
|
|
||||||
3: 'grid-cols-3',
|
|
||||||
4: 'grid-cols-4',
|
|
||||||
5: 'grid-cols-5',
|
|
||||||
6: 'grid-cols-6',
|
|
||||||
};
|
|
||||||
|
|
||||||
const classes = [];
|
const classes = [];
|
||||||
if (gridCols.sm) classes.push(`grid-cols-${gridCols.sm}`);
|
if (gridCols.sm) classes.push(`grid-cols-${gridCols.sm}`);
|
||||||
if (gridCols.md) classes.push(`md:grid-cols-${gridCols.md}`);
|
if (gridCols.md) classes.push(`md:grid-cols-${gridCols.md}`);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useMemo } from 'react';
|
import React, { useState, useMemo } from 'react';
|
||||||
import { ChevronUp, ChevronDown, Search, Filter, MoreHorizontal, Eye, Edit, Trash2 } from 'lucide-react';
|
import { ChevronUp, ChevronDown, Filter } from 'lucide-react';
|
||||||
import { SearchInput } from './InteractiveInput';
|
import { SearchInput } from './InteractiveInput';
|
||||||
import { InteractiveButton } from './InteractiveButton';
|
import { InteractiveButton } from './InteractiveButton';
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ export interface TableAction<T> {
|
||||||
label: string;
|
label: string;
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
onClick: (record: T) => void;
|
onClick: (record: T) => void;
|
||||||
variant?: 'default' | 'primary' | 'danger';
|
variant?: 'primary' | 'secondary' | 'danger' | 'success' | 'ghost' | 'outline';
|
||||||
disabled?: (record: T) => boolean;
|
disabled?: (record: T) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,8 +71,6 @@ export function DataTable<T extends Record<string, any>>({
|
||||||
direction: 'asc' | 'desc';
|
direction: 'asc' | 'desc';
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [filters, setFilters] = useState<Record<string, any>>({});
|
|
||||||
|
|
||||||
// 获取行的唯一键
|
// 获取行的唯一键
|
||||||
const getRowKey = (record: T, index: number): string => {
|
const getRowKey = (record: T, index: number): string => {
|
||||||
if (typeof rowKey === 'function') {
|
if (typeof rowKey === 'function') {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { modelService } from '../services/modelService';
|
||||||
import ModelCard from './ModelCard';
|
import ModelCard from './ModelCard';
|
||||||
import ModelForm from './ModelForm';
|
import ModelForm from './ModelForm';
|
||||||
import ModelSearch from './ModelSearch';
|
import ModelSearch from './ModelSearch';
|
||||||
import { ModelCardSkeleton } from './SkeletonLoader';
|
|
||||||
import {
|
import {
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
Squares2X2Icon,
|
Squares2X2Icon,
|
||||||
|
|
@ -185,21 +184,7 @@ const ModelList: React.FC<ModelListProps> = ({ onModelSelect }) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 骨架屏组件
|
|
||||||
const ModelCardSkeleton = () => (
|
|
||||||
<div className="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden animate-pulse">
|
|
||||||
<div className="aspect-[3/4] bg-gray-200 loading-shimmer" />
|
|
||||||
<div className="p-5 space-y-3">
|
|
||||||
<div className="h-6 bg-gray-200 rounded loading-shimmer" />
|
|
||||||
<div className="h-4 bg-gray-200 rounded w-3/4 loading-shimmer" />
|
|
||||||
<div className="h-4 bg-gray-200 rounded w-1/2 loading-shimmer" />
|
|
||||||
<div className="flex gap-2 pt-2">
|
|
||||||
<div className="flex-1 h-10 bg-gray-200 rounded-xl loading-shimmer" />
|
|
||||||
<div className="w-10 h-10 bg-gray-200 rounded-xl loading-shimmer" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -222,7 +207,27 @@ const ModelList: React.FC<ModelListProps> = ({ onModelSelect }) => {
|
||||||
{/* 模特卡片骨架 */}
|
{/* 模特卡片骨架 */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||||
{[...Array(8)].map((_, i) => (
|
{[...Array(8)].map((_, i) => (
|
||||||
<ModelCardSkeleton key={i} />
|
<div key={i} className="bg-white rounded-xl border border-gray-200 p-4 space-y-4 animate-pulse">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="h-12 w-12 bg-gray-300 rounded-full"></div>
|
||||||
|
<div className="space-y-2 flex-1">
|
||||||
|
<div className="h-4 bg-gray-300 rounded w-2/3"></div>
|
||||||
|
<div className="h-3 bg-gray-300 rounded w-1/2"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="h-3 bg-gray-300 rounded"></div>
|
||||||
|
<div className="h-3 bg-gray-300 rounded w-4/5"></div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div className="h-6 w-16 bg-gray-300 rounded-full"></div>
|
||||||
|
<div className="h-6 w-20 bg-gray-300 rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 pt-2">
|
||||||
|
<div className="h-8 bg-gray-300 rounded flex-1"></div>
|
||||||
|
<div className="h-8 w-8 bg-gray-300 rounded"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import React, { useState, useEffect } from 'react';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { useProjectStore } from '../store/projectStore';
|
import { useProjectStore } from '../store/projectStore';
|
||||||
import { ProjectFormData, ProjectFormErrors } from '../types/project';
|
import { ProjectFormData, ProjectFormErrors } from '../types/project';
|
||||||
import { InteractiveInput, InteractiveTextarea } from './InteractiveInput';
|
import { InteractiveInput } from './InteractiveInput';
|
||||||
import { InteractiveButton } from './InteractiveButton';
|
import { InteractiveButton } from './InteractiveButton';
|
||||||
import { Folder, X, AlertCircle } from 'lucide-react';
|
import { Folder, X } from 'lucide-react';
|
||||||
|
|
||||||
interface ProjectFormProps {
|
interface ProjectFormProps {
|
||||||
initialData?: Partial<ProjectFormData>;
|
initialData?: Partial<ProjectFormData>;
|
||||||
|
|
@ -206,16 +206,35 @@ export const ProjectForm: React.FC<ProjectFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 项目描述 */}
|
{/* 项目描述 */}
|
||||||
<InteractiveTextarea
|
<div className="form-group">
|
||||||
label="项目描述"
|
<label htmlFor="description" className="form-label">
|
||||||
value={formData.description}
|
项目描述
|
||||||
onChange={(value) => setFormData(prev => ({ ...prev, description: value }))}
|
</label>
|
||||||
placeholder="描述一下这个项目的用途和内容(可选)"
|
<textarea
|
||||||
error={errors.description}
|
id="description"
|
||||||
hint="添加描述有助于您更好地管理项目"
|
className={`form-textarea ${errors.description ? 'error' : ''}`}
|
||||||
rows={4}
|
value={formData.description}
|
||||||
maxLength={500}
|
onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
|
||||||
/>
|
placeholder="描述一下这个项目的用途和内容(可选)"
|
||||||
|
rows={4}
|
||||||
|
maxLength={500}
|
||||||
|
/>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="form-hint">
|
||||||
|
添加描述有助于您更好地管理项目
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-gray-500">
|
||||||
|
{formData.description.length}/500
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{errors.description && (
|
||||||
|
<div className="form-error">
|
||||||
|
<span className="text-red-600">
|
||||||
|
{errors.description}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{/* 模态框底部 */}
|
{/* 模态框底部 */}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@ import { invoke } from '@tauri-apps/api/core';
|
||||||
import { useProjectStore } from '../store/projectStore';
|
import { useProjectStore } from '../store/projectStore';
|
||||||
import { useUIStore } from '../store/uiStore';
|
import { useUIStore } from '../store/uiStore';
|
||||||
import { ProjectCard } from './ProjectCard';
|
import { ProjectCard } from './ProjectCard';
|
||||||
import { EmptyProjectList } from './EmptyState';
|
import { EmptyState } from './EmptyState';
|
||||||
import { LoadingSpinner } from './LoadingSpinner';
|
import { LoadingSpinner } from './LoadingSpinner';
|
||||||
import { ErrorMessage } from './ErrorMessage';
|
import { ErrorMessage } from './ErrorMessage';
|
||||||
import { DeleteConfirmDialog } from './DeleteConfirmDialog';
|
import { DeleteConfirmDialog } from './DeleteConfirmDialog';
|
||||||
import { ProjectCardSkeleton, PageLoadingSkeleton } from './SkeletonLoader';
|
import { PageLoadingSkeleton } from './SkeletonLoader';
|
||||||
import { InteractiveButton } from './InteractiveButton';
|
import { InteractiveButton } from './InteractiveButton';
|
||||||
import { Plus, Trash2 } from 'lucide-react';
|
import { Plus, Trash2 } from 'lucide-react';
|
||||||
|
|
||||||
|
|
@ -222,7 +222,18 @@ export const ProjectList: React.FC = () => {
|
||||||
{/* 项目内容区域 */}
|
{/* 项目内容区域 */}
|
||||||
{projects.length === 0 ? (
|
{projects.length === 0 ? (
|
||||||
<div className="animate-fade-in-up">
|
<div className="animate-fade-in-up">
|
||||||
<EmptyProjectList onCreateProject={openCreateProjectModal} />
|
<EmptyState
|
||||||
|
illustration="folder"
|
||||||
|
title="还没有项目"
|
||||||
|
description="创建您的第一个项目开始使用 MixVideo"
|
||||||
|
actionText="新建项目"
|
||||||
|
onAction={openCreateProjectModal}
|
||||||
|
showTips
|
||||||
|
tips={[
|
||||||
|
"提示:您可以通过拖拽文件夹到此处快速创建项目",
|
||||||
|
"支持导入现有的视频项目文件夹"
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue