fix: 修复下拉选择问题
This commit is contained in:
parent
719cc3782a
commit
c3c0bb8c08
|
|
@ -10,7 +10,7 @@ import { useProjectStore } from './store/projectStore';
|
||||||
import { useUIStore } from './store/uiStore';
|
import { useUIStore } from './store/uiStore';
|
||||||
import { CreateProjectRequest, UpdateProjectRequest } from './types/project';
|
import { CreateProjectRequest, UpdateProjectRequest } from './types/project';
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
|
import './styles/design-system.css';
|
||||||
/**
|
/**
|
||||||
* 主应用组件
|
* 主应用组件
|
||||||
* 遵循 Tauri 开发规范的应用架构设计
|
* 遵循 Tauri 开发规范的应用架构设计
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import {
|
import {
|
||||||
Search,
|
Search,
|
||||||
Filter,
|
|
||||||
Download,
|
Download,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
|
|
@ -10,7 +9,6 @@ import {
|
||||||
Clock,
|
Clock,
|
||||||
XCircle,
|
XCircle,
|
||||||
Eye,
|
Eye,
|
||||||
Trash2,
|
|
||||||
RotateCcw
|
RotateCcw
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { LoadingSpinner } from './LoadingSpinner';
|
import { LoadingSpinner } from './LoadingSpinner';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { ChevronDownIcon } from "lucide-react";
|
||||||
|
|
||||||
|
// 自定义下拉选择组件
|
||||||
|
export const CustomSelect: React.FC<{
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
options: { value: string; label: string }[];
|
||||||
|
placeholder?: string;
|
||||||
|
className?: string;
|
||||||
|
}> = ({ value, onChange, options, placeholder, className = '' }) => {
|
||||||
|
return (
|
||||||
|
<div className={`relative ${className}`}>
|
||||||
|
<select
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
className="w-full appearance-none bg-white border border-gray-200 rounded-lg px-3 py-2 pr-8 text-sm text-gray-900 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all duration-200 hover:border-gray-300 cursor-pointer"
|
||||||
|
>
|
||||||
|
{placeholder && <option value="">{placeholder}</option>}
|
||||||
|
{options.map((option) => (
|
||||||
|
<option key={option.value} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<div className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||||
|
<ChevronDownIcon className="h-4 w-4 text-gray-400" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -15,7 +15,6 @@ import {
|
||||||
import {
|
import {
|
||||||
HeartIcon as HeartIconSolid
|
HeartIcon as HeartIconSolid
|
||||||
} from '@heroicons/react/24/solid';
|
} from '@heroicons/react/24/solid';
|
||||||
import '../styles/design-system.css';
|
|
||||||
|
|
||||||
interface ModelCardProps {
|
interface ModelCardProps {
|
||||||
model: Model;
|
model: Model;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import {
|
||||||
SparklesIcon,
|
SparklesIcon,
|
||||||
HeartIcon
|
HeartIcon
|
||||||
} from '@heroicons/react/24/outline';
|
} from '@heroicons/react/24/outline';
|
||||||
import '../styles/design-system.css';
|
|
||||||
|
|
||||||
interface ModelListProps {
|
interface ModelListProps {
|
||||||
onModelSelect?: (model: Model) => void;
|
onModelSelect?: (model: Model) => void;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ModelStatus, Gender, ModelSortBy, SortOrder } from '../types/model';
|
import { ModelStatus, Gender, ModelSortBy, SortOrder } from '../types/model';
|
||||||
import { MagnifyingGlassIcon, FunnelIcon, ArrowUpIcon, ArrowDownIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
|
import { MagnifyingGlassIcon, FunnelIcon, ArrowUpIcon, ArrowDownIcon } from '@heroicons/react/24/outline';
|
||||||
import '../styles/design-system.css';
|
|
||||||
|
|
||||||
|
import { CustomSelect } from './CustomSelect'
|
||||||
interface ModelSearchProps {
|
interface ModelSearchProps {
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
onSearchChange: (query: string) => void;
|
onSearchChange: (query: string) => void;
|
||||||
|
|
@ -79,34 +79,7 @@ const ModelSearch: React.FC<ModelSearchProps> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 自定义下拉选择组件
|
|
||||||
const CustomSelect: React.FC<{
|
|
||||||
value: string;
|
|
||||||
onChange: (value: string) => void;
|
|
||||||
options: { value: string; label: string }[];
|
|
||||||
placeholder?: string;
|
|
||||||
className?: string;
|
|
||||||
}> = ({ value, onChange, options, placeholder, className = '' }) => {
|
|
||||||
return (
|
|
||||||
<div className={`relative ${className}`}>
|
|
||||||
<select
|
|
||||||
value={value}
|
|
||||||
onChange={(e) => onChange(e.target.value)}
|
|
||||||
className="w-full appearance-none bg-white border border-gray-200 rounded-lg px-3 py-2 pr-8 text-sm text-gray-900 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all duration-200 hover:border-gray-300 cursor-pointer"
|
|
||||||
>
|
|
||||||
{placeholder && <option value="">{placeholder}</option>}
|
|
||||||
{options.map((option) => (
|
|
||||||
<option key={option.value} value={option.value}>
|
|
||||||
{option.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
|
||||||
<ChevronDownIcon className="h-4 w-4 text-gray-400" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-4 animate-fade-in">
|
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-4 animate-fade-in">
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,6 @@ export const VideoClassificationProgress: React.FC<VideoClassificationProgressPr
|
||||||
getProjectQueueStatus,
|
getProjectQueueStatus,
|
||||||
getProjectTaskProgress,
|
getProjectTaskProgress,
|
||||||
getClassificationStats,
|
getClassificationStats,
|
||||||
pauseQueue,
|
|
||||||
resumeQueue,
|
|
||||||
stopQueue,
|
|
||||||
isLoading,
|
|
||||||
error,
|
error,
|
||||||
clearError,
|
clearError,
|
||||||
} = useVideoClassificationStore();
|
} = useVideoClassificationStore();
|
||||||
|
|
@ -129,33 +125,6 @@ export const VideoClassificationProgress: React.FC<VideoClassificationProgressPr
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 队列控制
|
|
||||||
const handlePauseResume = async () => {
|
|
||||||
try {
|
|
||||||
console.log('🎮 队列控制按钮点击,当前状态:', typedQueueStats?.status);
|
|
||||||
|
|
||||||
if (typedQueueStats?.status === 'Running') {
|
|
||||||
console.log('⏸️ 暂停队列...');
|
|
||||||
await pauseQueue();
|
|
||||||
} else if (typedQueueStats?.status === 'Paused') {
|
|
||||||
console.log('▶️ 恢复队列...');
|
|
||||||
await resumeQueue();
|
|
||||||
} else {
|
|
||||||
console.log('❓ 未知状态,无法操作:', typedQueueStats?.status);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ 队列控制失败:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleStop = async () => {
|
|
||||||
try {
|
|
||||||
console.log('🛑 停止队列...');
|
|
||||||
await stopQueue();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ 停止队列失败:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 计算进度百分比
|
// 计算进度百分比
|
||||||
const getOverallProgress = useCallback(() => {
|
const getOverallProgress = useCallback(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { ArrowLeft, FolderOpen, Calendar, Settings, Upload, FileVideo, FileAudio, FileImage, HardDrive } from 'lucide-react';
|
import { ArrowLeft, FolderOpen, Settings, Upload, FileVideo, FileAudio, FileImage, HardDrive } from 'lucide-react';
|
||||||
import { useProjectStore } from '../store/projectStore';
|
import { useProjectStore } from '../store/projectStore';
|
||||||
import { useMaterialStore } from '../store/materialStore';
|
import { useMaterialStore } from '../store/materialStore';
|
||||||
import { Project } from '../types/project';
|
import { Project } from '../types/project';
|
||||||
|
|
@ -8,7 +8,6 @@ import { MaterialImportResult } from '../types/material';
|
||||||
import { LoadingSpinner } from '../components/LoadingSpinner';
|
import { LoadingSpinner } from '../components/LoadingSpinner';
|
||||||
import { ErrorMessage } from '../components/ErrorMessage';
|
import { ErrorMessage } from '../components/ErrorMessage';
|
||||||
import { MaterialImportDialog } from '../components/MaterialImportDialog';
|
import { MaterialImportDialog } from '../components/MaterialImportDialog';
|
||||||
import { FFmpegDebugPanel } from '../components/FFmpegDebugPanel';
|
|
||||||
import { MaterialCard } from '../components/MaterialCard';
|
import { MaterialCard } from '../components/MaterialCard';
|
||||||
import { VideoClassificationProgress } from '../components/VideoClassificationProgress';
|
import { VideoClassificationProgress } from '../components/VideoClassificationProgress';
|
||||||
import { AiAnalysisLogViewer } from '../components/AiAnalysisLogViewer';
|
import { AiAnalysisLogViewer } from '../components/AiAnalysisLogViewer';
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue